Skip to content

Commit 5ae4939

Browse files
authored
[LLDB] Add formatters for MSVC STL std::vector (llvm#147538)
This adds synthetic child providers for `std::vector<T>` and `std::vector<bool>` for MSVC's STL. The structure of a `std::vector<T>` is relatively similar to libc++'s implementation that uses `__begin` and `__end`. `std::vector<bool>` is different. It's a `std::vector<unsigned int>` wrapper instead of `std::vector<uint8_t>`. This makes the calculation slightly less simple. I put a comment in the `GetChildAtIndex` to make this clear. - [NatVis for `std::vector<T>`](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/debugger/STL.natvis#L1193-L1205) - [NatVis for `std::vector<bool>`](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/debugger/STL.natvis#L1167-L1179) Towards llvm#24834.
1 parent 905bb5b commit 5ae4939

File tree

7 files changed

+407
-8
lines changed

7 files changed

+407
-8
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
3636
MsvcStl.cpp
3737
MsvcStlSmartPointer.cpp
3838
MsvcStlTuple.cpp
39+
MsvcStlVector.cpp
3940
MSVCUndecoratedNameParser.cpp
4041

4142
LINK_COMPONENTS

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14041404
stl_deref_flags.SetFrontEndWantsDereference();
14051405

14061406
cpp_category_sp->AddTypeSynthetic(
1407-
"^std::(__debug::)?vector<.+>(( )?&)?$", eFormatterMatchRegex,
1407+
"^std::__debug::vector<.+>(( )?&)?$", eFormatterMatchRegex,
14081408
SyntheticChildrenSP(new ScriptedSyntheticChildren(
14091409
stl_synth_flags,
14101410
"lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider")));
@@ -1465,10 +1465,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14651465
"libstdc++ std::bitset summary provider",
14661466
"^std::(__debug::)?bitset<.+>(( )?&)?$", stl_summary_flags, true);
14671467

1468-
AddCXXSummary(
1469-
cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
1470-
"libstdc++ std::vector summary provider",
1471-
"^std::(__debug::)?vector<.+>(( )?&)?$", stl_summary_flags, true);
1468+
AddCXXSummary(cpp_category_sp,
1469+
lldb_private::formatters::ContainerSizeSummaryProvider,
1470+
"libstdc++ std::__debug::vector summary provider",
1471+
"^std::__debug::vector<.+>(( )?&)?$", stl_summary_flags, true);
14721472

14731473
AddCXXSummary(
14741474
cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1615,6 +1615,20 @@ GenericTupleSyntheticFrontEndCreator(CXXSyntheticChildren *children,
16151615
return LibStdcppTupleSyntheticFrontEndCreator(children, valobj_sp);
16161616
}
16171617

1618+
static SyntheticChildrenFrontEnd *
1619+
GenericVectorSyntheticFrontEndCreator(CXXSyntheticChildren *children,
1620+
lldb::ValueObjectSP valobj_sp) {
1621+
if (!valobj_sp)
1622+
return nullptr;
1623+
1624+
// checks for vector<T> and vector<bool>
1625+
if (auto *msvc = MsvcStlVectorSyntheticFrontEndCreator(valobj_sp))
1626+
return msvc;
1627+
1628+
return new ScriptedSyntheticChildren::FrontEnd(
1629+
"lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider", *valobj_sp);
1630+
}
1631+
16181632
/// Load formatters that are formatting types from more than one STL
16191633
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16201634
if (!cpp_category_sp)
@@ -1686,6 +1700,12 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16861700
AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
16871701
"MSVC STL/libstdc++ std::tuple summary provider",
16881702
"^std::tuple<.*>(( )?&)?$", stl_summary_flags, true);
1703+
AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
1704+
"MSVC/libstdc++ std::vector summary provider",
1705+
"^std::vector<.+>(( )?&)?$", stl_summary_flags, true);
1706+
AddCXXSynthetic(cpp_category_sp, GenericVectorSyntheticFrontEndCreator,
1707+
"MSVC/libstdc++ std::vector synthetic provider",
1708+
"^std::vector<.+>(( )?&)?$", stl_synth_flags, true);
16891709
}
16901710

16911711
static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ SyntheticChildrenFrontEnd *
5151
MsvcStlTupleSyntheticFrontEndCreator(CXXSyntheticChildren *,
5252
lldb::ValueObjectSP valobj_sp);
5353

54+
// MSVC STL std::vector<>
55+
bool IsMsvcStlVector(ValueObject &valobj);
56+
lldb_private::SyntheticChildrenFrontEnd *
57+
MsvcStlVectorSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
58+
5459
} // namespace formatters
5560
} // namespace lldb_private
5661

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
//===-- MsvcStlVector.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 "MsvcStl.h"
10+
11+
#include "lldb/DataFormatters/FormattersHelpers.h"
12+
#include "lldb/DataFormatters/TypeSynthetic.h"
13+
14+
using namespace lldb;
15+
16+
namespace lldb_private {
17+
namespace formatters {
18+
19+
class MsvcStlVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
20+
public:
21+
MsvcStlVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
22+
23+
llvm::Expected<uint32_t> CalculateNumChildren() override;
24+
25+
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
26+
27+
lldb::ChildCacheState Update() override;
28+
29+
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
30+
31+
private:
32+
ValueObject *m_start = nullptr;
33+
ValueObject *m_finish = nullptr;
34+
CompilerType m_element_type;
35+
uint32_t m_element_size = 0;
36+
};
37+
38+
class MsvcStlVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
39+
public:
40+
MsvcStlVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
41+
42+
llvm::Expected<uint32_t> CalculateNumChildren() override;
43+
44+
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
45+
46+
lldb::ChildCacheState Update() override;
47+
48+
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
49+
50+
private:
51+
CompilerType m_bool_type;
52+
ExecutionContextRef m_exe_ctx_ref;
53+
uint64_t m_count = 0;
54+
uint64_t m_element_bit_size = 0;
55+
lldb::addr_t m_base_data_address = 0;
56+
std::map<size_t, lldb::ValueObjectSP> m_children;
57+
};
58+
59+
} // namespace formatters
60+
} // namespace lldb_private
61+
62+
lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd::
63+
MsvcStlVectorSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
64+
: SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() {
65+
if (valobj_sp)
66+
Update();
67+
}
68+
69+
llvm::Expected<uint32_t> lldb_private::formatters::
70+
MsvcStlVectorSyntheticFrontEnd::CalculateNumChildren() {
71+
if (!m_start || !m_finish)
72+
return llvm::createStringError(
73+
"Failed to determine start/end of vector data.");
74+
75+
uint64_t start_val = m_start->GetValueAsUnsigned(0);
76+
uint64_t finish_val = m_finish->GetValueAsUnsigned(0);
77+
78+
// A default-initialized empty vector.
79+
if (start_val == 0 && finish_val == 0)
80+
return 0;
81+
82+
if (start_val == 0)
83+
return llvm::createStringError("Invalid value for start of vector.");
84+
85+
if (finish_val == 0)
86+
return llvm::createStringError("Invalid value for end of vector.");
87+
88+
if (start_val > finish_val)
89+
return llvm::createStringError(
90+
"Start of vector data begins after end pointer.");
91+
92+
size_t num_children = (finish_val - start_val);
93+
if (num_children % m_element_size)
94+
return llvm::createStringError("Size not multiple of element size.");
95+
96+
return num_children / m_element_size;
97+
}
98+
99+
lldb::ValueObjectSP
100+
lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd::GetChildAtIndex(
101+
uint32_t idx) {
102+
if (!m_start || !m_finish)
103+
return lldb::ValueObjectSP();
104+
105+
uint64_t offset = idx * m_element_size;
106+
offset = offset + m_start->GetValueAsUnsigned(0);
107+
StreamString name;
108+
name.Printf("[%" PRIu64 "]", (uint64_t)idx);
109+
return CreateValueObjectFromAddress(name.GetString(), offset,
110+
m_backend.GetExecutionContextRef(),
111+
m_element_type);
112+
}
113+
114+
lldb::ChildCacheState
115+
lldb_private::formatters::MsvcStlVectorSyntheticFrontEnd::Update() {
116+
m_start = m_finish = nullptr;
117+
ValueObjectSP data_sp(m_backend.GetChildAtNamePath({"_Mypair", "_Myval2"}));
118+
119+
if (!data_sp)
120+
return lldb::ChildCacheState::eRefetch;
121+
122+
m_start = data_sp->GetChildMemberWithName("_Myfirst").get();
123+
m_finish = data_sp->GetChildMemberWithName("_Mylast").get();
124+
if (!m_start || !m_finish)
125+
return lldb::ChildCacheState::eRefetch;
126+
127+
m_element_type = m_start->GetCompilerType().GetPointeeType();
128+
llvm::Expected<uint64_t> size_or_err = m_element_type.GetByteSize(nullptr);
129+
if (size_or_err)
130+
m_element_size = *size_or_err;
131+
else
132+
LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters), size_or_err.takeError(),
133+
"{0}");
134+
135+
return lldb::ChildCacheState::eRefetch;
136+
}
137+
138+
llvm::Expected<size_t> lldb_private::formatters::
139+
MsvcStlVectorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
140+
if (!m_start || !m_finish)
141+
return llvm::createStringError("Type has no child named '%s'",
142+
name.AsCString());
143+
auto optional_idx = ExtractIndexFromString(name.GetCString());
144+
if (!optional_idx) {
145+
return llvm::createStringError("Type has no child named '%s'",
146+
name.AsCString());
147+
}
148+
return *optional_idx;
149+
}
150+
151+
lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::
152+
MsvcStlVectorBoolSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
153+
: SyntheticChildrenFrontEnd(*valobj_sp), m_bool_type(), m_exe_ctx_ref(),
154+
m_children() {
155+
if (valobj_sp) {
156+
Update();
157+
m_bool_type =
158+
valobj_sp->GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeBool);
159+
}
160+
}
161+
162+
llvm::Expected<uint32_t> lldb_private::formatters::
163+
MsvcStlVectorBoolSyntheticFrontEnd::CalculateNumChildren() {
164+
return m_count;
165+
}
166+
167+
lldb::ValueObjectSP
168+
lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::GetChildAtIndex(
169+
uint32_t idx) {
170+
auto iter = m_children.find(idx), end = m_children.end();
171+
if (iter != end)
172+
return iter->second;
173+
if (idx >= m_count)
174+
return {};
175+
if (m_base_data_address == 0 || m_count == 0)
176+
return {};
177+
if (!m_bool_type)
178+
return {};
179+
180+
// The vector<bool> is represented as a sequence of `int`s.
181+
// The size of an `int` is in `m_element_bit_size` (most often 32b).
182+
// To access the element at index `i`:
183+
// (bool)((data_address[i / bit_size] >> (i % bit_size)) & 1)
184+
185+
// int *byte_location = &data_address[i / bit_size]
186+
size_t byte_idx = (idx / m_element_bit_size) * (m_element_bit_size / 8);
187+
lldb::addr_t byte_location = m_base_data_address + byte_idx;
188+
189+
ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
190+
if (!process_sp)
191+
return {};
192+
Status err;
193+
Scalar scalar;
194+
size_t bytes_read = process_sp->ReadScalarIntegerFromMemory(
195+
byte_location, m_element_bit_size / 8, false, scalar, err);
196+
if (err.Fail() || bytes_read == 0 || !scalar.IsValid())
197+
return {};
198+
199+
size_t bit_index = idx % m_element_bit_size;
200+
bool bit_set = scalar.GetAPSInt()[bit_index];
201+
std::optional<uint64_t> size =
202+
llvm::expectedToOptional(m_bool_type.GetByteSize(nullptr));
203+
if (!size)
204+
return {};
205+
WritableDataBufferSP buffer_sp(new DataBufferHeap(*size, 0));
206+
if (bit_set && buffer_sp && buffer_sp->GetBytes()) {
207+
// regardless of endianness, anything non-zero is true
208+
*(buffer_sp->GetBytes()) = 1;
209+
}
210+
StreamString name;
211+
name.Printf("[%" PRIu64 "]", (uint64_t)idx);
212+
ValueObjectSP retval_sp(CreateValueObjectFromData(
213+
name.GetString(),
214+
DataExtractor(buffer_sp, process_sp->GetByteOrder(),
215+
process_sp->GetAddressByteSize()),
216+
m_exe_ctx_ref, m_bool_type));
217+
if (retval_sp)
218+
m_children[idx] = retval_sp;
219+
return retval_sp;
220+
}
221+
222+
lldb::ChildCacheState
223+
lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::Update() {
224+
m_exe_ctx_ref.Clear();
225+
m_count = 0;
226+
m_element_bit_size = 0;
227+
m_base_data_address = 0;
228+
m_children.clear();
229+
230+
ValueObjectSP valobj_sp = m_backend.GetSP();
231+
if (!valobj_sp)
232+
return lldb::ChildCacheState::eRefetch;
233+
auto exe_ctx_ref = valobj_sp->GetExecutionContextRef();
234+
235+
ValueObjectSP size_sp = valobj_sp->GetChildMemberWithName("_Mysize");
236+
if (!size_sp)
237+
return lldb::ChildCacheState::eRefetch;
238+
uint64_t count = size_sp->GetValueAsUnsigned(0);
239+
if (count == 0)
240+
return lldb::ChildCacheState::eReuse;
241+
242+
ValueObjectSP begin_sp(valobj_sp->GetChildAtNamePath(
243+
{"_Myvec", "_Mypair", "_Myval2", "_Myfirst"}));
244+
if (!begin_sp)
245+
return lldb::ChildCacheState::eRefetch;
246+
247+
// FIXME: the STL exposes _EEN_VBITS as a constant - it should be used instead
248+
CompilerType begin_ty = begin_sp->GetCompilerType().GetPointeeType();
249+
if (!begin_ty.IsValid())
250+
return lldb::ChildCacheState::eRefetch;
251+
llvm::Expected<uint64_t> element_bit_size = begin_ty.GetBitSize(nullptr);
252+
if (!element_bit_size)
253+
return lldb::ChildCacheState::eRefetch;
254+
255+
uint64_t base_data_address = begin_sp->GetValueAsUnsigned(0);
256+
if (!base_data_address)
257+
return lldb::ChildCacheState::eRefetch;
258+
259+
m_exe_ctx_ref = exe_ctx_ref;
260+
m_count = count;
261+
m_element_bit_size = *element_bit_size;
262+
m_base_data_address = base_data_address;
263+
return lldb::ChildCacheState::eRefetch;
264+
}
265+
266+
llvm::Expected<size_t>
267+
lldb_private::formatters::MsvcStlVectorBoolSyntheticFrontEnd::
268+
GetIndexOfChildWithName(ConstString name) {
269+
if (!m_count || !m_base_data_address)
270+
return llvm::createStringError("Type has no child named '%s'",
271+
name.AsCString());
272+
auto optional_idx = ExtractIndexFromString(name.AsCString());
273+
if (!optional_idx) {
274+
return llvm::createStringError("Type has no child named '%s'",
275+
name.AsCString());
276+
}
277+
uint32_t idx = *optional_idx;
278+
if (idx >= CalculateNumChildrenIgnoringErrors())
279+
return llvm::createStringError("Type has no child named '%s'",
280+
name.AsCString());
281+
return idx;
282+
}
283+
284+
lldb_private::SyntheticChildrenFrontEnd *
285+
lldb_private::formatters::MsvcStlVectorSyntheticFrontEndCreator(
286+
lldb::ValueObjectSP valobj_sp) {
287+
if (!valobj_sp)
288+
return nullptr;
289+
290+
valobj_sp = valobj_sp->GetNonSyntheticValue();
291+
if (!valobj_sp)
292+
return nullptr;
293+
294+
// We can't check the template parameter here, because PDB doesn't include
295+
// this information.
296+
297+
// vector<T>
298+
if (valobj_sp->GetChildMemberWithName("_Mypair") != nullptr)
299+
return new MsvcStlVectorSyntheticFrontEnd(valobj_sp);
300+
// vector<bool>
301+
if (valobj_sp->GetChildMemberWithName("_Myvec") != nullptr)
302+
return new MsvcStlVectorBoolSyntheticFrontEnd(valobj_sp);
303+
304+
return nullptr;
305+
}

0 commit comments

Comments
 (0)