[Lldb-commits] [lldb] Lldb/map iterator refactor (PR #97713)
Michael Buch via lldb-commits
lldb-commits at lists.llvm.org
Thu Jul 4 04:21:34 PDT 2024
https://github.com/Michael137 created https://github.com/llvm/llvm-project/pull/97713
Depends on https://github.com/llvm/llvm-project/pull/97687
Similar to https://github.com/llvm/llvm-project/pull/97579, this patch simplifies the way in which we retrieve the key/value pair of a `std::map` (in this case of the `std::map::iterator`).
We do this for the same reason: not only was the old logic hard to follow, and encoded the libc++ layout in non-obvious ways, it was also fragile to alignment miscalculations (https://github.com/llvm/llvm-project/pull/97443); this would break once the new layout of std::map landed as part of https://github.com/llvm/llvm-project/issues/93069.
Instead, this patch simply casts the `__iter_pointer` to the `__node_pointer` and uses a straightforward `GetChildMemberWithName("__value_")` to get to the key/value we care about.
We can eventually re-use the core-part of the `std::map` and `std::map::iterator` formatters. But it will be an easier to change to review once both simplifications landed.
>From f6b3e6055a9e2263f61e3f70d7a97ddbb7db5ab0 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 4 Jul 2024 09:30:19 +0200
Subject: [PATCH 1/2] [lldb][DataFormatter][NFC] Move std::map iterator
formatter into LibCxxMap.cpp
The two formatters follow very similar techniques to retrieve data out of the map. We're changing this for `std::map` in https://github.com/llvm/llvm-project/pull/97579 and plan to change it in the same way for the iterator formatter. Having them in the same place will allow us to re-use some of the logic (and we won't have to repeat some of the clarification comments).
---
.../Plugins/Language/CPlusPlus/LibCxx.cpp | 188 ------------------
.../Plugins/Language/CPlusPlus/LibCxx.h | 29 +--
.../Plugins/Language/CPlusPlus/LibCxxMap.cpp | 187 +++++++++++++++++
3 files changed, 191 insertions(+), 213 deletions(-)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
index ad467c3966e60..05cfa0568c25d 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -202,194 +202,6 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
return true;
}
-/*
- (lldb) fr var ibeg --raw --ptr-depth 1
- (std::__1::__map_iterator<std::__1::__tree_iterator<std::__1::pair<int,
- std::__1::basic_string<char, std::__1::char_traits<char>,
- std::__1::allocator<char> > >, std::__1::__tree_node<std::__1::pair<int,
- std::__1::basic_string<char, std::__1::char_traits<char>,
- std::__1::allocator<char> > >, void *> *, long> >) ibeg = {
- __i_ = {
- __ptr_ = 0x0000000100103870 {
- std::__1::__tree_node_base<void *> = {
- std::__1::__tree_end_node<std::__1::__tree_node_base<void *> *> = {
- __left_ = 0x0000000000000000
- }
- __right_ = 0x0000000000000000
- __parent_ = 0x00000001001038b0
- __is_black_ = true
- }
- __value_ = {
- first = 0
- second = { std::string }
- */
-
-lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
- LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
- : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
- if (valobj_sp)
- Update();
-}
-
-lldb::ChildCacheState
-lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
- m_pair_sp.reset();
- m_pair_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;
-
- // 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_pair_ptr = valobj_sp
- ->GetValueForExpressionPath(
- ".__i_.__ptr_->__value_", nullptr, nullptr,
- ValueObject::GetValueForExpressionPathOptions()
- .DontCheckDotVsArrowSyntax()
- .SetSyntheticChildrenTraversal(
- ValueObject::GetValueForExpressionPathOptions::
- SyntheticChildrenTraversal::None),
- nullptr)
- .get();
-
- if (!m_pair_ptr) {
- m_pair_ptr = valobj_sp
- ->GetValueForExpressionPath(
- ".__i_.__ptr_", nullptr, nullptr,
- ValueObject::GetValueForExpressionPathOptions()
- .DontCheckDotVsArrowSyntax()
- .SetSyntheticChildrenTraversal(
- ValueObject::GetValueForExpressionPathOptions::
- SyntheticChildrenTraversal::None),
- nullptr)
- .get();
- if (m_pair_ptr) {
- auto __i_(valobj_sp->GetChildMemberWithName("__i_"));
- if (!__i_) {
- m_pair_ptr = nullptr;
- return lldb::ChildCacheState::eRefetch;
- }
- CompilerType pair_type(
- __i_->GetCompilerType().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_pair_ptr = nullptr;
- return lldb::ChildCacheState::eRefetch;
- }
-
- auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
- m_pair_ptr = nullptr;
- if (addr && addr != LLDB_INVALID_ADDRESS) {
- 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::__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(
- llvm::StringRef(),
- {{"ptr0",
- ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
- {"ptr1",
- ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
- {"ptr2",
- ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
- {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
- {"payload", 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(4);
- }
- }
- }
-
- return lldb::ChildCacheState::eRefetch;
-}
-
-llvm::Expected<uint32_t> lldb_private::formatters::
- LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
- return 2;
-}
-
-lldb::ValueObjectSP
-lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
- uint32_t idx) {
- if (m_pair_ptr)
- return m_pair_ptr->GetChildAtIndex(idx);
- if (m_pair_sp)
- return m_pair_sp->GetChildAtIndex(idx);
- return lldb::ValueObjectSP();
-}
-
-bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
- MightHaveChildren() {
- return true;
-}
-
-size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
- GetIndexOfChildWithName(ConstString name) {
- if (name == "first")
- return 0;
- if (name == "second")
- return 1;
- return UINT32_MAX;
-}
-
-lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
- ~LibCxxMapIteratorSyntheticFrontEnd() {
- // this will be deleted when its parent dies (since it's a child object)
- // delete m_pair_ptr;
-}
-
-SyntheticChildrenFrontEnd *
-lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
- CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
- return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
- : nullptr);
-}
-
lldb_private::formatters::LibCxxUnorderedMapIteratorSyntheticFrontEnd::
LibCxxUnorderedMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
index 7fe15d1bf3f7a..21dba015d1ba1 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -87,31 +87,6 @@ bool LibcxxContainerSummaryProvider(ValueObject &valobj, Stream &stream,
bool LibcxxSpanSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options);
-class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
-public:
- LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
-
- 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;
-
- ~LibCxxMapIteratorSyntheticFrontEnd() override;
-
-private:
- ValueObject *m_pair_ptr;
- lldb::ValueObjectSP m_pair_sp;
-};
-
-SyntheticChildrenFrontEnd *
-LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
- lldb::ValueObjectSP);
-
/// Formats libcxx's std::unordered_map iterators
///
/// In raw form a std::unordered_map::iterator is represented as follows:
@@ -247,6 +222,10 @@ SyntheticChildrenFrontEnd *
LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP);
+SyntheticChildrenFrontEnd *
+LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP);
+
SyntheticChildrenFrontEnd *
LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP);
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
index 6c2bc1a34137a..c2bb3555908be 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -208,6 +208,27 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
size_t m_count = UINT32_MAX;
std::map<size_t, MapIterator> m_iterators;
};
+
+class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ 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;
+
+ ~LibCxxMapIteratorSyntheticFrontEnd() override;
+
+private:
+ ValueObject *m_pair_ptr;
+ lldb::ValueObjectSP m_pair_sp;
+};
} // namespace formatters
} // namespace lldb_private
@@ -456,3 +477,169 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
return (valobj_sp ? new LibcxxStdMapSyntheticFrontEnd(valobj_sp) : nullptr);
}
+
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
+ if (valobj_sp)
+ Update();
+}
+
+lldb::ChildCacheState
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
+ m_pair_sp.reset();
+ m_pair_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;
+
+ // 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_pair_ptr = valobj_sp
+ ->GetValueForExpressionPath(
+ ".__i_.__ptr_->__value_", nullptr, nullptr,
+ ValueObject::GetValueForExpressionPathOptions()
+ .DontCheckDotVsArrowSyntax()
+ .SetSyntheticChildrenTraversal(
+ ValueObject::GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::None),
+ nullptr)
+ .get();
+
+ if (!m_pair_ptr) {
+ m_pair_ptr = valobj_sp
+ ->GetValueForExpressionPath(
+ ".__i_.__ptr_", nullptr, nullptr,
+ ValueObject::GetValueForExpressionPathOptions()
+ .DontCheckDotVsArrowSyntax()
+ .SetSyntheticChildrenTraversal(
+ ValueObject::GetValueForExpressionPathOptions::
+ SyntheticChildrenTraversal::None),
+ nullptr)
+ .get();
+ if (m_pair_ptr) {
+ auto __i_(valobj_sp->GetChildMemberWithName("__i_"));
+ if (!__i_) {
+ m_pair_ptr = nullptr;
+ return lldb::ChildCacheState::eRefetch;
+ }
+ CompilerType pair_type(
+ __i_->GetCompilerType().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_pair_ptr = nullptr;
+ return lldb::ChildCacheState::eRefetch;
+ }
+
+ auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
+ m_pair_ptr = nullptr;
+ if (addr && addr != LLDB_INVALID_ADDRESS) {
+ 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::__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(
+ llvm::StringRef(),
+ {{"ptr0",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr1",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"ptr2",
+ ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
+ {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
+ {"payload", 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(4);
+ }
+ }
+ }
+
+ return lldb::ChildCacheState::eRefetch;
+}
+
+llvm::Expected<uint32_t> lldb_private::formatters::
+ LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
+ return 2;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
+ uint32_t idx) {
+ if (m_pair_ptr)
+ return m_pair_ptr->GetChildAtIndex(idx);
+ if (m_pair_sp)
+ return m_pair_sp->GetChildAtIndex(idx);
+ return lldb::ValueObjectSP();
+}
+
+bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ MightHaveChildren() {
+ return true;
+}
+
+size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (name == "first")
+ return 0;
+ if (name == "second")
+ return 1;
+ return UINT32_MAX;
+}
+
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
+ ~LibCxxMapIteratorSyntheticFrontEnd() {
+ // this will be deleted when its parent dies (since it's a child object)
+ // delete m_pair_ptr;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
>From ac7a74e913a75b13bda0f26ecea62862e6f48e4e Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 4 Jul 2024 12:49:35 +0200
Subject: [PATCH 2/2] [lldb][DataFormatter] Simplify libc++ std::map::iterator
formatter
---
.../Plugins/Language/CPlusPlus/LibCxxMap.cpp | 156 +++++-------------
.../TestDataFormatterLibccIterator.py | 10 ++
2 files changed, 52 insertions(+), 114 deletions(-)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
index c2bb3555908be..8655c4c1d505c 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -17,6 +17,7 @@
#include "lldb/Utility/Endian.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
using namespace lldb;
@@ -223,11 +224,10 @@ class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
size_t GetIndexOfChildWithName(ConstString name) override;
- ~LibCxxMapIteratorSyntheticFrontEnd() override;
+ ~LibCxxMapIteratorSyntheticFrontEnd() override = default;
private:
- ValueObject *m_pair_ptr;
- lldb::ValueObjectSP m_pair_sp;
+ ValueObjectSP m_pair_sp = nullptr;
};
} // namespace formatters
} // namespace lldb_private
@@ -480,7 +480,7 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator(
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
- : SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
if (valobj_sp)
Update();
}
@@ -488,117 +488,52 @@ lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
lldb::ChildCacheState
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
m_pair_sp.reset();
- m_pair_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;
- // 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_pair_ptr = valobj_sp
- ->GetValueForExpressionPath(
- ".__i_.__ptr_->__value_", nullptr, nullptr,
- ValueObject::GetValueForExpressionPathOptions()
- .DontCheckDotVsArrowSyntax()
- .SetSyntheticChildrenTraversal(
- ValueObject::GetValueForExpressionPathOptions::
- SyntheticChildrenTraversal::None),
- nullptr)
- .get();
-
- if (!m_pair_ptr) {
- m_pair_ptr = valobj_sp
- ->GetValueForExpressionPath(
- ".__i_.__ptr_", nullptr, nullptr,
- ValueObject::GetValueForExpressionPathOptions()
- .DontCheckDotVsArrowSyntax()
- .SetSyntheticChildrenTraversal(
- ValueObject::GetValueForExpressionPathOptions::
- SyntheticChildrenTraversal::None),
- nullptr)
- .get();
- if (m_pair_ptr) {
- auto __i_(valobj_sp->GetChildMemberWithName("__i_"));
- if (!__i_) {
- m_pair_ptr = nullptr;
- return lldb::ChildCacheState::eRefetch;
- }
- CompilerType pair_type(
- __i_->GetCompilerType().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_pair_ptr = nullptr;
- return lldb::ChildCacheState::eRefetch;
- }
+ auto tree_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
+ if (!tree_iter_sp)
+ return lldb::ChildCacheState::eRefetch;
- auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
- m_pair_ptr = nullptr;
- if (addr && addr != LLDB_INVALID_ADDRESS) {
- 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::__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(
- llvm::StringRef(),
- {{"ptr0",
- ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
- {"ptr1",
- ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
- {"ptr2",
- ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
- {"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
- {"payload", 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(4);
- }
- }
+ auto node_pointer_type =
+ tree_iter_sp->GetCompilerType().GetDirectNestedTypeWithName(
+ "__node_pointer");
+ if (!node_pointer_type.IsValid())
+ return lldb::ChildCacheState::eRefetch;
+
+ auto iter_pointer_sp = tree_iter_sp->GetChildMemberWithName("__ptr_");
+ if (!iter_pointer_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ auto node_pointer_sp = iter_pointer_sp->Cast(node_pointer_type);
+ if (!node_pointer_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ Status err;
+ node_pointer_sp = node_pointer_sp->Dereference(err);
+ if (!node_pointer_sp || err.Fail())
+ return lldb::ChildCacheState::eRefetch;
+
+ auto key_value_sp = node_pointer_sp->GetChildMemberWithName("__value_");
+ if (!key_value_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ key_value_sp = key_value_sp->Clone(ConstString("name"));
+ if (key_value_sp->GetNumChildrenIgnoringErrors() == 1) {
+ auto child0_sp = key_value_sp->GetChildAtIndex(0);
+ if (child0_sp &&
+ (child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc"))
+ key_value_sp = child0_sp->Clone(ConstString("pair"));
}
+ m_pair_sp = key_value_sp;
+
return lldb::ChildCacheState::eRefetch;
}
@@ -610,11 +545,10 @@ llvm::Expected<uint32_t> lldb_private::formatters::
lldb::ValueObjectSP
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
- if (m_pair_ptr)
- return m_pair_ptr->GetChildAtIndex(idx);
- if (m_pair_sp)
- return m_pair_sp->GetChildAtIndex(idx);
- return lldb::ValueObjectSP();
+ if (!m_pair_sp)
+ return nullptr;
+
+ return m_pair_sp->GetChildAtIndex(idx);
}
bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
@@ -631,12 +565,6 @@ size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
return UINT32_MAX;
}
-lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
- ~LibCxxMapIteratorSyntheticFrontEnd() {
- // this will be deleted when its parent dies (since it's a child object)
- // delete m_pair_ptr;
-}
-
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
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..709c899211ca1 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
@@ -54,8 +54,18 @@ def cleanup():
self.expect("frame variable iimI", substrs=["first = 43981", "second = 61681"])
self.expect("expr iimI", substrs=["first = 43981", "second = 61681"])
+ self.expect("frame variable iimI.first", substrs=["first = 43981"])
+ self.expect("frame variable iimI.first", substrs=["second"], matching=False)
+ self.expect("frame variable iimI.second", substrs=["second = 61681"])
+ self.expect("frame variable iimI.second", substrs=["first"], matching=False)
+
self.expect("frame variable simI", substrs=['first = "world"', "second = 42"])
self.expect("expr simI", substrs=['first = "world"', "second = 42"])
+ self.expect("frame variable simI.first", substrs=['first = "world"'])
+ self.expect("frame variable simI.first", substrs=["second"], matching=False)
+ self.expect("frame variable simI.second", substrs=["second = 42"])
+ self.expect("frame variable simI.second", substrs=["first"], matching=False)
+
self.expect("frame variable svI", substrs=['item = "hello"'])
self.expect("expr svI", substrs=['item = "hello"'])
More information about the lldb-commits
mailing list