Skip to content

Commit 2b82d72

Browse files
committed
[lldb] Support new libc++ __compressed_pair layout
1 parent f4c7811 commit 2b82d72

File tree

10 files changed

+253
-131
lines changed

10 files changed

+253
-131
lines changed

lldb/examples/synthetic/libcxx.py

Lines changed: 20 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,13 @@ 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("__end_cap_").GetValueAsUnsigned(0)
754768

755769
# check consistency
756770
if not map_first <= map_begin <= map_end <= map_endcap:

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

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,39 @@
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 bool isOldCompressedPairLayout(ValueObject &pair_obj) {
39+
return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
40+
}
41+
42+
static void consumeInlineNamespace(llvm::StringRef &name) {
43+
// Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
44+
auto scratch = name;
45+
if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
46+
scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
47+
if (scratch.consume_front("::")) {
48+
// Successfully consumed a namespace.
49+
name = scratch;
50+
}
51+
}
52+
}
53+
54+
bool lldb_private::formatters::isStdTemplate(ConstString type_name, llvm::StringRef type) {
55+
llvm::StringRef name = type_name.GetStringRef();
56+
// The type name may be prefixed with `std::__<inline-namespace>::`.
57+
if (name.consume_front("std::"))
58+
consumeInlineNamespace(name);
59+
return name.consume_front(type) && name.starts_with("<");
60+
}
61+
62+
3763
lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName(
3864
ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) {
3965
for (ConstString name : alternative_names) {
@@ -176,7 +202,9 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
176202
if (!ptr_sp)
177203
return false;
178204

179-
ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
205+
if (isOldCompressedPairLayout(*ptr_sp))
206+
ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
207+
180208
if (!ptr_sp)
181209
return false;
182210

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

513541
// Retrieve the actual pointer and the deleter, and clone them to give them
514542
// user-friendly names.
515-
ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
516-
if (value_pointer_sp)
517-
m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
543+
if (isOldCompressedPairLayout(*ptr_sp)) {
544+
if (ValueObjectSP value_pointer_sp =
545+
GetFirstValueOfLibCXXCompressedPair(*ptr_sp))
546+
m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
518547

519-
ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp);
520-
if (deleter_sp)
521-
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
548+
if (ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp))
549+
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
550+
} else {
551+
m_value_ptr_sp = ptr_sp->Clone(ConstString("pointer"));
552+
553+
if (ValueObjectSP deleter_sp = valobj_sp->GetChildMemberWithName("__deleter_"))
554+
if (deleter_sp->GetNumChildrenIgnoringErrors() > 0)
555+
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
556+
}
522557

523558
return lldb::ChildCacheState::eRefetch;
524559
}
@@ -556,24 +591,27 @@ namespace {
556591
enum class StringLayout { CSD, DSC };
557592
}
558593

594+
static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) {
595+
if (auto rep_sp = valobj.GetChildMemberWithName("__rep_"))
596+
return rep_sp;
597+
598+
ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
599+
if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
600+
return nullptr;
601+
602+
if (!isOldCompressedPairLayout(*valobj_r_sp))
603+
return nullptr;
604+
605+
return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp);
606+
}
607+
559608
/// Determine the size in bytes of \p valobj (a libc++ std::string object) and
560609
/// extract its data payload. Return the size + payload pair.
561610
// TODO: Support big-endian architectures.
562611
static std::optional<std::pair<uint64_t, ValueObjectSP>>
563612
ExtractLibcxxStringInfo(ValueObject &valobj) {
564-
ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
565-
if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
566-
return {};
567-
568-
// __r_ is a compressed_pair of the actual data and the allocator. The data we
569-
// want is in the first base class.
570-
ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0);
571-
if (!valobj_r_base_sp)
572-
return {};
573-
574-
ValueObjectSP valobj_rep_sp =
575-
valobj_r_base_sp->GetChildMemberWithName("__value_");
576-
if (!valobj_rep_sp)
613+
ValueObjectSP valobj_rep_sp = ExtractLibCxxStringData(valobj);
614+
if (!valobj_rep_sp || !valobj_rep_sp->GetError().Success())
577615
return {};
578616

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

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ GetChildMemberWithName(ValueObject &obj,
2626
lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair);
2727
lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair);
2828

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: 53 additions & 30 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,19 @@ 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);
299-
if (!impl_sp)
300-
return lldb::ChildCacheState::eRefetch;
298+
return ChildCacheState::eRefetch;
299+
301300
m_head = impl_sp->GetChildMemberWithName("__next_").get();
302-
return lldb::ChildCacheState::eRefetch;
301+
302+
// TODO: we have to do this in this order because __before_begin_ has a
303+
// __value_ member, as does compressed_pair. Is this a problem elsewhere too?
304+
if (!m_head) {
305+
impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
306+
if (impl_sp)
307+
m_head = impl_sp->GetChildMemberWithName("__next_").get();
308+
}
309+
310+
return ChildCacheState::eRefetch;
303311
}
304312

305313
ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp)
@@ -313,34 +321,49 @@ llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() {
313321
return m_count;
314322
if (!m_head || !m_tail || m_node_address == 0)
315323
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-
}
324+
325+
bool has_compressed_pair_layout = false;
326+
// ValueObjectSP
327+
// node_alloc(m_backend.GetChildMemberWithName("__node_alloc_")); if
328+
// (!node_alloc)
329+
// has_compressed_pair_layout = true;
330+
331+
ValueObjectSP size_node(m_backend.GetChildMemberWithName("__size_"));
332+
if (!size_node) {
333+
size_node = m_backend.GetChildMemberWithName(
334+
"__size_alloc_"); // pre-compressed_pair rework
335+
if (size_node)
336+
has_compressed_pair_layout = true;
322337
}
323-
if (m_count != UINT32_MAX) {
338+
339+
if (size_node) {
340+
if (has_compressed_pair_layout)
341+
if (ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_node))
342+
size_node = std::move(value);
343+
344+
m_count = size_node->GetValueAsUnsigned(UINT32_MAX);
345+
}
346+
347+
if (m_count != UINT32_MAX)
324348
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);
349+
350+
uint64_t next_val = m_head->GetValueAsUnsigned(0);
351+
uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
352+
if (next_val == 0 || prev_val == 0)
353+
return 0;
354+
if (next_val == m_node_address)
355+
return 0;
356+
if (next_val == prev_val)
357+
return 1;
358+
uint64_t size = 2;
359+
ListEntry current(m_head);
360+
while (current.next() && current.next().value() != m_node_address) {
361+
size++;
362+
current = current.next();
363+
if (size > m_list_capping_size)
364+
break;
343365
}
366+
return m_count = (size - 1);
344367
}
345368

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

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

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
181181
size_t GetIndexOfChildWithName(ConstString name) override;
182182

183183
private:
184+
size_t CalculateNumChildrenV1();
185+
size_t CalculateNumChildrenV2();
186+
184187
bool GetDataType();
185188

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

212216
class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
@@ -239,6 +243,31 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
239243
Update();
240244
}
241245

246+
size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
247+
CalculateNumChildrenV2() {
248+
ValueObjectSP node(m_tree->GetChildMemberWithName("__size_"));
249+
if (!node)
250+
return 0;
251+
252+
m_count = node->GetValueAsUnsigned(0);
253+
return m_count;
254+
}
255+
256+
size_t lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
257+
CalculateNumChildrenV1() {
258+
ValueObjectSP node(m_tree->GetChildMemberWithName("__pair3_"));
259+
if (!node)
260+
return 0;
261+
262+
node = formatters::GetFirstValueOfLibCXXCompressedPair(*node);
263+
264+
if (!node)
265+
return 0;
266+
267+
m_count = node->GetValueAsUnsigned(0);
268+
return m_count;
269+
}
270+
242271
llvm::Expected<uint32_t> lldb_private::formatters::
243272
LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() {
244273
if (m_count != UINT32_MAX)
@@ -247,17 +276,10 @@ llvm::Expected<uint32_t> lldb_private::formatters::
247276
if (m_tree == nullptr)
248277
return 0;
249278

250-
ValueObjectSP size_node(m_tree->GetChildMemberWithName("__pair3_"));
251-
if (!size_node)
252-
return 0;
279+
if (m_has_compressed_pair_layout)
280+
return CalculateNumChildrenV1();
253281

254-
size_node = GetFirstValueOfLibCXXCompressedPair(*size_node);
255-
256-
if (!size_node)
257-
return 0;
258-
259-
m_count = size_node->GetValueAsUnsigned(0);
260-
return m_count;
282+
return CalculateNumChildrenV2();
261283
}
262284

263285
bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() {
@@ -274,12 +296,22 @@ bool lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() {
274296
m_element_type = deref->GetCompilerType();
275297
return true;
276298
}
277-
deref = m_backend.GetChildAtNamePath({"__tree_", "__pair3_"});
278-
if (!deref)
279-
return false;
280-
m_element_type = deref->GetCompilerType()
281-
.GetTypeTemplateArgument(1)
282-
.GetTypeTemplateArgument(1);
299+
300+
if (m_has_compressed_pair_layout) {
301+
deref = m_backend.GetChildAtNamePath({"__tree_", "__pair3_"});
302+
303+
if (!deref)
304+
return false;
305+
m_element_type = deref->GetCompilerType()
306+
.GetTypeTemplateArgument(1)
307+
.GetTypeTemplateArgument(1);
308+
} else {
309+
deref = m_backend.GetChildAtNamePath({"__tree_", "__value_comp_"});
310+
if (!deref)
311+
return false;
312+
m_element_type = deref->GetCompilerType().GetTypeTemplateArgument(1);
313+
}
314+
283315
if (m_element_type) {
284316
std::string name;
285317
uint64_t bit_offset_ptr;
@@ -458,6 +490,10 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
458490
m_tree = m_backend.GetChildMemberWithName("__tree_").get();
459491
if (!m_tree)
460492
return lldb::ChildCacheState::eRefetch;
493+
494+
m_has_compressed_pair_layout =
495+
m_tree->GetChildMemberWithName("__pair1_") != nullptr;
496+
461497
m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get();
462498
return lldb::ChildCacheState::eRefetch;
463499
}

0 commit comments

Comments
 (0)