Skip to content

Commit 94fb856

Browse files
authored
[lldb] Add libstdcpp initializer_list formatter (#167515)
Make the existing libc++ formatter generic Add initializer_list summary provider. Add test for `libstdcpp`
1 parent 1618e49 commit 94fb856

File tree

8 files changed

+174
-137
lines changed

8 files changed

+174
-137
lines changed

lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
1414
CxxStringTypes.cpp
1515
Generic.cpp
1616
GenericBitset.cpp
17+
GenericInitializerList.cpp
1718
GenericList.cpp
1819
GenericOptional.cpp
1920
LibCxx.cpp
2021
LibCxxAtomic.cpp
21-
LibCxxInitializerList.cpp
2222
LibCxxMap.cpp
2323
LibCxxQueue.cpp
2424
LibCxxRangesRefView.cpp

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -899,11 +899,6 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
899899
"libc++ std::unordered containers synthetic children",
900900
"^std::__[[:alnum:]]+::unordered_(multi)?(map|set)<.+> >$",
901901
stl_synth_flags, true);
902-
AddCXXSynthetic(
903-
cpp_category_sp,
904-
lldb_private::formatters::LibcxxInitializerListSyntheticFrontEndCreator,
905-
"libc++ std::initializer_list synthetic children",
906-
"^std::initializer_list<.+>$", stl_synth_flags, true);
907902
AddCXXSynthetic(cpp_category_sp, LibcxxQueueFrontEndCreator,
908903
"libc++ std::queue synthetic children",
909904
"^std::__[[:alnum:]]+::queue<.+>$", stl_synth_flags, true);
@@ -1705,6 +1700,14 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
17051700
},
17061701
"MSVC STL/libstdc++ std::wstring summary provider"));
17071702

1703+
// NOTE: it is loaded as a common formatter because the libc++ version is not
1704+
// in the `__1` namespace, hence we need to dispatch based on the class
1705+
// layout.
1706+
AddCXXSynthetic(cpp_category_sp,
1707+
GenericInitializerListSyntheticFrontEndCreator,
1708+
"std::initializer_list synthetic children",
1709+
"^std::initializer_list<.+>$", stl_synth_flags, true);
1710+
17081711
stl_summary_flags.SetDontShowChildren(false);
17091712
stl_summary_flags.SetSkipPointers(false);
17101713

@@ -1748,6 +1751,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
17481751
"^std::(multi)?(map|set)<.+>(( )?&)?$", stl_synth_flags,
17491752
true);
17501753

1754+
AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
1755+
"std::initializer_list summary provider",
1756+
"^std::initializer_list<.+>$", stl_summary_flags, true);
17511757
AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
17521758
"MSVC STL/libstdc++ std::shared_ptr summary provider",
17531759
"^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true);

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ bool GenericOptionalSummaryProvider(ValueObject &valobj, Stream &stream,
2424
lldb::ValueObjectSP GetDesugaredSmartPointerValue(ValueObject &ptr,
2525
ValueObject &container);
2626

27+
SyntheticChildrenFrontEnd *
28+
GenericInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *,
29+
lldb::ValueObjectSP valobj_sp);
2730
} // namespace formatters
2831
} // namespace lldb_private
2932

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//===-- GenericInitializerList.cpp ----------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "lldb/DataFormatters/FormattersHelpers.h"
10+
#include "lldb/Utility/ConstString.h"
11+
#include "lldb/ValueObject/ValueObject.h"
12+
#include <cstddef>
13+
#include <optional>
14+
#include <type_traits>
15+
16+
using namespace lldb;
17+
using namespace lldb_private;
18+
19+
namespace generic_check {
20+
template <class T>
21+
using size_func = decltype(T::GetSizeMember(std::declval<ValueObject &>()));
22+
template <class T>
23+
using start_func = decltype(T::GetStartMember(std::declval<ValueObject &>()));
24+
namespace {
25+
template <typename...> struct check_func : std::true_type {};
26+
} // namespace
27+
28+
template <typename T>
29+
using has_functions = check_func<size_func<T>, start_func<T>>;
30+
} // namespace generic_check
31+
32+
struct LibCxx {
33+
static ValueObjectSP GetStartMember(ValueObject &backend) {
34+
return backend.GetChildMemberWithName("__begin_");
35+
}
36+
37+
static ValueObjectSP GetSizeMember(ValueObject &backend) {
38+
return backend.GetChildMemberWithName("__size_");
39+
}
40+
};
41+
42+
struct LibStdcpp {
43+
static ValueObjectSP GetStartMember(ValueObject &backend) {
44+
return backend.GetChildMemberWithName("_M_array");
45+
}
46+
47+
static ValueObjectSP GetSizeMember(ValueObject &backend) {
48+
return backend.GetChildMemberWithName("_M_len");
49+
}
50+
};
51+
52+
namespace lldb_private::formatters {
53+
54+
template <class StandardImpl>
55+
class GenericInitializerListSyntheticFrontEnd
56+
: public SyntheticChildrenFrontEnd {
57+
public:
58+
static_assert(generic_check::has_functions<StandardImpl>::value,
59+
"Missing Required Functions.");
60+
61+
GenericInitializerListSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
62+
: SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() {
63+
if (valobj_sp)
64+
Update();
65+
}
66+
67+
~GenericInitializerListSyntheticFrontEnd() override {
68+
// this needs to stay around because it's a child object who will follow its
69+
// parent's life cycle
70+
// delete m_start;
71+
}
72+
73+
llvm::Expected<uint32_t> CalculateNumChildren() override {
74+
m_num_elements = 0;
75+
76+
const ValueObjectSP size_sp(StandardImpl::GetSizeMember(m_backend));
77+
if (size_sp)
78+
m_num_elements = size_sp->GetValueAsUnsigned(0);
79+
return m_num_elements;
80+
}
81+
82+
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
83+
if (!m_start)
84+
return {};
85+
86+
uint64_t offset = static_cast<uint64_t>(idx) * m_element_size;
87+
offset = offset + m_start->GetValueAsUnsigned(0);
88+
StreamString name;
89+
name.Printf("[%" PRIu64 "]", (uint64_t)idx);
90+
return CreateValueObjectFromAddress(name.GetString(), offset,
91+
m_backend.GetExecutionContextRef(),
92+
m_element_type);
93+
}
94+
95+
lldb::ChildCacheState Update() override {
96+
m_start = nullptr;
97+
m_num_elements = 0;
98+
m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0);
99+
if (!m_element_type.IsValid())
100+
return lldb::ChildCacheState::eRefetch;
101+
102+
llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr);
103+
if (!size_or_err)
104+
LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(),
105+
"{0}");
106+
else {
107+
m_element_size = *size_or_err;
108+
// Store raw pointers or end up with a circular dependency.
109+
m_start = StandardImpl::GetStartMember(m_backend).get();
110+
}
111+
112+
return lldb::ChildCacheState::eRefetch;
113+
}
114+
115+
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
116+
if (!m_start) {
117+
return llvm::createStringError("Type has no child named '%s'",
118+
name.AsCString());
119+
}
120+
auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
121+
if (!optional_idx) {
122+
return llvm::createStringError("Type has no child named '%s'",
123+
name.AsCString());
124+
}
125+
return *optional_idx;
126+
}
127+
128+
private:
129+
ValueObject *m_start = nullptr;
130+
CompilerType m_element_type;
131+
uint32_t m_element_size = 0;
132+
size_t m_num_elements = 0;
133+
};
134+
135+
SyntheticChildrenFrontEnd *GenericInitializerListSyntheticFrontEndCreator(
136+
CXXSyntheticChildren * /*unused*/, lldb::ValueObjectSP valobj_sp) {
137+
if (!valobj_sp)
138+
return nullptr;
139+
140+
if (LibCxx::GetStartMember(*valobj_sp) != nullptr)
141+
return new GenericInitializerListSyntheticFrontEnd<LibCxx>(valobj_sp);
142+
143+
return new GenericInitializerListSyntheticFrontEnd<LibStdcpp>(valobj_sp);
144+
}
145+
} // namespace lldb_private::formatters

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,6 @@ SyntheticChildrenFrontEnd *
194194
LibCxxUnorderedMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
195195
lldb::ValueObjectSP);
196196

197-
SyntheticChildrenFrontEnd *
198-
LibcxxInitializerListSyntheticFrontEndCreator(CXXSyntheticChildren *,
199-
lldb::ValueObjectSP);
200-
201197
SyntheticChildrenFrontEnd *LibcxxQueueFrontEndCreator(CXXSyntheticChildren *,
202198
lldb::ValueObjectSP);
203199

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

Lines changed: 0 additions & 124 deletions
This file was deleted.

lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/TestDataFormatterStdInitializerList.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,25 @@ def do_test(self):
2828
substrs=["stopped", "stop reason = breakpoint"],
2929
)
3030

31-
self.expect("frame variable ili", substrs=["[1] = 2", "[4] = 5"])
31+
self.expect(
32+
"frame variable ili",
33+
substrs=["ili = size=5", "[0] = 1", "[1] = 2", "[4] = 5"],
34+
)
3235
self.expect(
3336
"frame variable ils",
34-
substrs=['[4] = "surprise it is a long string!! yay!!"'],
37+
substrs=[
38+
"ils = size=5",
39+
'[0] = "1"',
40+
'[4] = "surprise it is a long string!! yay!!"',
41+
],
3542
)
3643

3744
@add_test_categories(["libc++"])
3845
def test_libcxx(self):
3946
self.build(dictionary={"USE_LIBCPP": 1})
4047
self.do_test()
48+
49+
@add_test_categories(["libstdcxx"])
50+
def test_libstdcpp(self):
51+
self.build(dictionary={"USE_LIBSTDCPP": 1})
52+
self.do_test()

lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/initializer_list/main.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <initializer_list>
22
#include <string>
3-
#include <vector>
43

54
int main() {
65
std::initializer_list<int> ili{1, 2, 3, 4, 5};

0 commit comments

Comments
 (0)