Skip to content

Commit 9e9b117

Browse files
authored
[lldb] Support new libc++ __compressed_pair layout (#96538)
This patch is in preparation for the `__compressed_pair` refactor in #76756. This is mostly reviewable now. With the new layout we no longer need to unwrap the `__compressed_pair`. Instead, we just need to look for child members. E.g., to get to the underlying pointer of `std::unique_ptr` we no longer do, ``` GetFirstValueOfCXXCompressedPair(GetChildMemberWithName("__ptr_")) ``` but instead do ``` GetChildMemberWithName("__ptr_") ``` We need to be slightly careful because previously the `__compressed_pair` had a member called `__value_`, whereas now `__value_` might be a member of the class that used to hold the `__compressed_pair`. So before unwrapping the pair, we added checks for `isOldCompressedLayout` (not sure yet whether folding this check into `GetFirstValueOfCXXCompressedPair` is better).
1 parent 8d89f83 commit 9e9b117

File tree

8 files changed

+282
-153
lines changed

8 files changed

+282
-153
lines changed

lldb/examples/synthetic/libcxx.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,12 @@ def _get_value_of_compressed_pair(self, pair):
721721
def update(self):
722722
logger = lldb.formatters.Logger.Logger()
723723
try:
724+
has_compressed_pair_layout = True
725+
alloc_valobj = self.valobj.GetChildMemberWithName("__alloc_")
726+
size_valobj = self.valobj.GetChildMemberWithName("__size_")
727+
if alloc_valobj.IsValid() and size_valobj.IsValid():
728+
has_compressed_pair_layout = False
729+
724730
# A deque is effectively a two-dim array, with fixed width.
725731
# 'map' contains pointers to the rows of this array. The
726732
# full memory area allocated by the deque is delimited
@@ -734,9 +740,13 @@ def update(self):
734740
# variable tells which element in this NxM array is the 0th
735741
# one, and the 'size' element gives the number of elements
736742
# in the deque.
737-
count = self._get_value_of_compressed_pair(
738-
self.valobj.GetChildMemberWithName("__size_")
739-
)
743+
if has_compressed_pair_layout:
744+
count = self._get_value_of_compressed_pair(
745+
self.valobj.GetChildMemberWithName("__size_")
746+
)
747+
else:
748+
count = size_valobj.GetValueAsUnsigned(0)
749+
740750
# give up now if we cant access memory reliably
741751
if self.block_size < 0:
742752
logger.write("block_size < 0")
@@ -748,9 +758,15 @@ def update(self):
748758
self.map_begin = map_.GetChildMemberWithName("__begin_")
749759
map_begin = self.map_begin.GetValueAsUnsigned(0)
750760
map_end = map_.GetChildMemberWithName("__end_").GetValueAsUnsigned(0)
751-
map_endcap = self._get_value_of_compressed_pair(
752-
map_.GetChildMemberWithName("__end_cap_")
753-
)
761+
762+
if has_compressed_pair_layout:
763+
map_endcap = self._get_value_of_compressed_pair(
764+
map_.GetChildMemberWithName("__end_cap_")
765+
)
766+
else:
767+
map_endcap = map_.GetChildMemberWithName(
768+
"__end_cap_"
769+
).GetValueAsUnsigned(0)
754770

755771
# check consistency
756772
if not map_first <= map_begin <= map_end <= map_endcap:

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

Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,40 @@
2727
#include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
2828
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
2929
#include "lldb/lldb-enumerations.h"
30+
#include "lldb/lldb-forward.h"
3031
#include <optional>
3132
#include <tuple>
3233

3334
using namespace lldb;
3435
using namespace lldb_private;
3536
using namespace lldb_private::formatters;
3637

38+
static void consumeInlineNamespace(llvm::StringRef &name) {
39+
// Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
40+
auto scratch = name;
41+
if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
42+
scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
43+
if (scratch.consume_front("::")) {
44+
// Successfully consumed a namespace.
45+
name = scratch;
46+
}
47+
}
48+
}
49+
50+
bool lldb_private::formatters::isOldCompressedPairLayout(
51+
ValueObject &pair_obj) {
52+
return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
53+
}
54+
55+
bool lldb_private::formatters::isStdTemplate(ConstString type_name,
56+
llvm::StringRef type) {
57+
llvm::StringRef name = type_name.GetStringRef();
58+
// The type name may be prefixed with `std::__<inline-namespace>::`.
59+
if (name.consume_front("std::"))
60+
consumeInlineNamespace(name);
61+
return name.consume_front(type) && name.starts_with("<");
62+
}
63+
3764
lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName(
3865
ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) {
3966
for (ConstString name : alternative_names) {
@@ -53,7 +80,7 @@ lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair(
5380
if (first_child)
5481
value = first_child->GetChildMemberWithName("__value_");
5582
if (!value) {
56-
// pre-r300140 member name
83+
// pre-c88580c member name
5784
value = pair.GetChildMemberWithName("__first_");
5885
}
5986
return value;
@@ -70,7 +97,7 @@ lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
7097
}
7198
}
7299
if (!value) {
73-
// pre-r300140 member name
100+
// pre-c88580c member name
74101
value = pair.GetChildMemberWithName("__second_");
75102
}
76103
return value;
@@ -176,7 +203,9 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
176203
if (!ptr_sp)
177204
return false;
178205

179-
ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
206+
if (isOldCompressedPairLayout(*ptr_sp))
207+
ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
208+
180209
if (!ptr_sp)
181210
return false;
182211

@@ -363,13 +392,22 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
363392

364393
// Retrieve the actual pointer and the deleter, and clone them to give them
365394
// user-friendly names.
366-
ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
367-
if (value_pointer_sp)
368-
m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
395+
if (isOldCompressedPairLayout(*ptr_sp)) {
396+
if (ValueObjectSP value_pointer_sp =
397+
GetFirstValueOfLibCXXCompressedPair(*ptr_sp))
398+
m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
399+
400+
if (ValueObjectSP deleter_sp =
401+
GetSecondValueOfLibCXXCompressedPair(*ptr_sp))
402+
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
403+
} else {
404+
m_value_ptr_sp = ptr_sp->Clone(ConstString("pointer"));
369405

370-
ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp);
371-
if (deleter_sp)
372-
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
406+
if (ValueObjectSP deleter_sp =
407+
valobj_sp->GetChildMemberWithName("__deleter_"))
408+
if (deleter_sp->GetNumChildrenIgnoringErrors() > 0)
409+
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
410+
}
373411

374412
return lldb::ChildCacheState::eRefetch;
375413
}
@@ -407,24 +445,27 @@ namespace {
407445
enum class StringLayout { CSD, DSC };
408446
}
409447

448+
static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) {
449+
if (auto rep_sp = valobj.GetChildMemberWithName("__rep_"))
450+
return rep_sp;
451+
452+
ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
453+
if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
454+
return nullptr;
455+
456+
if (!isOldCompressedPairLayout(*valobj_r_sp))
457+
return nullptr;
458+
459+
return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp);
460+
}
461+
410462
/// Determine the size in bytes of \p valobj (a libc++ std::string object) and
411463
/// extract its data payload. Return the size + payload pair.
412464
// TODO: Support big-endian architectures.
413465
static std::optional<std::pair<uint64_t, ValueObjectSP>>
414466
ExtractLibcxxStringInfo(ValueObject &valobj) {
415-
ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
416-
if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
417-
return {};
418-
419-
// __r_ is a compressed_pair of the actual data and the allocator. The data we
420-
// want is in the first base class.
421-
ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0);
422-
if (!valobj_r_base_sp)
423-
return {};
424-
425-
ValueObjectSP valobj_rep_sp =
426-
valobj_r_base_sp->GetChildMemberWithName("__value_");
427-
if (!valobj_rep_sp)
467+
ValueObjectSP valobj_rep_sp = ExtractLibCxxStringData(valobj);
468+
if (!valobj_rep_sp || !valobj_rep_sp->GetError().Success())
428469
return {};
429470

430471
ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l");

lldb/source/Plugins/Language/CPlusPlus/LibCxx.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ GetChildMemberWithName(ValueObject &obj,
2525

2626
lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair);
2727
lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair);
28-
28+
bool isOldCompressedPairLayout(ValueObject &pair_obj);
29+
bool isStdTemplate(ConstString type_name, llvm::StringRef type);
2930

3031
bool LibcxxStringSummaryProviderASCII(
3132
ValueObject &valobj, Stream &stream,

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

Lines changed: 43 additions & 29 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

2122
using namespace lldb;
2223
using namespace lldb_private;
@@ -294,12 +295,17 @@ lldb::ChildCacheState ForwardListFrontEnd::Update() {
294295

295296
ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_"));
296297
if (!impl_sp)
297-
return lldb::ChildCacheState::eRefetch;
298-
impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
298+
return ChildCacheState::eRefetch;
299+
300+
if (isOldCompressedPairLayout(*impl_sp))
301+
impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
302+
299303
if (!impl_sp)
300-
return lldb::ChildCacheState::eRefetch;
304+
return ChildCacheState::eRefetch;
305+
301306
m_head = impl_sp->GetChildMemberWithName("__next_").get();
302-
return lldb::ChildCacheState::eRefetch;
307+
308+
return ChildCacheState::eRefetch;
303309
}
304310

305311
ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp)
@@ -313,34 +319,42 @@ llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() {
313319
return m_count;
314320
if (!m_head || !m_tail || m_node_address == 0)
315321
return 0;
316-
ValueObjectSP size_alloc(m_backend.GetChildMemberWithName("__size_alloc_"));
317-
if (size_alloc) {
318-
ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_alloc);
319-
if (value) {
320-
m_count = value->GetValueAsUnsigned(UINT32_MAX);
321-
}
322+
323+
ValueObjectSP size_node_sp(m_backend.GetChildMemberWithName("__size_"));
324+
if (!size_node_sp) {
325+
size_node_sp = m_backend.GetChildMemberWithName(
326+
"__size_alloc_"); // pre-compressed_pair rework
327+
328+
if (!isOldCompressedPairLayout(*size_node_sp))
329+
return llvm::createStringError("Unexpected std::list layout: expected "
330+
"old __compressed_pair layout.");
331+
332+
size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp);
322333
}
323-
if (m_count != UINT32_MAX) {
334+
335+
if (size_node_sp)
336+
m_count = size_node_sp->GetValueAsUnsigned(UINT32_MAX);
337+
338+
if (m_count != UINT32_MAX)
324339
return m_count;
325-
} else {
326-
uint64_t next_val = m_head->GetValueAsUnsigned(0);
327-
uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
328-
if (next_val == 0 || prev_val == 0)
329-
return 0;
330-
if (next_val == m_node_address)
331-
return 0;
332-
if (next_val == prev_val)
333-
return 1;
334-
uint64_t size = 2;
335-
ListEntry current(m_head);
336-
while (current.next() && current.next().value() != m_node_address) {
337-
size++;
338-
current = current.next();
339-
if (size > m_list_capping_size)
340-
break;
341-
}
342-
return m_count = (size - 1);
340+
341+
uint64_t next_val = m_head->GetValueAsUnsigned(0);
342+
uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
343+
if (next_val == 0 || prev_val == 0)
344+
return 0;
345+
if (next_val == m_node_address)
346+
return 0;
347+
if (next_val == prev_val)
348+
return 1;
349+
uint64_t size = 2;
350+
ListEntry current(m_head);
351+
while (current.next() && current.next().value() != m_node_address) {
352+
size++;
353+
current = current.next();
354+
if (size > m_list_capping_size)
355+
break;
343356
}
357+
return m_count = (size - 1);
344358
}
345359

346360
lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(uint32_t idx) {

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

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
202202
size_t GetIndexOfChildWithName(ConstString name) override;
203203

204204
private:
205+
llvm::Expected<uint32_t> CalculateNumChildrenForOldCompressedPairLayout();
206+
205207
/// Returns the ValueObject for the __tree_node type that
206208
/// holds the key/value pair of the node at index \ref idx.
207209
///
@@ -254,6 +256,27 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
254256
Update();
255257
}
256258

259+
llvm::Expected<uint32_t>
260+
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
261+
CalculateNumChildrenForOldCompressedPairLayout() {
262+
ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_"));
263+
if (!node_sp)
264+
return 0;
265+
266+
if (!isOldCompressedPairLayout(*node_sp))
267+
return llvm::createStringError("Unexpected std::map layout: expected "
268+
"old __compressed_pair layout.");
269+
270+
node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
271+
272+
if (!node_sp)
273+
return 0;
274+
275+
m_count = node_sp->GetValueAsUnsigned(0);
276+
277+
return m_count;
278+
}
279+
257280
llvm::Expected<uint32_t> lldb_private::formatters::
258281
LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() {
259282
if (m_count != UINT32_MAX)
@@ -262,17 +285,12 @@ llvm::Expected<uint32_t> lldb_private::formatters::
262285
if (m_tree == nullptr)
263286
return 0;
264287

265-
ValueObjectSP size_node(m_tree->GetChildMemberWithName("__pair3_"));
266-
if (!size_node)
267-
return 0;
268-
269-
size_node = GetFirstValueOfLibCXXCompressedPair(*size_node);
270-
271-
if (!size_node)
272-
return 0;
288+
if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) {
289+
m_count = node_sp->GetValueAsUnsigned(0);
290+
return m_count;
291+
}
273292

274-
m_count = size_node->GetValueAsUnsigned(0);
275-
return m_count;
293+
return CalculateNumChildrenForOldCompressedPairLayout();
276294
}
277295

278296
ValueObjectSP
@@ -371,6 +389,7 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
371389
m_tree = m_backend.GetChildMemberWithName("__tree_").get();
372390
if (!m_tree)
373391
return lldb::ChildCacheState::eRefetch;
392+
374393
m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get();
375394
m_node_ptr_type =
376395
m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer");

0 commit comments

Comments
 (0)