Skip to content

Commit

Permalink
[lldb] Support new libc++ __compressed_pair layout
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael137 committed Jul 10, 2024
1 parent f4c7811 commit 2b82d72
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 131 deletions.
26 changes: 20 additions & 6 deletions lldb/examples/synthetic/libcxx.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,12 @@ def _get_value_of_compressed_pair(self, pair):
def update(self):
logger = lldb.formatters.Logger.Logger()
try:
has_compressed_pair_layout = True
alloc_valobj = self.valobj.GetChildMemberWithName("__alloc_")
size_valobj = self.valobj.GetChildMemberWithName("__size_")
if alloc_valobj.IsValid() and size_valobj.IsValid():
has_compressed_pair_layout = False

# A deque is effectively a two-dim array, with fixed width.
# 'map' contains pointers to the rows of this array. The
# full memory area allocated by the deque is delimited
Expand All @@ -734,9 +740,13 @@ def update(self):
# variable tells which element in this NxM array is the 0th
# one, and the 'size' element gives the number of elements
# in the deque.
count = self._get_value_of_compressed_pair(
self.valobj.GetChildMemberWithName("__size_")
)
if has_compressed_pair_layout:
count = self._get_value_of_compressed_pair(
self.valobj.GetChildMemberWithName("__size_")
)
else:
count = size_valobj.GetValueAsUnsigned(0)

# give up now if we cant access memory reliably
if self.block_size < 0:
logger.write("block_size < 0")
Expand All @@ -748,9 +758,13 @@ def update(self):
self.map_begin = map_.GetChildMemberWithName("__begin_")
map_begin = self.map_begin.GetValueAsUnsigned(0)
map_end = map_.GetChildMemberWithName("__end_").GetValueAsUnsigned(0)
map_endcap = self._get_value_of_compressed_pair(
map_.GetChildMemberWithName("__end_cap_")
)

if has_compressed_pair_layout:
map_endcap = self._get_value_of_compressed_pair(
map_.GetChildMemberWithName("__end_cap_")
)
else:
map_endcap = map_.GetChildMemberWithName("__end_cap_").GetValueAsUnsigned(0)

# check consistency
if not map_first <= map_begin <= map_end <= map_endcap:
Expand Down
78 changes: 58 additions & 20 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,39 @@
#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include <optional>
#include <tuple>

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

static bool isOldCompressedPairLayout(ValueObject &pair_obj) {
return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
}

static void consumeInlineNamespace(llvm::StringRef &name) {
// Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
auto scratch = name;
if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
if (scratch.consume_front("::")) {
// Successfully consumed a namespace.
name = scratch;
}
}
}

bool lldb_private::formatters::isStdTemplate(ConstString type_name, llvm::StringRef type) {
llvm::StringRef name = type_name.GetStringRef();
// The type name may be prefixed with `std::__<inline-namespace>::`.
if (name.consume_front("std::"))
consumeInlineNamespace(name);
return name.consume_front(type) && name.starts_with("<");
}


lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName(
ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) {
for (ConstString name : alternative_names) {
Expand Down Expand Up @@ -176,7 +202,9 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
if (!ptr_sp)
return false;

ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
if (isOldCompressedPairLayout(*ptr_sp))
ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);

if (!ptr_sp)
return false;

Expand Down Expand Up @@ -512,13 +540,20 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {

// Retrieve the actual pointer and the deleter, and clone them to give them
// user-friendly names.
ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
if (value_pointer_sp)
m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
if (isOldCompressedPairLayout(*ptr_sp)) {
if (ValueObjectSP value_pointer_sp =
GetFirstValueOfLibCXXCompressedPair(*ptr_sp))
m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));

ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp);
if (deleter_sp)
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
if (ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp))
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
} else {
m_value_ptr_sp = ptr_sp->Clone(ConstString("pointer"));

if (ValueObjectSP deleter_sp = valobj_sp->GetChildMemberWithName("__deleter_"))
if (deleter_sp->GetNumChildrenIgnoringErrors() > 0)
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
}

return lldb::ChildCacheState::eRefetch;
}
Expand Down Expand Up @@ -556,24 +591,27 @@ namespace {
enum class StringLayout { CSD, DSC };
}

static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) {
if (auto rep_sp = valobj.GetChildMemberWithName("__rep_"))
return rep_sp;

ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
return nullptr;

if (!isOldCompressedPairLayout(*valobj_r_sp))
return nullptr;

return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp);
}

/// Determine the size in bytes of \p valobj (a libc++ std::string object) and
/// extract its data payload. Return the size + payload pair.
// TODO: Support big-endian architectures.
static std::optional<std::pair<uint64_t, ValueObjectSP>>
ExtractLibcxxStringInfo(ValueObject &valobj) {
ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
return {};

// __r_ is a compressed_pair of the actual data and the allocator. The data we
// want is in the first base class.
ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0);
if (!valobj_r_base_sp)
return {};

ValueObjectSP valobj_rep_sp =
valobj_r_base_sp->GetChildMemberWithName("__value_");
if (!valobj_rep_sp)
ValueObjectSP valobj_rep_sp = ExtractLibCxxStringData(valobj);
if (!valobj_rep_sp || !valobj_rep_sp->GetError().Success())
return {};

ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l");
Expand Down
1 change: 1 addition & 0 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ GetChildMemberWithName(ValueObject &obj,
lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair);
lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair);

bool isStdTemplate(ConstString type_name, llvm::StringRef type);

bool LibcxxStringSummaryProviderASCII(
ValueObject &valobj, Stream &stream,
Expand Down
83 changes: 53 additions & 30 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxxList.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"

using namespace lldb;
using namespace lldb_private;
Expand Down Expand Up @@ -294,12 +295,19 @@ lldb::ChildCacheState ForwardListFrontEnd::Update() {

ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_"));
if (!impl_sp)
return lldb::ChildCacheState::eRefetch;
impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
if (!impl_sp)
return lldb::ChildCacheState::eRefetch;
return ChildCacheState::eRefetch;

m_head = impl_sp->GetChildMemberWithName("__next_").get();
return lldb::ChildCacheState::eRefetch;

// TODO: we have to do this in this order because __before_begin_ has a
// __value_ member, as does compressed_pair. Is this a problem elsewhere too?
if (!m_head) {
impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
if (impl_sp)
m_head = impl_sp->GetChildMemberWithName("__next_").get();
}

return ChildCacheState::eRefetch;
}

ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp)
Expand All @@ -313,34 +321,49 @@ llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() {
return m_count;
if (!m_head || !m_tail || m_node_address == 0)
return 0;
ValueObjectSP size_alloc(m_backend.GetChildMemberWithName("__size_alloc_"));
if (size_alloc) {
ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_alloc);
if (value) {
m_count = value->GetValueAsUnsigned(UINT32_MAX);
}

bool has_compressed_pair_layout = false;
// ValueObjectSP
// node_alloc(m_backend.GetChildMemberWithName("__node_alloc_")); if
// (!node_alloc)
// has_compressed_pair_layout = true;

ValueObjectSP size_node(m_backend.GetChildMemberWithName("__size_"));
if (!size_node) {
size_node = m_backend.GetChildMemberWithName(
"__size_alloc_"); // pre-compressed_pair rework
if (size_node)
has_compressed_pair_layout = true;
}
if (m_count != UINT32_MAX) {

if (size_node) {
if (has_compressed_pair_layout)
if (ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_node))
size_node = std::move(value);

m_count = size_node->GetValueAsUnsigned(UINT32_MAX);
}

if (m_count != UINT32_MAX)
return m_count;
} else {
uint64_t next_val = m_head->GetValueAsUnsigned(0);
uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
if (next_val == 0 || prev_val == 0)
return 0;
if (next_val == m_node_address)
return 0;
if (next_val == prev_val)
return 1;
uint64_t size = 2;
ListEntry current(m_head);
while (current.next() && current.next().value() != m_node_address) {
size++;
current = current.next();
if (size > m_list_capping_size)
break;
}
return m_count = (size - 1);

uint64_t next_val = m_head->GetValueAsUnsigned(0);
uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
if (next_val == 0 || prev_val == 0)
return 0;
if (next_val == m_node_address)
return 0;
if (next_val == prev_val)
return 1;
uint64_t size = 2;
ListEntry current(m_head);
while (current.next() && current.next().value() != m_node_address) {
size++;
current = current.next();
if (size > m_list_capping_size)
break;
}
return m_count = (size - 1);
}

lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(uint32_t idx) {
Expand Down
68 changes: 52 additions & 16 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
size_t GetIndexOfChildWithName(ConstString name) override;

private:
size_t CalculateNumChildrenV1();
size_t CalculateNumChildrenV2();

bool GetDataType();

void GetValueOffset(const lldb::ValueObjectSP &node);
Expand All @@ -207,6 +210,7 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
uint32_t m_skip_size = UINT32_MAX;
size_t m_count = UINT32_MAX;
std::map<size_t, MapIterator> m_iterators;
bool m_has_compressed_pair_layout = false;
};

class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
Expand Down Expand Up @@ -239,6 +243,31 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
Update();
}

size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
CalculateNumChildrenV2() {
ValueObjectSP node(m_tree->GetChildMemberWithName("__size_"));
if (!node)
return 0;

m_count = node->GetValueAsUnsigned(0);
return m_count;
}

size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
CalculateNumChildrenV1() {
ValueObjectSP node(m_tree->GetChildMemberWithName("__pair3_"));
if (!node)
return 0;

node = formatters::GetFirstValueOfLibCXXCompressedPair(*node);

if (!node)
return 0;

m_count = node->GetValueAsUnsigned(0);
return m_count;
}

llvm::Expected<uint32_t> lldb_private::formatters::
LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() {
if (m_count != UINT32_MAX)
Expand All @@ -247,17 +276,10 @@ llvm::Expected<uint32_t> lldb_private::formatters::
if (m_tree == nullptr)
return 0;

ValueObjectSP size_node(m_tree->GetChildMemberWithName("__pair3_"));
if (!size_node)
return 0;
if (m_has_compressed_pair_layout)
return CalculateNumChildrenV1();

size_node = GetFirstValueOfLibCXXCompressedPair(*size_node);

if (!size_node)
return 0;

m_count = size_node->GetValueAsUnsigned(0);
return m_count;
return CalculateNumChildrenV2();
}

bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() {
Expand All @@ -274,12 +296,22 @@ bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() {
m_element_type = deref->GetCompilerType();
return true;
}
deref = m_backend.GetChildAtNamePath({"__tree_", "__pair3_"});
if (!deref)
return false;
m_element_type = deref->GetCompilerType()
.GetTypeTemplateArgument(1)
.GetTypeTemplateArgument(1);

if (m_has_compressed_pair_layout) {
deref = m_backend.GetChildAtNamePath({"__tree_", "__pair3_"});

if (!deref)
return false;
m_element_type = deref->GetCompilerType()
.GetTypeTemplateArgument(1)
.GetTypeTemplateArgument(1);
} else {
deref = m_backend.GetChildAtNamePath({"__tree_", "__value_comp_"});
if (!deref)
return false;
m_element_type = deref->GetCompilerType().GetTypeTemplateArgument(1);
}

if (m_element_type) {
std::string name;
uint64_t bit_offset_ptr;
Expand Down Expand Up @@ -458,6 +490,10 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
m_tree = m_backend.GetChildMemberWithName("__tree_").get();
if (!m_tree)
return lldb::ChildCacheState::eRefetch;

m_has_compressed_pair_layout =
m_tree->GetChildMemberWithName("__pair1_") != nullptr;

m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get();
return lldb::ChildCacheState::eRefetch;
}
Expand Down
Loading

0 comments on commit 2b82d72

Please sign in to comment.