Skip to content

Commit

Permalink
[lldb][DataFormatter] Simplify std::map formatter (#97579)
Browse files Browse the repository at this point in the history
Depends on:
* #97544
* #97549
* #97551

This patch tries to simplify the way in which the `std::map` formatter
goes from the root `__tree` pointer to a specific key/value pair.

Previously we would:
1. synthesize a structure that mimicked what `__iter_pointer` looked
like in memory
2. call `GetChildCompilerTypeAtIndex` on it to find the byte offset at
which the pair was located in the synthesized structure
3. finally, use that offset through a call to
`GetSyntheticChildAtOffset` to retrieve the key/value pair

Not only was this 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. This allows us to get rid of some support infrastructure/class
state.

Ideally we would fix the underlying alignment issue, but this unblocks
the libc++ refactor in the interim, while also benefitting the formatter
in terms of readability (in my opinion).
  • Loading branch information
Michael137 authored Jul 8, 2024
1 parent 7776fba commit fe8933b
Showing 1 changed file with 41 additions and 109 deletions.
150 changes: 41 additions & 109 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,31 @@
#include "lldb/Utility/Stream.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include <cstdint>
#include <locale>
#include <optional>

using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::formatters;

// The flattened layout of the std::__tree_iterator::__ptr_ looks
// as follows:
//
// 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
// +-----------------------------+
//
// where __ptr_ has type __iter_pointer.

class MapEntry {
public:
MapEntry() = default;
Expand Down Expand Up @@ -182,10 +202,6 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
size_t GetIndexOfChildWithName(ConstString name) override;

private:
bool GetDataType();

void GetValueOffset(const lldb::ValueObjectSP &node);

/// Returns the ValueObject for the __tree_node type that
/// holds the key/value pair of the node at index \ref idx.
///
Expand All @@ -204,8 +220,7 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {

ValueObject *m_tree = nullptr;
ValueObject *m_root_node = nullptr;
CompilerType m_element_type;
uint32_t m_skip_size = UINT32_MAX;
CompilerType m_node_ptr_type;
size_t m_count = UINT32_MAX;
std::map<size_t, MapIterator> m_iterators;
};
Expand Down Expand Up @@ -234,7 +249,7 @@ class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {

lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
LibcxxStdMapSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
: SyntheticChildrenFrontEnd(*valobj_sp), m_element_type(), m_iterators() {
: SyntheticChildrenFrontEnd(*valobj_sp) {
if (valobj_sp)
Update();
}
Expand All @@ -260,130 +275,44 @@ llvm::Expected<uint32_t> lldb_private::formatters::
return m_count;
}

bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() {
if (m_element_type.IsValid())
return true;
m_element_type.Clear();
ValueObjectSP deref;
Status error;
deref = m_root_node->Dereference(error);
if (!deref || error.Fail())
return false;
deref = m_backend.GetChildAtNamePath({"__tree_", "__pair3_"});
if (!deref)
return false;
m_element_type = deref->GetCompilerType()
.GetTypeTemplateArgument(1)
.GetTypeTemplateArgument(1);
if (m_element_type) {
std::string name;
uint64_t bit_offset_ptr;
uint32_t bitfield_bit_size_ptr;
bool is_bitfield_ptr;
m_element_type = m_element_type.GetFieldAtIndex(
0, name, &bit_offset_ptr, &bitfield_bit_size_ptr, &is_bitfield_ptr);
m_element_type = m_element_type.GetTypedefedType();
return m_element_type.IsValid();
} else {
m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0);
return m_element_type.IsValid();
}
}

void lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset(
const lldb::ValueObjectSP &node) {
if (m_skip_size != UINT32_MAX)
return;
if (!node)
return;

CompilerType node_type(node->GetCompilerType());
auto ast_ctx = node_type.GetTypeSystem().dyn_cast_or_null<TypeSystemClang>();
if (!ast_ctx)
return;

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", (m_element_type.GetCompleteType(), m_element_type)}});
std::string child_name;
uint32_t child_byte_size;
int32_t child_byte_offset = 0;
uint32_t child_bitfield_bit_size;
uint32_t child_bitfield_bit_offset;
bool child_is_base_class;
bool child_is_deref_of_parent;
uint64_t language_flags;
auto child_type =
llvm::expectedToStdOptional(tree_node_type.GetChildCompilerTypeAtIndex(
nullptr, 4, true, true, true, child_name, child_byte_size,
child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset,
child_is_base_class, child_is_deref_of_parent, nullptr,
language_flags));
if (child_type && child_type->IsValid())
m_skip_size = (uint32_t)child_byte_offset;
}

ValueObjectSP
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetKeyValuePair(
size_t idx, size_t max_depth) {
MapIterator iterator(m_root_node, max_depth);

const bool need_to_skip = (idx > 0);
size_t actual_advance = idx;
if (need_to_skip) {
size_t advance_by = idx;
if (idx > 0) {
// If we have already created the iterator for the previous
// index, we can start from there and advance by 1.
auto cached_iterator = m_iterators.find(idx - 1);
if (cached_iterator != m_iterators.end()) {
iterator = cached_iterator->second;
actual_advance = 1;
advance_by = 1;
}
}

ValueObjectSP iterated_sp(iterator.advance(actual_advance));
ValueObjectSP iterated_sp(iterator.advance(advance_by));
if (!iterated_sp)
// this tree is garbage - stop
return nullptr;

if (!GetDataType())
if (!m_node_ptr_type.IsValid())
return nullptr;

if (!need_to_skip) {
Status error;
iterated_sp = iterated_sp->Dereference(error);
if (!iterated_sp || error.Fail())
return nullptr;

GetValueOffset(iterated_sp);
iterated_sp = iterated_sp->GetSyntheticChildAtOffset(m_skip_size,
m_element_type, true);

if (!iterated_sp)
return nullptr;
} else {
// because of the way our debug info is made, we need to read item 0
// first so that we can cache information used to generate other elements
if (m_skip_size == UINT32_MAX)
GetChildAtIndex(0);

if (m_skip_size == UINT32_MAX)
return nullptr;

iterated_sp = iterated_sp->GetSyntheticChildAtOffset(m_skip_size,
m_element_type, true);
if (!iterated_sp)
return nullptr;
}
// iterated_sp is a __iter_pointer at this point.
// We can cast it to a __node_pointer (which is what libc++ does).
auto value_type_sp = iterated_sp->Cast(m_node_ptr_type);
if (!value_type_sp)
return nullptr;

// Finally, get the key/value pair.
value_type_sp = value_type_sp->GetChildMemberWithName("__value_");
if (!value_type_sp)
return nullptr;

m_iterators[idx] = iterator;
assert(iterated_sp != nullptr &&
"Cached MapIterator for invalid ValueObject");

return iterated_sp;
return value_type_sp;
}

lldb::ValueObjectSP
Expand Down Expand Up @@ -443,6 +372,9 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
if (!m_tree)
return lldb::ChildCacheState::eRefetch;
m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get();
m_node_ptr_type =
m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer");

return lldb::ChildCacheState::eRefetch;
}

Expand Down

0 comments on commit fe8933b

Please sign in to comment.