Skip to content

Commit da68e72

Browse files
authored
[LLDB] Add formatters for MSVC STL std::unique_ptr (llvm#148248)
This PR adds a summary and synthetic children for `std::unique_ptr` from MSVC's STL ([NatVis](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/debugger/STL.natvis#L285-L303)). As with libc++, the deleter is only shown if it's non-empty. Tested both the shared_ptr and unique_ptr tests on Windows. Towards llvm#24834.
1 parent a716cc0 commit da68e72

File tree

4 files changed

+161
-4
lines changed

4 files changed

+161
-4
lines changed

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

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,10 +1566,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
15661566
"std::optional synthetic child", "^std::optional<.+>(( )?&)?$",
15671567
stl_deref_flags, true);
15681568

1569-
AddCXXSummary(cpp_category_sp,
1570-
lldb_private::formatters::LibStdcppUniquePointerSummaryProvider,
1571-
"libstdc++ std::unique_ptr summary provider",
1572-
"^std::unique_ptr<.+>(( )?&)?$", stl_summary_flags, true);
15731569
AddCXXSummary(cpp_category_sp,
15741570
lldb_private::formatters::StdlibCoroutineHandleSummaryProvider,
15751571
"libstdc++ std::coroutine_handle summary provider",
@@ -1599,6 +1595,24 @@ GenericSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream,
15991595
return LibStdcppSmartPointerSummaryProvider(valobj, stream, options);
16001596
}
16011597

1598+
static lldb_private::SyntheticChildrenFrontEnd *
1599+
GenericUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *children,
1600+
lldb::ValueObjectSP valobj_sp) {
1601+
if (!valobj_sp)
1602+
return nullptr;
1603+
1604+
if (IsMsvcStlUniquePtr(*valobj_sp))
1605+
return MsvcStlUniquePtrSyntheticFrontEndCreator(valobj_sp);
1606+
return LibStdcppUniquePtrSyntheticFrontEndCreator(children, valobj_sp);
1607+
}
1608+
1609+
static bool GenericUniquePtrSummaryProvider(ValueObject &valobj, Stream &stream,
1610+
const TypeSummaryOptions &options) {
1611+
if (IsMsvcStlUniquePtr(valobj))
1612+
return MsvcStlUniquePtrSummaryProvider(valobj, stream, options);
1613+
return LibStdcppUniquePointerSummaryProvider(valobj, stream, options);
1614+
}
1615+
16021616
/// Load formatters that are formatting types from more than one STL
16031617
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16041618
if (!cpp_category_sp)
@@ -1642,19 +1656,28 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16421656
},
16431657
"MSVC STL/libstdc++ std::wstring summary provider"));
16441658

1659+
stl_summary_flags.SetDontShowChildren(false);
1660+
stl_summary_flags.SetSkipPointers(false);
1661+
16451662
AddCXXSynthetic(cpp_category_sp, GenericSmartPointerSyntheticFrontEndCreator,
16461663
"std::shared_ptr synthetic children",
16471664
"^std::shared_ptr<.+>(( )?&)?$", stl_synth_flags, true);
16481665
AddCXXSynthetic(cpp_category_sp, GenericSmartPointerSyntheticFrontEndCreator,
16491666
"std::weak_ptr synthetic children",
16501667
"^std::weak_ptr<.+>(( )?&)?$", stl_synth_flags, true);
1668+
AddCXXSynthetic(cpp_category_sp, GenericUniquePtrSyntheticFrontEndCreator,
1669+
"std::unique_ptr synthetic children",
1670+
"^std::unique_ptr<.+>(( )?&)?$", stl_synth_flags, true);
16511671

16521672
AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
16531673
"MSVC STL/libstdc++ std::shared_ptr summary provider",
16541674
"^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true);
16551675
AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
16561676
"MSVC STL/libstdc++ std::weak_ptr summary provider",
16571677
"^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true);
1678+
AddCXXSummary(cpp_category_sp, GenericUniquePtrSummaryProvider,
1679+
"MSVC STL/libstdc++ std::unique_ptr summary provider",
1680+
"^std::unique_ptr<.+>(( )?&)?$", stl_summary_flags, true);
16581681
}
16591682

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

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ bool MsvcStlSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream,
3737
lldb_private::SyntheticChildrenFrontEnd *
3838
MsvcStlSmartPointerSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
3939

40+
// MSVC STL std::unique_ptr<>
41+
bool IsMsvcStlUniquePtr(ValueObject &valobj);
42+
bool MsvcStlUniquePtrSummaryProvider(ValueObject &valobj, Stream &stream,
43+
const TypeSummaryOptions &options);
44+
45+
lldb_private::SyntheticChildrenFrontEnd *
46+
MsvcStlUniquePtrSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
47+
4048
} // namespace formatters
4149
} // namespace lldb_private
4250

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

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,23 @@ class MsvcStlSmartPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
8484
ValueObject *m_ptr_obj = nullptr;
8585
};
8686

87+
class MsvcStlUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
88+
public:
89+
MsvcStlUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
90+
91+
llvm::Expected<uint32_t> CalculateNumChildren() override;
92+
93+
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
94+
95+
lldb::ChildCacheState Update() override;
96+
97+
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
98+
99+
private:
100+
lldb::ValueObjectSP m_value_ptr_sp;
101+
lldb::ValueObjectSP m_deleter_sp;
102+
};
103+
87104
} // namespace formatters
88105
} // namespace lldb_private
89106

@@ -163,3 +180,101 @@ lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEndCreator(
163180
lldb::ValueObjectSP valobj_sp) {
164181
return new MsvcStlSmartPointerSyntheticFrontEnd(valobj_sp);
165182
}
183+
184+
bool lldb_private::formatters::IsMsvcStlUniquePtr(ValueObject &valobj) {
185+
if (auto valobj_sp = valobj.GetNonSyntheticValue())
186+
return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr;
187+
188+
return false;
189+
}
190+
191+
bool lldb_private::formatters::MsvcStlUniquePtrSummaryProvider(
192+
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
193+
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
194+
if (!valobj_sp)
195+
return false;
196+
197+
ValueObjectSP ptr_sp(valobj_sp->GetChildAtNamePath({"_Mypair", "_Myval2"}));
198+
if (!ptr_sp)
199+
return false;
200+
201+
DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
202+
203+
return true;
204+
}
205+
206+
lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEnd::
207+
MsvcStlUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
208+
: SyntheticChildrenFrontEnd(*valobj_sp) {
209+
if (valobj_sp)
210+
Update();
211+
}
212+
213+
llvm::Expected<uint32_t> lldb_private::formatters::
214+
MsvcStlUniquePtrSyntheticFrontEnd::CalculateNumChildren() {
215+
if (m_value_ptr_sp)
216+
return m_deleter_sp ? 2 : 1;
217+
return 0;
218+
}
219+
220+
lldb::ValueObjectSP
221+
lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEnd::GetChildAtIndex(
222+
uint32_t idx) {
223+
if (!m_value_ptr_sp)
224+
return lldb::ValueObjectSP();
225+
226+
if (idx == 0)
227+
return m_value_ptr_sp;
228+
229+
if (idx == 1)
230+
return m_deleter_sp;
231+
232+
if (idx == 2) {
233+
Status status;
234+
auto value_sp = m_value_ptr_sp->Dereference(status);
235+
if (status.Success()) {
236+
return value_sp;
237+
}
238+
}
239+
240+
return lldb::ValueObjectSP();
241+
}
242+
243+
lldb::ChildCacheState
244+
lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEnd::Update() {
245+
ValueObjectSP valobj_sp = m_backend.GetSP();
246+
if (!valobj_sp)
247+
return lldb::ChildCacheState::eRefetch;
248+
249+
ValueObjectSP pair_sp = valobj_sp->GetChildMemberWithName("_Mypair");
250+
if (!pair_sp)
251+
return lldb::ChildCacheState::eRefetch;
252+
253+
if (auto value_ptr_sp = pair_sp->GetChildMemberWithName("_Myval2"))
254+
m_value_ptr_sp = value_ptr_sp->Clone(ConstString("pointer"));
255+
256+
// Only present if the deleter is non-empty
257+
if (auto deleter_sp = pair_sp->GetChildMemberWithName("_Myval1"))
258+
m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
259+
260+
return lldb::ChildCacheState::eRefetch;
261+
}
262+
263+
llvm::Expected<size_t>
264+
lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEnd::
265+
GetIndexOfChildWithName(ConstString name) {
266+
if (name == "pointer")
267+
return 0;
268+
if (name == "deleter")
269+
return 1;
270+
if (name == "obj" || name == "object" || name == "$$dereference$$")
271+
return 2;
272+
return llvm::createStringError("Type has no child named '%s'",
273+
name.AsCString());
274+
}
275+
276+
lldb_private::SyntheticChildrenFrontEnd *
277+
lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEndCreator(
278+
lldb::ValueObjectSP valobj_sp) {
279+
return new MsvcStlUniquePtrSyntheticFrontEnd(valobj_sp);
280+
}

lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ def test_libcxx(self):
102102
self.build(dictionary={"USE_LIBCPP": 1})
103103
self.do_test()
104104

105+
@add_test_categories(["msvcstl"])
106+
def test_msvcstl(self):
107+
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
108+
self.build()
109+
self.do_test()
110+
105111
def do_test_recursive_unique_ptr(self):
106112
# Tests that LLDB can handle when we have a loop in the unique_ptr
107113
# reference chain and that it correctly handles the different options
@@ -155,3 +161,8 @@ def test_recursive_unique_ptr_libstdcxx(self):
155161
def test_recursive_unique_ptr_libcxx(self):
156162
self.build(dictionary={"USE_LIBCPP": 1})
157163
self.do_test_recursive_unique_ptr()
164+
165+
@add_test_categories(["msvcstl"])
166+
def test_recursive_unique_ptr_msvcstl(self):
167+
self.build()
168+
self.do_test_recursive_unique_ptr()

0 commit comments

Comments
 (0)