Skip to content

Commit a348875

Browse files
authored
[LLDB][libc++] Adds valarray proxy data formatters. (#88613)
These proxies are returned by operator[](...). These proxies all "behave" the same. They store a pointer to the data of the valarray they are a proxy for and they have an internal array of indices. This internal array is considered its contents.
1 parent f8d314f commit a348875

File tree

6 files changed

+295
-9
lines changed

6 files changed

+295
-9
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
1414
LibCxxQueue.cpp
1515
LibCxxRangesRefView.cpp
1616
LibCxxSliceArray.cpp
17+
LibCxxProxyArray.cpp
1718
LibCxxSpan.cpp
1819
LibCxxTuple.cpp
1920
LibCxxUnorderedMap.cpp

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,12 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
760760
lldb_private::formatters::LibcxxStdSliceArraySyntheticFrontEndCreator,
761761
"libc++ std::slice_array synthetic children",
762762
"^std::__[[:alnum:]]+::slice_array<.+>$", stl_deref_flags, true);
763+
AddCXXSynthetic(
764+
cpp_category_sp,
765+
lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEndCreator,
766+
"libc++ synthetic children for the valarray proxy arrays",
767+
"^std::__[[:alnum:]]+::(gslice|mask|indirect)_array<.+>$",
768+
stl_deref_flags, true);
763769
AddCXXSynthetic(
764770
cpp_category_sp,
765771
lldb_private::formatters::LibcxxStdForwardListSyntheticFrontEndCreator,
@@ -890,6 +896,11 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
890896
"libc++ std::slice_array summary provider",
891897
"^std::__[[:alnum:]]+::slice_array<.+>$", stl_summary_flags,
892898
true);
899+
AddCXXSummary(cpp_category_sp,
900+
lldb_private::formatters::LibcxxContainerSummaryProvider,
901+
"libc++ summary provider for the valarray proxy arrays",
902+
"^std::__[[:alnum:]]+::(gslice|mask|indirect)_array<.+>$",
903+
stl_summary_flags, true);
893904
AddCXXSummary(
894905
cpp_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider,
895906
"libc++ std::list summary provider",

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ SyntheticChildrenFrontEnd *
231231
LibcxxStdSliceArraySyntheticFrontEndCreator(CXXSyntheticChildren *,
232232
lldb::ValueObjectSP);
233233

234+
SyntheticChildrenFrontEnd *
235+
LibcxxStdProxyArraySyntheticFrontEndCreator(CXXSyntheticChildren *,
236+
lldb::ValueObjectSP);
237+
234238
SyntheticChildrenFrontEnd *
235239
LibcxxStdListSyntheticFrontEndCreator(CXXSyntheticChildren *,
236240
lldb::ValueObjectSP);
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
//===-- LibCxxProxyArray.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 "LibCxx.h"
10+
11+
#include "lldb/Core/ValueObject.h"
12+
#include "lldb/DataFormatters/FormattersHelpers.h"
13+
#include <optional>
14+
15+
using namespace lldb;
16+
using namespace lldb_private;
17+
using namespace lldb_private::formatters;
18+
19+
namespace lldb_private {
20+
namespace formatters {
21+
22+
/// Data formatter for libc++'s std::"proxy_array".
23+
///
24+
/// A proxy_array's are created by using:
25+
/// std::gslice_array operator[](const std::gslice& gslicearr);
26+
/// std::mask_array operator[](const std::valarray<bool>& boolarr);
27+
/// std::indirect_array operator[](const std::valarray<std::size_t>& indarr);
28+
///
29+
/// These arrays have the following members:
30+
/// - __vp_ points to std::valarray::__begin_
31+
/// - __1d_ an array of offsets of the elements from @a __vp_
32+
class LibcxxStdProxyArraySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
33+
public:
34+
LibcxxStdProxyArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
35+
36+
~LibcxxStdProxyArraySyntheticFrontEnd() override;
37+
38+
llvm::Expected<uint32_t> CalculateNumChildren() override;
39+
40+
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
41+
42+
lldb::ChildCacheState Update() override;
43+
44+
bool MightHaveChildren() override;
45+
46+
size_t GetIndexOfChildWithName(ConstString name) override;
47+
48+
private:
49+
/// A non-owning pointer to the array's __vp_.
50+
ValueObject *m_base = nullptr;
51+
/// The type of the array's template argument T.
52+
CompilerType m_element_type;
53+
/// The sizeof the array's template argument T.
54+
uint32_t m_element_size = 0;
55+
56+
/// A non-owning pointer to the array's __1d_.__begin_.
57+
ValueObject *m_start = nullptr;
58+
/// A non-owning pointer to the array's __1d_.__end_.
59+
ValueObject *m_finish = nullptr;
60+
/// The type of the __1d_ array's template argument T (size_t).
61+
CompilerType m_element_type_size_t;
62+
/// The sizeof the __1d_ array's template argument T (size_t)
63+
uint32_t m_element_size_size_t = 0;
64+
};
65+
66+
} // namespace formatters
67+
} // namespace lldb_private
68+
69+
lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::
70+
LibcxxStdProxyArraySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
71+
: SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() {
72+
if (valobj_sp)
73+
Update();
74+
}
75+
76+
lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::
77+
~LibcxxStdProxyArraySyntheticFrontEnd() {
78+
// these need to stay around because they are child objects who will follow
79+
// their parent's life cycle
80+
// delete m_base;
81+
}
82+
83+
llvm::Expected<uint32_t> lldb_private::formatters::
84+
LibcxxStdProxyArraySyntheticFrontEnd::CalculateNumChildren() {
85+
86+
if (!m_start || !m_finish)
87+
return 0;
88+
uint64_t start_val = m_start->GetValueAsUnsigned(0);
89+
uint64_t finish_val = m_finish->GetValueAsUnsigned(0);
90+
91+
if (start_val == 0 || finish_val == 0)
92+
return 0;
93+
94+
if (start_val >= finish_val)
95+
return 0;
96+
97+
size_t num_children = (finish_val - start_val);
98+
if (num_children % m_element_size_size_t)
99+
return 0;
100+
return num_children / m_element_size_size_t;
101+
}
102+
103+
lldb::ValueObjectSP
104+
lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::GetChildAtIndex(
105+
uint32_t idx) {
106+
if (!m_base)
107+
return lldb::ValueObjectSP();
108+
109+
uint64_t offset = idx * m_element_size_size_t;
110+
offset = offset + m_start->GetValueAsUnsigned(0);
111+
112+
lldb::ValueObjectSP indirect = CreateValueObjectFromAddress(
113+
"", offset, m_backend.GetExecutionContextRef(), m_element_type_size_t);
114+
if (!indirect)
115+
return lldb::ValueObjectSP();
116+
117+
const size_t value = indirect->GetValueAsUnsigned(0);
118+
if (!value)
119+
return lldb::ValueObjectSP();
120+
121+
offset = value * m_element_size;
122+
offset = offset + m_base->GetValueAsUnsigned(0);
123+
124+
StreamString name;
125+
name.Printf("[%" PRIu64 "] -> [%zu]", (uint64_t)idx, value);
126+
return CreateValueObjectFromAddress(name.GetString(), offset,
127+
m_backend.GetExecutionContextRef(),
128+
m_element_type);
129+
}
130+
131+
lldb::ChildCacheState
132+
lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::Update() {
133+
m_base = nullptr;
134+
m_start = nullptr;
135+
m_finish = nullptr;
136+
137+
CompilerType type = m_backend.GetCompilerType();
138+
if (type.GetNumTemplateArguments() == 0)
139+
return ChildCacheState::eRefetch;
140+
141+
m_element_type = type.GetTypeTemplateArgument(0);
142+
if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr))
143+
m_element_size = *size;
144+
145+
if (m_element_size == 0)
146+
return ChildCacheState::eRefetch;
147+
148+
ValueObjectSP vector = m_backend.GetChildMemberWithName("__1d_");
149+
if (!vector)
150+
return ChildCacheState::eRefetch;
151+
152+
type = vector->GetCompilerType();
153+
if (type.GetNumTemplateArguments() == 0)
154+
return ChildCacheState::eRefetch;
155+
156+
m_element_type_size_t = type.GetTypeTemplateArgument(0);
157+
if (std::optional<uint64_t> size = m_element_type_size_t.GetByteSize(nullptr))
158+
m_element_size_size_t = *size;
159+
160+
if (m_element_size_size_t == 0)
161+
return ChildCacheState::eRefetch;
162+
163+
ValueObjectSP base = m_backend.GetChildMemberWithName("__vp_");
164+
ValueObjectSP start = vector->GetChildMemberWithName("__begin_");
165+
ValueObjectSP finish = vector->GetChildMemberWithName("__end_");
166+
if (!base || !start || !finish)
167+
return ChildCacheState::eRefetch;
168+
169+
m_base = base.get();
170+
m_start = start.get();
171+
m_finish = finish.get();
172+
173+
return ChildCacheState::eRefetch;
174+
}
175+
176+
bool lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::
177+
MightHaveChildren() {
178+
return true;
179+
}
180+
181+
size_t lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEnd::
182+
GetIndexOfChildWithName(ConstString name) {
183+
if (!m_base)
184+
return std::numeric_limits<size_t>::max();
185+
return ExtractIndexFromString(name.GetCString());
186+
}
187+
188+
lldb_private::SyntheticChildrenFrontEnd *
189+
lldb_private::formatters::LibcxxStdProxyArraySyntheticFrontEndCreator(
190+
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
191+
if (!valobj_sp)
192+
return nullptr;
193+
return new LibcxxStdProxyArraySyntheticFrontEnd(valobj_sp);
194+
}

lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/valarray/TestDataFormatterLibcxxValarray.py

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,21 +89,93 @@ def test_with_run_command(self):
8989
"frame variable sa",
9090
substrs=[
9191
"sa = stride=2 size=4",
92-
"[0] = 1",
93-
"[1] = 3",
94-
"[2] = 5",
95-
"[3] = 7",
92+
"[0] = 11",
93+
"[1] = 13",
94+
"[2] = 15",
95+
"[3] = 17",
9696
"}",
9797
],
9898
)
9999

100100
# check access-by-index
101-
self.expect("frame variable sa[0]", substrs=["1"])
102-
self.expect("frame variable sa[1]", substrs=["3"])
103-
self.expect("frame variable sa[2]", substrs=["5"])
104-
self.expect("frame variable sa[3]", substrs=["7"])
101+
self.expect("frame variable sa[0]", substrs=["11"])
102+
self.expect("frame variable sa[1]", substrs=["13"])
103+
self.expect("frame variable sa[2]", substrs=["15"])
104+
self.expect("frame variable sa[3]", substrs=["17"])
105105
self.expect(
106106
"frame variable sa[4]",
107107
error=True,
108108
substrs=['array index 4 is not valid for "(slice_array<int>) sa"'],
109109
)
110+
111+
#
112+
# std::gslice_array
113+
#
114+
115+
self.expect(
116+
"frame variable ga",
117+
substrs=[
118+
"ga = size=3",
119+
"[0] -> [3] = 13",
120+
"[1] -> [4] = 14",
121+
"[2] -> [5] = 15",
122+
"}",
123+
],
124+
)
125+
126+
# check access-by-index
127+
self.expect("frame variable ga[0]", substrs=["13"])
128+
self.expect("frame variable ga[1]", substrs=["14"])
129+
self.expect("frame variable ga[2]", substrs=["15"])
130+
self.expect(
131+
"frame variable ga[3]",
132+
error=True,
133+
substrs=['array index 3 is not valid for "(gslice_array<int>) ga"'],
134+
)
135+
#
136+
# std::mask_array
137+
#
138+
139+
self.expect(
140+
"frame variable ma",
141+
substrs=[
142+
"ma = size=2",
143+
"[0] -> [1] = 11",
144+
"[1] -> [2] = 12",
145+
"}",
146+
],
147+
)
148+
149+
# check access-by-index
150+
self.expect("frame variable ma[0]", substrs=["11"])
151+
self.expect("frame variable ma[1]", substrs=["12"])
152+
self.expect(
153+
"frame variable ma[2]",
154+
error=True,
155+
substrs=['array index 2 is not valid for "(mask_array<int>) ma"'],
156+
)
157+
158+
#
159+
# std::indirect_array
160+
#
161+
162+
self.expect(
163+
"frame variable ia",
164+
substrs=[
165+
"ia = size=3",
166+
"[0] -> [3] = 13",
167+
"[1] -> [6] = 16",
168+
"[2] -> [9] = 19",
169+
"}",
170+
],
171+
)
172+
173+
# check access-by-index
174+
self.expect("frame variable ia[0]", substrs=["13"])
175+
self.expect("frame variable ia[1]", substrs=["16"])
176+
self.expect("frame variable ia[2]", substrs=["19"])
177+
self.expect(
178+
"frame variable ia[3]",
179+
error=True,
180+
substrs=['array index 3 is not valid for "(indirect_array<int>) ia"'],
181+
)

lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/valarray/main.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ int main() {
1313

1414
std::valarray<double> va_double({1.0, 0.5, 0.25, 0.125});
1515

16-
std::valarray<int> va({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
16+
std::valarray<int> va({10, 11, 12, 13, 14, 15, 16, 17, 18, 19});
1717
std::slice_array<int> sa = va[std::slice(1, 4, 2)];
18+
std::gslice_array<int> ga = va[std::gslice(
19+
3, std::valarray<std::size_t>(3, 1), std::valarray<std::size_t>(1, 1))];
20+
std::mask_array<int> ma = va[std::valarray<bool>{false, true, true}];
21+
std::indirect_array<int> ia = va[std::valarray<size_t>{3, 6, 9}];
1822

1923
std::cout << "break here\n";
2024
}

0 commit comments

Comments
 (0)