Skip to content

[lldb][DataFormatter] Simplify libc++ std::map::iterator formatter #97713

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 0 additions & 188 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
29 changes: 4 additions & 25 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -247,6 +222,10 @@ SyntheticChildrenFrontEnd *
LibcxxStdMapSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP);

SyntheticChildrenFrontEnd *
LibCxxMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP);

SyntheticChildrenFrontEnd *
LibcxxStdUnorderedMapSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP);
Expand Down
115 changes: 115 additions & 0 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -208,6 +209,26 @@ 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 = default;

private:
ValueObjectSP m_pair_sp = nullptr;
};
} // namespace formatters
} // namespace lldb_private

Expand Down Expand Up @@ -456,3 +477,97 @@ 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) {
if (valobj_sp)
Update();
}

lldb::ChildCacheState
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::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 tree_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
if (!tree_iter_sp)
return lldb::ChildCacheState::eRefetch;

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;
}

llvm::Expected<uint32_t> lldb_private::formatters::
LibCxxMapIteratorSyntheticFrontEnd::CalculateNumChildren() {
return 2;
}

lldb::ValueObjectSP
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
if (!m_pair_sp)
return nullptr;

return m_pair_sp->GetChildAtIndex(idx);
}

bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
MightHaveChildren() {
return true;
}

size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
GetIndexOfChildWithName(ConstString name) {
if (name == "first")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since GetChildAtIndex is forwarding to m_pair_sp, could this do the same as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup good idea!

return 0;
if (name == "second")
return 1;
return UINT32_MAX;
}

SyntheticChildrenFrontEnd *
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
return (valobj_sp ? new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)
: nullptr);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"'])
Loading