Skip to content

Commit 9dca3ac

Browse files
authored
[lldb][DataFormatter] Simplify libc++ std::map::iterator formatter (#97713)
Depends on #97687 Similar to #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 (#97443); this would break once the new layout of std::map landed as part of #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.
1 parent a5bfe20 commit 9dca3ac

File tree

2 files changed

+66
-118
lines changed

2 files changed

+66
-118
lines changed

lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp

Lines changed: 56 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "lldb/Utility/Endian.h"
1818
#include "lldb/Utility/Status.h"
1919
#include "lldb/Utility/Stream.h"
20+
#include "lldb/lldb-enumerations.h"
2021
#include "lldb/lldb-forward.h"
2122

2223
using namespace lldb;
@@ -223,11 +224,10 @@ class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
223224

224225
size_t GetIndexOfChildWithName(ConstString name) override;
225226

226-
~LibCxxMapIteratorSyntheticFrontEnd() override;
227+
~LibCxxMapIteratorSyntheticFrontEnd() override = default;
227228

228229
private:
229-
ValueObject *m_pair_ptr;
230-
lldb::ValueObjectSP m_pair_sp;
230+
ValueObjectSP m_pair_sp = nullptr;
231231
};
232232
} // namespace formatters
233233
} // namespace lldb_private
@@ -464,125 +464,71 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator(
464464

465465
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
466466
LibCxxMapIteratorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
467-
: SyntheticChildrenFrontEnd(*valobj_sp), m_pair_ptr(), m_pair_sp() {
467+
: SyntheticChildrenFrontEnd(*valobj_sp) {
468468
if (valobj_sp)
469469
Update();
470470
}
471471

472472
lldb::ChildCacheState
473473
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() {
474474
m_pair_sp.reset();
475-
m_pair_ptr = nullptr;
476475

477476
ValueObjectSP valobj_sp = m_backend.GetSP();
478477
if (!valobj_sp)
479478
return lldb::ChildCacheState::eRefetch;
480479

481480
TargetSP target_sp(valobj_sp->GetTargetSP());
482-
483481
if (!target_sp)
484482
return lldb::ChildCacheState::eRefetch;
485483

486-
// this must be a ValueObject* because it is a child of the ValueObject we
487-
// are producing children for it if were a ValueObjectSP, we would end up
488-
// with a loop (iterator -> synthetic -> child -> parent == iterator) and
489-
// that would in turn leak memory by never allowing the ValueObjects to die
490-
// and free their memory
491-
m_pair_ptr = valobj_sp
492-
->GetValueForExpressionPath(
493-
".__i_.__ptr_->__value_", nullptr, nullptr,
494-
ValueObject::GetValueForExpressionPathOptions()
495-
.DontCheckDotVsArrowSyntax()
496-
.SetSyntheticChildrenTraversal(
497-
ValueObject::GetValueForExpressionPathOptions::
498-
SyntheticChildrenTraversal::None),
499-
nullptr)
500-
.get();
501-
502-
if (!m_pair_ptr) {
503-
m_pair_ptr = valobj_sp
504-
->GetValueForExpressionPath(
505-
".__i_.__ptr_", nullptr, nullptr,
506-
ValueObject::GetValueForExpressionPathOptions()
507-
.DontCheckDotVsArrowSyntax()
508-
.SetSyntheticChildrenTraversal(
509-
ValueObject::GetValueForExpressionPathOptions::
510-
SyntheticChildrenTraversal::None),
511-
nullptr)
512-
.get();
513-
if (m_pair_ptr) {
514-
auto __i_(valobj_sp->GetChildMemberWithName("__i_"));
515-
if (!__i_) {
516-
m_pair_ptr = nullptr;
517-
return lldb::ChildCacheState::eRefetch;
518-
}
519-
CompilerType pair_type(
520-
__i_->GetCompilerType().GetTypeTemplateArgument(0));
521-
std::string name;
522-
uint64_t bit_offset_ptr;
523-
uint32_t bitfield_bit_size_ptr;
524-
bool is_bitfield_ptr;
525-
pair_type = pair_type.GetFieldAtIndex(
526-
0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
527-
if (!pair_type) {
528-
m_pair_ptr = nullptr;
529-
return lldb::ChildCacheState::eRefetch;
530-
}
484+
// m_backend is a std::map::iterator
485+
// ...which is a __map_iterator<__tree_iterator<..., __node_pointer, ...>>
486+
//
487+
// Then, __map_iterator::__i_ is a __tree_iterator
488+
auto tree_iter_sp = valobj_sp->GetChildMemberWithName("__i_");
489+
if (!tree_iter_sp)
490+
return lldb::ChildCacheState::eRefetch;
531491

532-
auto addr(m_pair_ptr->GetValueAsUnsigned(LLDB_INVALID_ADDRESS));
533-
m_pair_ptr = nullptr;
534-
if (addr && addr != LLDB_INVALID_ADDRESS) {
535-
auto ts = pair_type.GetTypeSystem();
536-
auto ast_ctx = ts.dyn_cast_or_null<TypeSystemClang>();
537-
if (!ast_ctx)
538-
return lldb::ChildCacheState::eRefetch;
539-
540-
// Mimick layout of std::__tree_iterator::__ptr_ and read it in
541-
// from process memory.
542-
//
543-
// The following shows the contiguous block of memory:
544-
//
545-
// +-----------------------------+ class __tree_end_node
546-
// __ptr_ | pointer __left_; |
547-
// +-----------------------------+ class __tree_node_base
548-
// | pointer __right_; |
549-
// | __parent_pointer __parent_; |
550-
// | bool __is_black_; |
551-
// +-----------------------------+ class __tree_node
552-
// | __node_value_type __value_; | <<< our key/value pair
553-
// +-----------------------------+
554-
//
555-
CompilerType tree_node_type = ast_ctx->CreateStructForIdentifier(
556-
llvm::StringRef(),
557-
{{"ptr0",
558-
ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
559-
{"ptr1",
560-
ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
561-
{"ptr2",
562-
ast_ctx->GetBasicType(lldb::eBasicTypeVoid).GetPointerType()},
563-
{"cw", ast_ctx->GetBasicType(lldb::eBasicTypeBool)},
564-
{"payload", pair_type}});
565-
std::optional<uint64_t> size = tree_node_type.GetByteSize(nullptr);
566-
if (!size)
567-
return lldb::ChildCacheState::eRefetch;
568-
WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
569-
ProcessSP process_sp(target_sp->GetProcessSP());
570-
Status error;
571-
process_sp->ReadMemory(addr, buffer_sp->GetBytes(),
572-
buffer_sp->GetByteSize(), error);
573-
if (error.Fail())
574-
return lldb::ChildCacheState::eRefetch;
575-
DataExtractor extractor(buffer_sp, process_sp->GetByteOrder(),
576-
process_sp->GetAddressByteSize());
577-
auto pair_sp = CreateValueObjectFromData(
578-
"pair", extractor, valobj_sp->GetExecutionContextRef(),
579-
tree_node_type);
580-
if (pair_sp)
581-
m_pair_sp = pair_sp->GetChildAtIndex(4);
582-
}
583-
}
492+
// Type is __tree_iterator::__node_pointer
493+
// (We could alternatively also get this from the template argument)
494+
auto node_pointer_type =
495+
tree_iter_sp->GetCompilerType().GetDirectNestedTypeWithName(
496+
"__node_pointer");
497+
if (!node_pointer_type.IsValid())
498+
return lldb::ChildCacheState::eRefetch;
499+
500+
// __ptr_ is a __tree_iterator::__iter_pointer
501+
auto iter_pointer_sp = tree_iter_sp->GetChildMemberWithName("__ptr_");
502+
if (!iter_pointer_sp)
503+
return lldb::ChildCacheState::eRefetch;
504+
505+
// Cast the __iter_pointer to a __node_pointer (which stores our key/value
506+
// pair)
507+
auto node_pointer_sp = iter_pointer_sp->Cast(node_pointer_type);
508+
if (!node_pointer_sp)
509+
return lldb::ChildCacheState::eRefetch;
510+
511+
auto key_value_sp = node_pointer_sp->GetChildMemberWithName("__value_");
512+
if (!key_value_sp)
513+
return lldb::ChildCacheState::eRefetch;
514+
515+
// Create the synthetic child, which is a pair where the key and value can be
516+
// retrieved by querying the synthetic frontend for
517+
// GetIndexOfChildWithName("first") and GetIndexOfChildWithName("second")
518+
// respectively.
519+
//
520+
// std::map stores the actual key/value pair in value_type::__cc_ (or
521+
// previously __cc).
522+
key_value_sp = key_value_sp->Clone(ConstString("pair"));
523+
if (key_value_sp->GetNumChildrenIgnoringErrors() == 1) {
524+
auto child0_sp = key_value_sp->GetChildAtIndex(0);
525+
if (child0_sp &&
526+
(child0_sp->GetName() == "__cc_" || child0_sp->GetName() == "__cc"))
527+
key_value_sp = child0_sp->Clone(ConstString("pair"));
584528
}
585529

530+
m_pair_sp = key_value_sp;
531+
586532
return lldb::ChildCacheState::eRefetch;
587533
}
588534

@@ -594,11 +540,10 @@ llvm::Expected<uint32_t> lldb_private::formatters::
594540
lldb::ValueObjectSP
595541
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetChildAtIndex(
596542
uint32_t idx) {
597-
if (m_pair_ptr)
598-
return m_pair_ptr->GetChildAtIndex(idx);
599-
if (m_pair_sp)
600-
return m_pair_sp->GetChildAtIndex(idx);
601-
return lldb::ValueObjectSP();
543+
if (!m_pair_sp)
544+
return nullptr;
545+
546+
return m_pair_sp->GetChildAtIndex(idx);
602547
}
603548

604549
bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
@@ -608,17 +553,10 @@ bool lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
608553

609554
size_t lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
610555
GetIndexOfChildWithName(ConstString name) {
611-
if (name == "first")
612-
return 0;
613-
if (name == "second")
614-
return 1;
615-
return UINT32_MAX;
616-
}
556+
if (!m_pair_sp)
557+
return UINT32_MAX;
617558

618-
lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::
619-
~LibCxxMapIteratorSyntheticFrontEnd() {
620-
// this will be deleted when its parent dies (since it's a child object)
621-
// delete m_pair_ptr;
559+
return m_pair_sp->GetIndexOfChildWithName(name);
622560
}
623561

624562
SyntheticChildrenFrontEnd *

lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/iterator/TestDataFormatterLibccIterator.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,19 @@ def cleanup():
5454
self.expect("frame variable iimI", substrs=["first = 43981", "second = 61681"])
5555
self.expect("expr iimI", substrs=["first = 43981", "second = 61681"])
5656

57+
self.expect("frame variable iimI.first", substrs=["first = 43981"])
58+
self.expect("frame variable iimI.first", substrs=["second"], matching=False)
59+
self.expect("frame variable iimI.second", substrs=["second = 61681"])
60+
self.expect("frame variable iimI.second", substrs=["first"], matching=False)
61+
5762
self.expect("frame variable simI", substrs=['first = "world"', "second = 42"])
5863
self.expect("expr simI", substrs=['first = "world"', "second = 42"])
5964

65+
self.expect("frame variable simI.first", substrs=['first = "world"'])
66+
self.expect("frame variable simI.first", substrs=["second"], matching=False)
67+
self.expect("frame variable simI.second", substrs=["second = 42"])
68+
self.expect("frame variable simI.second", substrs=["first"], matching=False)
69+
6070
self.expect("frame variable svI", substrs=['item = "hello"'])
6171
self.expect("expr svI", substrs=['item = "hello"'])
6272

0 commit comments

Comments
 (0)