Skip to content

Commit 0c9798c

Browse files
committed
[LLDB] Add formatters for MSVC STL std::deque
1 parent 287b944 commit 0c9798c

File tree

5 files changed

+226
-16
lines changed

5 files changed

+226
-16
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
3535
LibStdcppUniquePointer.cpp
3636
MsvcStl.cpp
3737
MsvcStlAtomic.cpp
38+
MsvcStlDeque.cpp
3839
MsvcStlSmartPointer.cpp
3940
MsvcStlTuple.cpp
4041
MsvcStlUnordered.cpp

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,7 +1414,7 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14141414
stl_synth_flags,
14151415
"lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
14161416
cpp_category_sp->AddTypeSynthetic(
1417-
"^std::(__debug)?deque<.+>(( )?&)?$", eFormatterMatchRegex,
1417+
"^std::__debug::deque<.+>(( )?&)?$", eFormatterMatchRegex,
14181418
SyntheticChildrenSP(new ScriptedSyntheticChildren(
14191419
stl_deref_flags,
14201420
"lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider")));
@@ -1472,10 +1472,9 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14721472
"libstdc++ std::set summary provider",
14731473
"^std::(__debug::)?set<.+> >(( )?&)?$", stl_summary_flags, true);
14741474

1475-
AddCXXSummary(
1476-
cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
1477-
"libstdc++ std::deque summary provider",
1478-
"^std::(__debug::)?deque<.+>(( )?&)?$", stl_summary_flags, true);
1475+
AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
1476+
"libstdc++ debug std::deque summary provider",
1477+
"^std::__debug::?deque<.+>(( )?&)?$", stl_summary_flags, true);
14791478

14801479
AddCXXSummary(
14811480
cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1672,6 +1671,18 @@ GenericUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *children,
16721671
*valobj_sp);
16731672
}
16741673

1674+
static SyntheticChildrenFrontEnd *
1675+
GenericDequeSyntheticFrontEndCreator(CXXSyntheticChildren *children,
1676+
ValueObjectSP valobj_sp) {
1677+
if (!valobj_sp)
1678+
return nullptr;
1679+
1680+
if (IsMsvcStlDeque(*valobj_sp))
1681+
return MsvcStlDequeSyntheticFrontEndCreator(children, valobj_sp);
1682+
return new ScriptedSyntheticChildren::FrontEnd(
1683+
"lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider", *valobj_sp);
1684+
}
1685+
16751686
/// Load formatters that are formatting types from more than one STL
16761687
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16771688
if (!cpp_category_sp)
@@ -1743,6 +1754,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
17431754
"std::unordered container synthetic children",
17441755
"^std::unordered_(multi)?(map|set)<.+> ?>$", stl_synth_flags,
17451756
true);
1757+
AddCXXSynthetic(cpp_category_sp, GenericDequeSyntheticFrontEndCreator,
1758+
"std::deque container synthetic children",
1759+
"^std::deque<.+>(( )?&)?$", stl_synth_flags, true);
17461760

17471761
SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
17481762
stl_deref_flags.SetFrontEndWantsDereference();
@@ -1786,6 +1800,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
17861800
"MSVC STL/libstdc++ std unordered container summary provider",
17871801
"^std::unordered_(multi)?(map|set)<.+> ?>$", stl_summary_flags,
17881802
true);
1803+
AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
1804+
"MSVC STL/libstd++ std::deque summary provider",
1805+
"^std::deque<.+>(( )?&)?$", stl_summary_flags, true);
17891806
}
17901807

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

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ SyntheticChildrenFrontEnd *
9292
MsvcStlUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *,
9393
lldb::ValueObjectSP valobj_sp);
9494

95+
// MSVC STL std::deque<>
96+
bool IsMsvcStlDeque(ValueObject &valobj);
97+
SyntheticChildrenFrontEnd *
98+
MsvcStlDequeSyntheticFrontEndCreator(CXXSyntheticChildren *,
99+
lldb::ValueObjectSP valobj_sp);
100+
95101
} // namespace formatters
96102
} // namespace lldb_private
97103

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
//===-- MsvcStlDeque.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 MsvcStlDequeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
20+
public:
21+
MsvcStlDequeSyntheticFrontEnd(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_map = nullptr;
33+
ExecutionContextRef m_exe_ctx_ref;
34+
35+
size_t m_block_size = 0;
36+
size_t m_offset = 0;
37+
size_t m_map_size = 0;
38+
39+
size_t m_element_size = 0;
40+
CompilerType m_element_type;
41+
42+
uint32_t m_size = 0;
43+
};
44+
45+
} // namespace formatters
46+
} // namespace lldb_private
47+
48+
lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::
49+
MsvcStlDequeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
50+
: SyntheticChildrenFrontEnd(*valobj_sp) {
51+
if (valobj_sp)
52+
Update();
53+
}
54+
55+
llvm::Expected<uint32_t> lldb_private::formatters::
56+
MsvcStlDequeSyntheticFrontEnd::CalculateNumChildren() {
57+
if (!m_map)
58+
return llvm::createStringError("Failed to read size");
59+
return m_size;
60+
}
61+
62+
lldb::ValueObjectSP
63+
lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::GetChildAtIndex(
64+
uint32_t idx) {
65+
if (idx >= m_size || !m_map)
66+
return nullptr;
67+
ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
68+
if (!process_sp)
69+
return nullptr;
70+
71+
// _EEN_DS = _Block_size
72+
// _Map[(($i + _Myoff) / _EEN_DS) % _Mapsize][($i + _Myoff) % _EEN_DS]
73+
size_t first_idx = ((idx + m_offset) / m_block_size) % m_map_size;
74+
lldb::addr_t first_address = m_map->GetValueAsUnsigned(0) +
75+
first_idx * process_sp->GetAddressByteSize();
76+
77+
Status err;
78+
lldb::addr_t second_base =
79+
process_sp->ReadPointerFromMemory(first_address, err);
80+
if (err.Fail())
81+
return nullptr;
82+
83+
size_t second_idx = (idx + m_offset) % m_block_size;
84+
size_t second_address = second_base + second_idx * m_element_size;
85+
86+
StreamString name;
87+
name.Printf("[%" PRIu64 "]", (uint64_t)idx);
88+
return CreateValueObjectFromAddress(name.GetString(), second_address,
89+
m_backend.GetExecutionContextRef(),
90+
m_element_type);
91+
}
92+
93+
lldb::ChildCacheState
94+
lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::Update() {
95+
m_size = 0;
96+
m_map = nullptr;
97+
m_element_type.Clear();
98+
99+
auto storage_sp = m_backend.GetChildAtNamePath({"_Mypair", "_Myval2"});
100+
if (!storage_sp)
101+
return lldb::eRefetch;
102+
103+
auto deque_type = m_backend.GetCompilerType().GetNonReferenceType();
104+
if (deque_type.IsPointerType())
105+
deque_type = deque_type.GetPointeeType();
106+
if (!deque_type)
107+
return lldb::eRefetch;
108+
109+
auto block_size_decl = deque_type.GetStaticFieldWithName("_Block_size");
110+
if (!block_size_decl)
111+
return lldb::eRefetch;
112+
auto block_size = block_size_decl.GetConstantValue();
113+
if (!block_size.IsValid())
114+
return lldb::eRefetch;
115+
116+
auto element_type = deque_type.GetTypeTemplateArgument(0);
117+
if (!element_type)
118+
return lldb::eRefetch;
119+
auto element_size = element_type.GetByteSize(nullptr);
120+
if (!element_size)
121+
return lldb::eRefetch;
122+
123+
auto offset_sp = storage_sp->GetChildMemberWithName("_Myoff");
124+
auto map_size_sp = storage_sp->GetChildMemberWithName("_Mapsize");
125+
auto map_sp = storage_sp->GetChildMemberWithName("_Map");
126+
auto size_sp = storage_sp->GetChildMemberWithName("_Mysize");
127+
if (!offset_sp || !map_size_sp || !map_sp || !size_sp)
128+
return lldb::eRefetch;
129+
130+
bool ok = false;
131+
uint64_t offset = offset_sp->GetValueAsUnsigned(0, &ok);
132+
if (!ok)
133+
return lldb::eRefetch;
134+
135+
uint64_t map_size = map_size_sp->GetValueAsUnsigned(0, &ok);
136+
if (!ok)
137+
return lldb::eRefetch;
138+
139+
uint64_t size = size_sp->GetValueAsUnsigned(0, &ok);
140+
if (!ok)
141+
return lldb::eRefetch;
142+
143+
m_map = map_sp.get();
144+
m_exe_ctx_ref = m_backend.GetExecutionContextRef();
145+
m_block_size = block_size.ULongLong();
146+
m_offset = offset;
147+
m_map_size = map_size;
148+
m_element_size = *element_size;
149+
m_element_type = element_type;
150+
m_size = size;
151+
return lldb::eRefetch;
152+
}
153+
154+
llvm::Expected<size_t> lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::
155+
GetIndexOfChildWithName(ConstString name) {
156+
if (!m_map)
157+
return llvm::createStringError("Type has no child named '%s'",
158+
name.AsCString());
159+
if (auto optional_idx = ExtractIndexFromString(name.GetCString()))
160+
return *optional_idx;
161+
162+
return llvm::createStringError("Type has no child named '%s'",
163+
name.AsCString());
164+
}
165+
166+
bool lldb_private::formatters::IsMsvcStlDeque(ValueObject &valobj) {
167+
if (auto valobj_sp = valobj.GetNonSyntheticValue())
168+
return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr;
169+
return false;
170+
}
171+
172+
lldb_private::SyntheticChildrenFrontEnd *
173+
lldb_private::formatters::MsvcStlDequeSyntheticFrontEndCreator(
174+
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
175+
return new MsvcStlDequeSyntheticFrontEnd(valobj_sp);
176+
}

lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
from lldbsuite.test.lldbtest import *
44
from lldbsuite.test import lldbutil
55

6-
USE_LIBSTDCPP = "USE_LIBSTDCPP"
7-
USE_LIBCPP = "USE_LIBCPP"
8-
96

107
class GenericDequeDataFormatterTestCase(TestBase):
118
def findVariable(self, name):
@@ -56,8 +53,7 @@ def check_numbers(self, var_name, show_ptr=False):
5653
],
5754
)
5855

59-
def do_test(self, stdlib_type):
60-
self.build(dictionary={stdlib_type: "1"})
56+
def do_test(self):
6157
(_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(
6258
self, "break here", lldb.SBFileSpec("main.cpp")
6359
)
@@ -135,15 +131,22 @@ def do_test(self, stdlib_type):
135131

136132
@add_test_categories(["libstdcxx"])
137133
def test_libstdcpp(self):
138-
self.do_test(USE_LIBSTDCPP)
134+
self.build(dictionary={"USE_LIBSTDCPP": 1})
135+
self.do_test()
139136

140137
@add_test_categories(["libc++"])
141138
def test_libcpp(self):
142-
self.do_test(USE_LIBCPP)
139+
self.build(dictionary={"USE_LIBCPP": 1})
140+
self.do_test()
141+
142+
@add_test_categories(["msvcstl"])
143+
def test_msvcstl(self):
144+
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
145+
self.build()
146+
self.do_test()
143147

144-
def do_test_ref_and_ptr(self, stdlib_type: str):
148+
def do_test_ref_and_ptr(self):
145149
"""Test formatting of std::deque& and std::deque*"""
146-
self.build(dictionary={stdlib_type: "1"})
147150
(self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
148151
self, "stop here", lldb.SBFileSpec("main.cpp", False)
149152
)
@@ -157,8 +160,15 @@ def do_test_ref_and_ptr(self, stdlib_type: str):
157160

158161
@add_test_categories(["libstdcxx"])
159162
def test_libstdcpp_ref_and_ptr(self):
160-
self.do_test_ref_and_ptr(USE_LIBSTDCPP)
163+
self.build(dictionary={"USE_LIBSTDCPP": 1})
164+
self.do_test_ref_and_ptr()
161165

162166
@add_test_categories(["libc++"])
163167
def test_libcpp_ref_and_ptr(self):
164-
self.do_test_ref_and_ptr(USE_LIBCPP)
168+
self.build(dictionary={"USE_LIBCPP": 1})
169+
self.do_test_ref_and_ptr()
170+
171+
@add_test_categories(["msvcstl"])
172+
def test_msvcstl_ref_and_ptr(self):
173+
self.build()
174+
self.do_test_ref_and_ptr()

0 commit comments

Comments
 (0)