Skip to content

[lldb] Support new libc++ __compressed_pair layout #96538

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 7 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 22 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)
Comment on lines +743 to +748
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there any verification we can do on this to ensure the data in this object is not just random data because the class hasn't been initialized? We had an issue with std::initializer_list<T> being super simple and if random data was in the object, its size could be huge. Can we verify the alignment of anything like __begin_ or __end_cap_ and if anything look really off or not aligned correctly return zero as the count?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yea I remember looking into an issue like that for some other formatter. Though this patch doesn't change behaviour in this case. If we had garbage in __size_, we previously would've also read its value and treated it as the count. The only difference here is that we don't unwrap the compressed_pair anymore.

I'll open a follow-up issue for fixing this in a separate PR. In fact, the other issue might still be open somewhere. I'll have a look

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah here's one example: #69614


# give up now if we cant access memory reliably
if self.block_size < 0:
logger.write("block_size < 0")
Expand All @@ -748,9 +758,15 @@ 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
85 changes: 63 additions & 22 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,40 @@
#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 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::isOldCompressedPairLayout(
ValueObject &pair_obj) {
return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
}

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 All @@ -53,7 +80,7 @@ lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair(
if (first_child)
value = first_child->GetChildMemberWithName("__value_");
if (!value) {
// pre-r300140 member name
// pre-c88580c member name
value = pair.GetChildMemberWithName("__first_");
}
return value;
Expand All @@ -70,7 +97,7 @@ lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
}
}
if (!value) {
// pre-r300140 member name
// pre-c88580c member name
value = pair.GetChildMemberWithName("__second_");
}
return value;
Expand Down Expand Up @@ -176,7 +203,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 @@ -363,13 +392,22 @@ 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"));

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"));

ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp);
if (deleter_sp)
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
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 @@ -407,24 +445,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
3 changes: 2 additions & 1 deletion lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ GetChildMemberWithName(ValueObject &obj,

lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair);
lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair);

bool isOldCompressedPairLayout(ValueObject &pair_obj);
bool isStdTemplate(ConstString type_name, llvm::StringRef type);

bool LibcxxStringSummaryProviderASCII(
ValueObject &valobj, Stream &stream,
Expand Down
72 changes: 43 additions & 29 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,17 @@ lldb::ChildCacheState ForwardListFrontEnd::Update() {

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

if (isOldCompressedPairLayout(*impl_sp))
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;

return ChildCacheState::eRefetch;
}

ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp)
Expand All @@ -313,34 +319,42 @@ 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);
}

ValueObjectSP size_node_sp(m_backend.GetChildMemberWithName("__size_"));
if (!size_node_sp) {
size_node_sp = m_backend.GetChildMemberWithName(
"__size_alloc_"); // pre-compressed_pair rework

if (!isOldCompressedPairLayout(*size_node_sp))
return llvm::createStringError("Unexpected std::list layout: expected "
"old __compressed_pair layout.");

size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp);
}
if (m_count != UINT32_MAX) {

if (size_node_sp)
m_count = size_node_sp->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
39 changes: 29 additions & 10 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
size_t GetIndexOfChildWithName(ConstString name) override;

private:
llvm::Expected<uint32_t> CalculateNumChildrenForOldCompressedPairLayout();

/// Returns the ValueObject for the __tree_node type that
/// holds the key/value pair of the node at index \ref idx.
///
Expand Down Expand Up @@ -254,6 +256,27 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
Update();
}

llvm::Expected<uint32_t>
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
CalculateNumChildrenForOldCompressedPairLayout() {
ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_"));
if (!node_sp)
return 0;

if (!isOldCompressedPairLayout(*node_sp))
return llvm::createStringError("Unexpected std::map layout: expected "
"old __compressed_pair layout.");

node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);

if (!node_sp)
return 0;

m_count = node_sp->GetValueAsUnsigned(0);

return m_count;
}

llvm::Expected<uint32_t> lldb_private::formatters::
LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() {
if (m_count != UINT32_MAX)
Expand All @@ -262,17 +285,12 @@ 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;

size_node = GetFirstValueOfLibCXXCompressedPair(*size_node);

if (!size_node)
return 0;
if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) {
m_count = node_sp->GetValueAsUnsigned(0);
return m_count;
}

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

ValueObjectSP
Expand Down Expand Up @@ -371,6 +389,7 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
m_tree = m_backend.GetChildMemberWithName("__tree_").get();
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");
Expand Down
Loading
Loading