llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Michael Buch (Michael137) <details> <summary>Changes</summary> Depends on https://github.com/llvm/llvm-project/pull/97752 This patch changes the way we retrieve the key/value pair in the `std::unordered_map::iterator` formatter (similar to how we are changing it for `std::map::iterator` in https://github.com/llvm/llvm-project/pull/97713, the motivations being the same). The `std::unordered_map` already does it this way, so we align the iterator counterpart to do the same. --- Full diff: https://github.com/llvm/llvm-project/pull/97754.diff 5 Files Affected: - (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp (-149) - (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxx.h (+4-50) - (modified) lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp (+150) - (modified) lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py (+16) - (modified) lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp (+19-6) ``````````diff diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index 05cfa0568c25d..feaa51a96843a 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -202,155 +202,6 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( return true; } -lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: - LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) - : SyntheticChildrenFrontEnd(*valobj_sp) { - if (valobj_sp) - Update(); -} - -lldb::ChildCacheState lldb_private::formatters:: - LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() { - m_pair_sp.reset(); - m_iter_ptr = nullptr; - - ValueObjectSP valobj_sp = m_backend.GetSP(); - if (!valobj_sp) - return lldb::ChildCacheState::eRefetch; - - TargetSP target_sp(valobj_sp->GetTargetSP()); - - if (!target_sp) - return lldb::ChildCacheState::eRefetch; - - if (!valobj_sp) - return lldb::ChildCacheState::eRefetch; - - 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("__i_")); - if (!iter_child) { - m_iter_ptr = nullptr; - return lldb::ChildCacheState::eRefetch; - } - - 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 lldb::ChildCacheState::eRefetch; - } - - uint64_t addr = m_iter_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); - m_iter_ptr = nullptr; - - if (addr == 0 || addr == LLDB_INVALID_ADDRESS) - return lldb::ChildCacheState::eRefetch; - - auto ts = pair_type.GetTypeSystem(); - auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>(); - if (!ast_ctx) - return lldb::ChildCacheState::eRefetch; - - // 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( - llvm::StringRef(), - {{"__next_", - ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()}, - {"__hash_", ast_ctx->GetBasicType(lldb::eBasicTypeUnsignedLongLong)}, - {"__value_", pair_type}}); - std::optional<uint64_t> size = tree_node_type.GetByteSize(nullptr); - if (!size) - return lldb::ChildCacheState::eRefetch; - 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 lldb::ChildCacheState::eRefetch; - 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); - } - - return lldb::ChildCacheState::eRefetch; -} - -llvm::Expected<uint32_t> lldb_private::formatters:: - LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() { - return 2; -} - -lldb::ValueObjectSP lldb_private::formatters:: - LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { - if (m_pair_sp) - return m_pair_sp->GetChildAtIndex(idx); - 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 = { diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h index 21dba015d1ba1..5307b5251ca84 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -87,56 +87,6 @@ bool LibcxxContainerSummaryProvider(ValueObject &valobj, Stream &stream, bool LibcxxSpanSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); -/// Formats libcxx's std::unordered_map iterators -/// -/// In raw form a std::unordered_map::iterator is represented as follows: -/// -/// (lldb) var it --raw --ptr-depth 1 -/// (std::__1::__hash_map_iterator< -/// std::__1::__hash_iterator< -/// std::__1::__hash_node< -/// std::__1::__hash_value_type< -/// std::__1::basic_string<char, std::__1::char_traits<char>, -/// std::__1::allocator<char> >, std::__1::basic_string<char, -/// std::__1::char_traits<char>, std::__1::allocator<char> > >, -/// void *> *> >) -/// it = { -/// __i_ = { -/// __node_ = 0x0000600001700040 { -/// __next_ = 0x0000600001704000 -/// } -/// } -/// } -class LibCxxUnorderedMapIteratorSyntheticFrontEnd - : public SyntheticChildrenFrontEnd { -public: - LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); - - ~LibCxxUnorderedMapIteratorSyntheticFrontEnd() override = default; - - llvm::Expected<uint32_t> CalculateNumChildren() override; - - lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; - - lldb::ChildCacheState Update() override; - - bool MightHaveChildren() override; - - size_t GetIndexOfChildWithName(ConstString name) override; - -private: - ValueObject *m_iter_ptr = nullptr; ///< Held, not owned. Child of iterator - ///< ValueObject supplied at construction. - - lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair - ///< that the iterator currently points - ///< to. -}; - -SyntheticChildrenFrontEnd * -LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, - lldb::ValueObjectSP); - SyntheticChildrenFrontEnd * LibCxxVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); @@ -230,6 +180,10 @@ SyntheticChildrenFrontEnd * LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd * +LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + SyntheticChildrenFrontEnd * LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp index af29fdb6d0010..bfd8b648a6a46 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -51,6 +51,50 @@ class LibcxxStdUnorderedMapSyntheticFrontEnd ValueObject *m_next_element = nullptr; std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache; }; + +/// Formats libcxx's std::unordered_map iterators +/// +/// In raw form a std::unordered_map::iterator is represented as follows: +/// +/// (lldb) var it --raw --ptr-depth 1 +/// (std::__1::__hash_map_iterator< +/// std::__1::__hash_iterator< +/// std::__1::__hash_node< +/// std::__1::__hash_value_type< +/// std::__1::basic_string<char, std::__1::char_traits<char>, +/// std::__1::allocator<char> >, std::__1::basic_string<char, +/// std::__1::char_traits<char>, std::__1::allocator<char> > >, +/// void *> *> >) +/// it = { +/// __i_ = { +/// __node_ = 0x0000600001700040 { +/// __next_ = 0x0000600001704000 +/// } +/// } +/// } +class LibCxxUnorderedMapIteratorSyntheticFrontEnd + : public SyntheticChildrenFrontEnd { +public: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp); + + ~LibCxxUnorderedMapIteratorSyntheticFrontEnd() override = default; + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + + bool MightHaveChildren() override; + + size_t GetIndexOfChildWithName(ConstString name) override; + +private: + lldb::ValueObjectSP m_pair_sp; ///< ValueObject for the key/value pair + ///< that the iterator currently points + ///< to. +}; + } // namespace formatters } // namespace lldb_private @@ -246,3 +290,109 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEndCreator( return (valobj_sp ? new LibcxxStdUnorderedMapSyntheticFrontEnd(valobj_sp) : nullptr); } + +lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +lldb::ChildCacheState lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::Update() { + m_pair_sp.reset(); + + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + TargetSP target_sp(valobj_sp->GetTargetSP()); + + if (!target_sp) + return lldb::ChildCacheState::eRefetch; + + auto hash_iter_sp = valobj_sp->GetChildMemberWithName("__i_"); + if (!hash_iter_sp) + return lldb::ChildCacheState::eRefetch; + + auto hash_iter_type = hash_iter_sp->GetCompilerType(); + if (!hash_iter_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + auto node_pointer_type = hash_iter_type.GetTypeTemplateArgument(0); + if (!node_pointer_type.IsValid()) + return lldb::ChildCacheState::eRefetch; + + auto hash_node_sp = hash_iter_sp->GetChildMemberWithName("__node_"); + if (!hash_node_sp) + return lldb::ChildCacheState::eRefetch; + + hash_node_sp = hash_iter_sp->Cast(node_pointer_type); + if (!hash_node_sp) + return lldb::ChildCacheState::eRefetch; + + auto key_value_sp = hash_node_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) { + key_value_sp = hash_node_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) { + // clang-format off + // Since D101206 (ba79fb2e1f), libc++ wraps the `__value_` in an + // anonymous union. + // Child 0: __hash_node_base base class + // Child 1: __hash_ + // Child 2: anonymous union + // clang-format on + auto anon_union_sp = hash_node_sp->GetChildAtIndex(2); + if (!anon_union_sp) + return lldb::ChildCacheState::eRefetch; + + key_value_sp = anon_union_sp->GetChildMemberWithName("__value_"); + if (!key_value_sp) + return lldb::ChildCacheState::eRefetch; + } + } + + auto potential_child_sp = key_value_sp->Clone(ConstString("pair")); + if (potential_child_sp) + if (potential_child_sp->GetNumChildrenIgnoringErrors() == 1) + if (auto child0_sp = potential_child_sp->GetChildAtIndex(0); + child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc") + potential_child_sp = child0_sp->Clone(ConstString("pair")); + + m_pair_sp = potential_child_sp; + + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::CalculateNumChildren() { + return 2; +} + +lldb::ValueObjectSP lldb_private::formatters:: + LibCxxUnorderedMapIteratorSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { + if (m_pair_sp) + return m_pair_sp->GetChildAtIndex(idx); + 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); +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py index d9e316b9b8f4e..efd7128cd6ac7 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py @@ -59,3 +59,19 @@ def cleanup(): self.expect("frame variable svI", substrs=['item = "hello"']) self.expect("expr svI", substrs=['item = "hello"']) + + self.expect("frame variable iiumI", substrs=["first = 61453", "second = 51966"]) + self.expect("expr iiumI", substrs=["first = 61453", "second = 51966"]) + + self.expect("frame variable siumI", substrs=['first = "hello"', "second = 137"]) + self.expect("expr siumI", substrs=['first = "hello"', "second = 137"]) + + self.expect("frame variable iiumI.first", substrs=["first = 61453"]) + self.expect("frame variable iiumI.first", substrs=["second"], matching=False) + self.expect("frame variable iiumI.second", substrs=["second = 51966"]) + self.expect("frame variable iiumI.second", substrs=["first"], matching=False) + + self.expect("frame variable siumI.first", substrs=['first = "hello"']) + self.expect("frame variable siumI.first", substrs=["second"], matching=False) + self.expect("frame variable siumI.second", substrs=["second = 137"]) + self.expect("frame variable siumI.second", substrs=["first"], matching=False) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp index 9d1cbfd912868..534a22f98ffb8 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/main.cpp @@ -5,11 +5,16 @@ typedef std::map<int, int> intint_map; typedef std::map<std::string, int> strint_map; +typedef std::unordered_map<int, int> intint_umap; +typedef std::unordered_map<std::string, int> strint_umap; + typedef std::vector<int> int_vector; typedef std::vector<std::string> string_vector; -typedef intint_map::iterator iimter; -typedef strint_map::iterator simter; +typedef intint_map::iterator ii_map_iter; +typedef strint_map::iterator si_map_iter; +typedef intint_umap::iterator ii_umap_iter; +typedef strint_umap::iterator si_umap_iter; typedef int_vector::iterator ivter; typedef string_vector::iterator svter; @@ -22,16 +27,24 @@ int main() strint_map sim; sim["world"] = 42; - int_vector iv; + intint_umap iium; + iium[0xF00D] = 0xCAFE; + + strint_umap sium; + sium["hello"] = 137; + + int_vector iv; iv.push_back(3); string_vector sv; sv.push_back("hello"); - iimter iimI = iim.begin(); - simter simI = sim.begin(); + ii_map_iter iimI = iim.begin(); + si_map_iter simI = sim.begin(); + ii_umap_iter iiumI = iium.begin(); + si_umap_iter siumI = sium.begin(); - ivter ivI = iv.begin(); + ivter ivI = iv.begin(); svter svI = sv.begin(); return 0; // Set break point at this line. `````````` </details> https://github.com/llvm/llvm-project/pull/97754 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits