Skip to content

Commit e9bda49

Browse files
authored
[lldb] add libstdcpp span formatter (#168705)
1 parent e947139 commit e9bda49

File tree

5 files changed

+145
-7
lines changed

5 files changed

+145
-7
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
3131
LibCxxValarray.cpp
3232
LibCxxVector.cpp
3333
LibStdcpp.cpp
34+
LibStdcppSpan.cpp
3435
LibStdcppTuple.cpp
3536
LibStdcppUniquePointer.cpp
3637
MsvcStl.cpp

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14241424
stl_synth_flags,
14251425
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
14261426

1427+
AddCXXSynthetic(cpp_category_sp, LibStdcppSpanSyntheticFrontEndCreator,
1428+
"libstdc++ std::span synthetic children", "^std::span<.+>$",
1429+
stl_deref_flags, true);
1430+
14271431
stl_summary_flags.SetDontShowChildren(false);
14281432
stl_summary_flags.SetSkipPointers(false);
14291433

@@ -1514,6 +1518,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
15141518
lldb_private::formatters::StdlibCoroutineHandleSummaryProvider,
15151519
"libstdc++ std::coroutine_handle summary provider",
15161520
libstdcpp_std_coroutine_handle_regex, stl_summary_flags, true);
1521+
1522+
AddCXXSummary(cpp_category_sp,
1523+
lldb_private::formatters::ContainerSizeSummaryProvider,
1524+
"libstdc++ std::span summary provider", "^std::span<.+>$",
1525+
stl_summary_flags, true);
15171526
}
15181527

15191528
static lldb_private::SyntheticChildrenFrontEnd *

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ SyntheticChildrenFrontEnd *
3737
LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
3838
lldb::ValueObjectSP);
3939

40+
SyntheticChildrenFrontEnd *
41+
LibStdcppSpanSyntheticFrontEndCreator(CXXSyntheticChildren *,
42+
lldb::ValueObjectSP);
43+
4044
SyntheticChildrenFrontEnd *
4145
LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *,
4246
lldb::ValueObjectSP);
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//===---------------------------------------------------------------------===//
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 "LibStdcpp.h"
10+
11+
#include "lldb/DataFormatters/FormattersHelpers.h"
12+
#include "lldb/Utility/ConstString.h"
13+
#include "lldb/ValueObject/ValueObject.h"
14+
#include "llvm/ADT/APSInt.h"
15+
#include "llvm/Support/Error.h"
16+
#include <cstddef>
17+
#include <optional>
18+
19+
using namespace lldb;
20+
21+
namespace lldb_private::formatters {
22+
23+
class LibStdcppSpanSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
24+
public:
25+
LibStdcppSpanSyntheticFrontEnd(const lldb::ValueObjectSP &valobj_sp)
26+
: SyntheticChildrenFrontEnd(*valobj_sp) {
27+
if (valobj_sp)
28+
Update();
29+
}
30+
31+
~LibStdcppSpanSyntheticFrontEnd() override = default;
32+
33+
llvm::Expected<uint32_t> CalculateNumChildren() override {
34+
return m_num_elements;
35+
}
36+
37+
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
38+
if (!m_start)
39+
return {};
40+
41+
uint64_t offset = (static_cast<uint64_t>(idx) * m_element_size);
42+
offset += m_start->GetValueAsUnsigned(0);
43+
const std::string name = llvm::formatv("[{0}]", idx);
44+
return CreateValueObjectFromAddress(
45+
name, offset, m_backend.GetExecutionContextRef(), m_element_type);
46+
}
47+
48+
lldb::ChildCacheState Update() override {
49+
const ValueObjectSP data_ptr = m_backend.GetChildMemberWithName("_M_ptr");
50+
if (!data_ptr)
51+
return lldb::ChildCacheState::eRefetch;
52+
53+
m_element_type = data_ptr->GetCompilerType().GetPointeeType();
54+
55+
// Get element size.
56+
llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr);
57+
if (!size_or_err) {
58+
LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(),
59+
"{0}");
60+
return lldb::ChildCacheState::eReuse;
61+
}
62+
63+
m_element_size = *size_or_err;
64+
if (m_element_size > 0) {
65+
m_start = data_ptr.get();
66+
}
67+
68+
// Get number of elements.
69+
if (const ValueObjectSP size_sp =
70+
m_backend.GetChildAtNamePath({"_M_extent", "_M_extent_value"})) {
71+
m_num_elements = size_sp->GetValueAsUnsigned(0);
72+
} else if (const auto arg =
73+
m_backend.GetCompilerType().GetIntegralTemplateArgument(1)) {
74+
75+
m_num_elements = arg->value.GetAPSInt().getLimitedValue();
76+
}
77+
78+
return lldb::ChildCacheState::eReuse;
79+
}
80+
81+
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
82+
if (!m_start)
83+
return llvm::createStringError(
84+
llvm::formatv("Type has no child named {0}", name.GetStringRef()));
85+
86+
auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
87+
if (!optional_idx) {
88+
return llvm::createStringError(
89+
llvm::formatv("Type has no child named {0}", name.GetStringRef()));
90+
}
91+
return *optional_idx;
92+
}
93+
94+
private:
95+
ValueObject *m_start = nullptr; ///< First element of span. Held, not owned.
96+
CompilerType m_element_type; ///< Type of span elements.
97+
size_t m_num_elements = 0; ///< Number of elements in span.
98+
uint32_t m_element_size = 0; ///< Size in bytes of each span element.
99+
};
100+
101+
SyntheticChildrenFrontEnd *
102+
LibStdcppSpanSyntheticFrontEndCreator(CXXSyntheticChildren * /*unused*/,
103+
lldb::ValueObjectSP valobj_sp) {
104+
if (!valobj_sp)
105+
return nullptr;
106+
const CompilerType type = valobj_sp->GetCompilerType();
107+
if (!type || type.GetNumTemplateArguments() != 2)
108+
return nullptr;
109+
return new LibStdcppSpanSyntheticFrontEnd(valobj_sp);
110+
}
111+
112+
} // namespace lldb_private::formatters

lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/span/TestDataFormatterStdSpan.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def do_test(self):
7474
result_summary="item 0 is 1",
7575
)
7676

77-
self.runCmd("type summary delete span")
77+
self.runCmd("type summary clear")
7878

7979
# New span with strings
8080
lldbutil.continue_to_breakpoint(process, bkpt)
@@ -155,12 +155,6 @@ def do_test(self):
155155
)
156156
self.check_size("nested", 2)
157157

158-
@skipIf(compiler="clang", compiler_version=["<", "11.0"])
159-
@add_test_categories(["libc++"])
160-
def test_libcxx(self):
161-
self.build(dictionary={"USE_LIBCPP": 1})
162-
self.do_test()
163-
164158
def do_test_ref_and_ptr(self):
165159
"""Test that std::span is correctly formatted when passed by ref and ptr"""
166160
(self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
@@ -174,8 +168,26 @@ def do_test_ref_and_ptr(self):
174168

175169
self.expect("frame variable ptr", patterns=["ptr = 0x[0-9a-f]+ size=5"])
176170

171+
@skipIf(compiler="clang", compiler_version=["<", "11.0"])
172+
@add_test_categories(["libc++"])
173+
def test_libcxx(self):
174+
self.build(dictionary={"USE_LIBCPP": 1})
175+
self.do_test()
176+
177177
@skipIf(compiler="clang", compiler_version=["<", "11.0"])
178178
@add_test_categories(["libc++"])
179179
def test_ref_and_ptr_libcxx(self):
180180
self.build(dictionary={"USE_LIBCPP": 1})
181181
self.do_test_ref_and_ptr()
182+
183+
@skipIf(compiler="clang", compiler_version=["<", "11.0"])
184+
@add_test_categories(["libstdcxx"])
185+
def test_libstdcxx(self):
186+
self.build(dictionary={"USE_LIBSTDCPP": 1})
187+
self.do_test()
188+
189+
@skipIf(compiler="clang", compiler_version=["<", "11.0"])
190+
@add_test_categories(["libstdcxx"])
191+
def test_ref_and_ptr_libstdcxx(self):
192+
self.build(dictionary={"USE_LIBSTDCPP": 1})
193+
self.do_test_ref_and_ptr()

0 commit comments

Comments
 (0)