diff options
Diffstat (limited to 'lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp')
-rw-r--r-- | lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp | 279 |
1 files changed, 205 insertions, 74 deletions
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index eaaa16413b1e..3b04b3a1b2ac 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -27,6 +27,7 @@ #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" +#include "lldb/lldb-enumerations.h" #include <tuple> using namespace lldb; @@ -283,6 +284,22 @@ bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() { llvm::dyn_cast_or_null<TypeSystemClang>(pair_type.GetTypeSystem()); if (!ast_ctx) return false; + + // Mimick layout of std::__tree_iterator::__ptr_ and read it in + // from process memory. + // + // The following shows the contiguous block of memory: + // + // +-----------------------------+ class __tree_end_node + // __ptr_ | pointer __left_; | + // +-----------------------------+ class __tree_node_base + // | pointer __right_; | + // | __parent_pointer __parent_; | + // | bool __is_black_; | + // +-----------------------------+ class __tree_node + // | __node_value_type __value_; | <<< our key/value pair + // +-----------------------------+ + // CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( ConstString(), {{"ptr0", @@ -359,6 +376,156 @@ lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator( : nullptr); } +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + Update() { + m_pair_sp.reset(); + m_iter_ptr = nullptr; + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return false; + + if (!valobj_sp) + return false; + + auto exprPathOptions = ValueObject::GetValueForExpressionPathOptions() + .DontCheckDotVsArrowSyntax() + .SetSyntheticChildrenTraversal( + ValueObject::GetValueForExpressionPathOptions:: + SyntheticChildrenTraversal::None); + + // This must be a ValueObject* because it is a child of the ValueObject we + // are producing children for it if were a ValueObjectSP, we would end up + // with a loop (iterator -> synthetic -> child -> parent == iterator) and + // that would in turn leak memory by never allowing the ValueObjects to die + // and free their memory. + m_iter_ptr = + valobj_sp + ->GetValueForExpressionPath(".__i_.__node_", nullptr, nullptr, + exprPathOptions, nullptr) + .get(); + + if (m_iter_ptr) { + auto iter_child( + valobj_sp->GetChildMemberWithName(ConstString("__i_"), true)); + if (!iter_child) { + m_iter_ptr = nullptr; + return false; + } + + CompilerType node_type(iter_child->GetCompilerType() + .GetTypeTemplateArgument(0) + .GetPointeeType()); + + CompilerType pair_type(node_type.GetTypeTemplateArgument(0)); + + std::string name; + uint64_t bit_offset_ptr; + uint32_t bitfield_bit_size_ptr; + bool is_bitfield_ptr; + + pair_type = pair_type.GetFieldAtIndex( + 0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr); + if (!pair_type) { + m_iter_ptr = nullptr; + return false; + } + + uint64_t addr = m_iter_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + m_iter_ptr = nullptr; + + if (addr == 0 || addr == LLDB_INVALID_ADDRESS) + return false; + + TypeSystemClang *ast_ctx = + llvm::dyn_cast_or_null<TypeSystemClang>(pair_type.GetTypeSystem()); + if (!ast_ctx) + return false; + + // Mimick layout of std::__hash_iterator::__node_ and read it in + // from process memory. + // + // The following shows the contiguous block of memory: + // + // +-----------------------------+ class __hash_node_base + // __node_ | __next_pointer __next_; | + // +-----------------------------+ class __hash_node + // | size_t __hash_; | + // | __node_value_type __value_; | <<< our key/value pair + // +-----------------------------+ + // + CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier( + ConstString(), + {{"__next_", + ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, + {"__hash_", ast_ctx->GetBasicType(lldb::eBasicTypeUnsignedLongLong)}, + {"__value_", pair_type}}); + llvm::Optional<uint64_t> size = tree_node_type.GetByteSize(nullptr); + if (!size) + return false; + WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0)); + ProcessSP process_sp(target_sp->GetProcessSP()); + Status error; + process_sp->ReadMemory(addr, buffer_sp->GetBytes(), + buffer_sp->GetByteSize(), error); + if (error.Fail()) + return false; + DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + auto pair_sp = CreateValueObjectFromData( + "pair", extractor, valobj_sp->GetExecutionContextRef(), tree_node_type); + if (pair_sp) + m_pair_sp = pair_sp->GetChildAtIndex(2, true); + } + + return false; +} + +size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(size_t idx) { + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx, true); + return lldb::ValueObjectSP(); +} + +bool lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + MightHaveChildren() { + return true; +} + +size_t lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "first") + return 0; + if (name == "second") + return 1; + return UINT32_MAX; +} + +SyntheticChildrenFrontEnd * +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibCxxUnorderedMapIteratorSyntheticFrontEnd(valobj_sp) + : nullptr); +} + /* (lldb) fr var ibeg --raw --ptr-depth 1 -T (std::__1::__wrap_iter<int *>) ibeg = { @@ -547,101 +714,77 @@ bool lldb_private::formatters::LibcxxContainerSummaryProvider( } /// The field layout in a libc++ string (cap, side, data or data, size, cap). -enum LibcxxStringLayoutMode { - eLibcxxStringLayoutModeCSD = 0, - eLibcxxStringLayoutModeDSC = 1, - eLibcxxStringLayoutModeInvalid = 0xffff -}; +namespace { +enum class StringLayout { CSD, DSC }; +} /// Determine the size in bytes of \p valobj (a libc++ std::string object) and /// extract its data payload. Return the size + payload pair. // TODO: Support big-endian architectures. static llvm::Optional<std::pair<uint64_t, ValueObjectSP>> ExtractLibcxxStringInfo(ValueObject &valobj) { - ValueObjectSP dataval_sp(valobj.GetChildAtIndexPath({0, 0, 0, 0})); - if (!dataval_sp) + ValueObjectSP valobj_r_sp = + valobj.GetChildMemberWithName(ConstString("__r_"), /*can_create=*/true); + if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) return {}; - if (!dataval_sp->GetError().Success()) + + // __r_ is a compressed_pair of the actual data and the allocator. The data we + // want is in the first base class. + ValueObjectSP valobj_r_base_sp = + valobj_r_sp->GetChildAtIndex(0, /*can_create=*/true); + if (!valobj_r_base_sp) return {}; - ValueObjectSP layout_decider( - dataval_sp->GetChildAtIndexPath(llvm::ArrayRef<size_t>({0, 0}))); + ValueObjectSP valobj_rep_sp = valobj_r_base_sp->GetChildMemberWithName( + ConstString("__value_"), /*can_create=*/true); + if (!valobj_rep_sp) + return {}; - // this child should exist - if (!layout_decider) + ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName(ConstString("__l"), + /*can_create=*/true); + if (!l) return {}; - ConstString g_data_name("__data_"); - ConstString g_size_name("__size_"); + StringLayout layout = l->GetIndexOfChildWithName(ConstString("__data_")) == 0 + ? StringLayout::DSC + : StringLayout::CSD; + bool short_mode = false; // this means the string is in short-mode and the // data is stored inline bool using_bitmasks = true; // Whether the class uses bitmasks for the mode // flag (pre-D123580). uint64_t size; - LibcxxStringLayoutMode layout = (layout_decider->GetName() == g_data_name) - ? eLibcxxStringLayoutModeDSC - : eLibcxxStringLayoutModeCSD; uint64_t size_mode_value = 0; - ValueObjectSP short_sp(dataval_sp->GetChildAtIndex(1, true)); + ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName( + ConstString("__s"), /*can_create=*/true); if (!short_sp) return {}; - ValueObjectSP short_fields_sp; ValueObjectSP is_long = short_sp->GetChildMemberWithName(ConstString("__is_long_"), true); - if (is_long) { - short_fields_sp = short_sp; - } else { - // After D128285, we need to access the `__is_long_` and `__size_` fields - // from a packed anonymous struct - short_fields_sp = short_sp->GetChildAtIndex(0, true); - is_long = short_sp->GetChildMemberWithName(ConstString("__is_long_"), true); - } + ValueObjectSP size_sp = + short_sp->GetChildAtNamePath({ConstString("__size_")}); + if (!size_sp) + return {}; if (is_long) { using_bitmasks = false; short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0); - if (ValueObjectSP size_member = - dataval_sp->GetChildAtNamePath({ConstString("__s"), ConstString("__size_")})) - size = size_member->GetValueAsUnsigned(/*fail_value=*/0); - else - return {}; - } else if (layout == eLibcxxStringLayoutModeDSC) { - llvm::SmallVector<size_t, 3> size_mode_locations[] = { - {1, 2}, // Post-c3d0205ee771 layout. This was in use for only a brief - // period, so we can delete it if it becomes a burden. - {1, 1, 0}, - {1, 1, 1}, - }; - ValueObjectSP size_mode; - for (llvm::ArrayRef<size_t> loc : size_mode_locations) { - size_mode = dataval_sp->GetChildAtIndexPath(loc); - if (size_mode && size_mode->GetName() == g_size_name) - break; - } - - if (!size_mode) - return {}; - - size_mode_value = (size_mode->GetValueAsUnsigned(0)); - short_mode = ((size_mode_value & 0x80) == 0); + size = size_sp->GetValueAsUnsigned(/*fail_value=*/0); } else { - ValueObjectSP size_mode(dataval_sp->GetChildAtIndexPath({1, 0, 0})); - if (!size_mode) - return {}; - - size_mode_value = (size_mode->GetValueAsUnsigned(0)); - short_mode = ((size_mode_value & 1) == 0); + // The string mode is encoded in the size field. + size_mode_value = size_sp->GetValueAsUnsigned(0); + uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1; + short_mode = (size_mode_value & mode_mask) == 0; } if (short_mode) { ValueObjectSP location_sp = - short_sp->GetChildMemberWithName(g_data_name, true); + short_sp->GetChildMemberWithName(ConstString("__data_"), true); if (using_bitmasks) - size = (layout == eLibcxxStringLayoutModeDSC) - ? size_mode_value - : ((size_mode_value >> 1) % 256); + size = (layout == StringLayout::DSC) ? size_mode_value + : ((size_mode_value >> 1) % 256); // When the small-string optimization takes place, the data must fit in the // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's @@ -656,10 +799,6 @@ ExtractLibcxxStringInfo(ValueObject &valobj) { return std::make_pair(size, location_sp); } - ValueObjectSP l(dataval_sp->GetChildAtIndex(0, true)); - if (!l) - return {}; - // we can use the layout_decider object as the data pointer ValueObjectSP location_sp = l->GetChildMemberWithName(ConstString("__data_"), /*can_create=*/true); @@ -667,19 +806,11 @@ ExtractLibcxxStringInfo(ValueObject &valobj) { l->GetChildMemberWithName(ConstString("__size_"), /*can_create=*/true); ValueObjectSP capacity_vo = l->GetChildMemberWithName(ConstString("__cap_"), /*can_create=*/true); - if (!capacity_vo) { - // After D128285, we need to access the `__cap_` field from a packed - // anonymous struct - if (ValueObjectSP packed_fields_sp = l->GetChildAtIndex(0, true)) { - ValueObjectSP capacity_vo = packed_fields_sp->GetChildMemberWithName( - ConstString("__cap_"), /*can_create=*/true); - } - } if (!size_vo || !location_sp || !capacity_vo) return {}; size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); - if (!using_bitmasks && layout == eLibcxxStringLayoutModeCSD) + if (!using_bitmasks && layout == StringLayout::CSD) capacity *= 2; if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || capacity < size) |