Skip to content

Commit def7bbb

Browse files
authored
[LLDB] Add formatters for MSVC STL std::shared_ptr (#147575)
This PR adds formatters for `std::shared_ptr` and `std::weak_ptr`. They are similar to the ones from libc++ and libstdc++. [Section from MSVC STL NatVis](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/debugger/STL.natvis#L512-L578). To support debugging with PDB debug info, I had to add an early exit in `GetDesugaredSmartPointerValue`, because with PDB, LLDB doesn't know about template types. This isn't an issue here, since the typedef type is already resolved there, so no casting is needed. The tests don't check for PDB - maybe this should be changed? I don't know a good way to do this. PDB has the downside that it resolves typedefs. Here in particular, the test for `element_type` would need to be replaced with `User` and `std::string` with `std::basic_string<char,std::char_traits<char>,std::allocator<char> >`. Towards #24834.
1 parent b8d21bf commit def7bbb

File tree

6 files changed

+218
-19
lines changed

6 files changed

+218
-19
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
3434
LibStdcppTuple.cpp
3535
LibStdcppUniquePointer.cpp
3636
MsvcStl.cpp
37+
MsvcStlSmartPointer.cpp
3738
MSVCUndecoratedNameParser.cpp
3839

3940
LINK_COMPONENTS

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

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,16 +1540,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
15401540
lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator,
15411541
"std::unique_ptr synthetic children", "^std::unique_ptr<.+>(( )?&)?$",
15421542
stl_synth_flags, true);
1543-
AddCXXSynthetic(
1544-
cpp_category_sp,
1545-
lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
1546-
"std::shared_ptr synthetic children", "^std::shared_ptr<.+>(( )?&)?$",
1547-
stl_synth_flags, true);
1548-
AddCXXSynthetic(
1549-
cpp_category_sp,
1550-
lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
1551-
"std::weak_ptr synthetic children", "^std::weak_ptr<.+>(( )?&)?$",
1552-
stl_synth_flags, true);
15531543
AddCXXSynthetic(
15541544
cpp_category_sp,
15551545
lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator,
@@ -1580,14 +1570,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
15801570
lldb_private::formatters::LibStdcppUniquePointerSummaryProvider,
15811571
"libstdc++ std::unique_ptr summary provider",
15821572
"^std::unique_ptr<.+>(( )?&)?$", stl_summary_flags, true);
1583-
AddCXXSummary(cpp_category_sp,
1584-
lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
1585-
"libstdc++ std::shared_ptr summary provider",
1586-
"^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true);
1587-
AddCXXSummary(cpp_category_sp,
1588-
lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
1589-
"libstdc++ std::weak_ptr summary provider",
1590-
"^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true);
15911573
AddCXXSummary(cpp_category_sp,
15921574
lldb_private::formatters::StdlibCoroutineHandleSummaryProvider,
15931575
"libstdc++ std::coroutine_handle summary provider",
@@ -1598,6 +1580,25 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
15981580
"^std::optional<.+>(( )?&)?$", stl_summary_flags, true);
15991581
}
16001582

1583+
static lldb_private::SyntheticChildrenFrontEnd *
1584+
GenericSmartPointerSyntheticFrontEndCreator(CXXSyntheticChildren *children,
1585+
lldb::ValueObjectSP valobj_sp) {
1586+
if (!valobj_sp)
1587+
return nullptr;
1588+
1589+
if (IsMsvcStlSmartPointer(*valobj_sp))
1590+
return MsvcStlSmartPointerSyntheticFrontEndCreator(valobj_sp);
1591+
return LibStdcppSharedPtrSyntheticFrontEndCreator(children, valobj_sp);
1592+
}
1593+
1594+
static bool
1595+
GenericSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream,
1596+
const TypeSummaryOptions &options) {
1597+
if (IsMsvcStlSmartPointer(valobj))
1598+
return MsvcStlSmartPointerSummaryProvider(valobj, stream, options);
1599+
return LibStdcppSmartPointerSummaryProvider(valobj, stream, options);
1600+
}
1601+
16011602
/// Load formatters that are formatting types from more than one STL
16021603
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16031604
if (!cpp_category_sp)
@@ -1611,6 +1612,10 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16111612
.SetDontShowValue(false)
16121613
.SetShowMembersOneLiner(false)
16131614
.SetHideItemNames(false);
1615+
SyntheticChildren::Flags stl_synth_flags;
1616+
stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
1617+
false);
1618+
16141619
using StringElementType = StringPrinter::StringElementType;
16151620

16161621
RegisterStdStringSummaryProvider(
@@ -1636,6 +1641,20 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16361641
return LibStdcppStringSummaryProvider(valobj, stream, options);
16371642
},
16381643
"MSVC STL/libstdc++ std::wstring summary provider"));
1644+
1645+
AddCXXSynthetic(cpp_category_sp, GenericSmartPointerSyntheticFrontEndCreator,
1646+
"std::shared_ptr synthetic children",
1647+
"^std::shared_ptr<.+>(( )?&)?$", stl_synth_flags, true);
1648+
AddCXXSynthetic(cpp_category_sp, GenericSmartPointerSyntheticFrontEndCreator,
1649+
"std::weak_ptr synthetic children",
1650+
"^std::weak_ptr<.+>(( )?&)?$", stl_synth_flags, true);
1651+
1652+
AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
1653+
"MSVC STL/libstdc++ std::shared_ptr summary provider",
1654+
"^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true);
1655+
AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
1656+
"MSVC STL/libstdc++ std::weak_ptr summary provider",
1657+
"^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true);
16391658
}
16401659

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

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//===---------------------------------------------------------------------===//
88

99
#include "Generic.h"
10+
#include "LibStdcpp.h"
11+
#include "MsvcStl.h"
1012

1113
lldb::ValueObjectSP lldb_private::formatters::GetDesugaredSmartPointerValue(
1214
ValueObject &ptr, ValueObject &container) {
@@ -16,7 +18,8 @@ lldb::ValueObjectSP lldb_private::formatters::GetDesugaredSmartPointerValue(
1618

1719
auto arg = container_type.GetTypeTemplateArgument(0);
1820
if (!arg)
19-
return nullptr;
21+
// If there isn't enough debug info, use the pointer type as is
22+
return ptr.GetSP();
2023

2124
return ptr.Cast(arg.GetPointerType());
2225
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ bool MsvcStlWStringSummaryProvider(
2929
ValueObject &valobj, Stream &stream,
3030
const TypeSummaryOptions &options); // VC 2015+ std::wstring
3131

32+
// MSVC STL std::shared_ptr<> and std::weak_ptr<>
33+
bool IsMsvcStlSmartPointer(ValueObject &valobj);
34+
bool MsvcStlSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream,
35+
const TypeSummaryOptions &options);
36+
37+
lldb_private::SyntheticChildrenFrontEnd *
38+
MsvcStlSmartPointerSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
39+
3240
} // namespace formatters
3341
} // namespace lldb_private
3442

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//===-- MsvcStlSmartPointer.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 "Generic.h"
10+
#include "MsvcStl.h"
11+
12+
#include "lldb/DataFormatters/FormattersHelpers.h"
13+
#include "lldb/DataFormatters/TypeSynthetic.h"
14+
15+
using namespace lldb;
16+
17+
bool lldb_private::formatters::IsMsvcStlSmartPointer(ValueObject &valobj) {
18+
return valobj.GetChildMemberWithName("_Ptr") != nullptr;
19+
}
20+
21+
bool lldb_private::formatters::MsvcStlSmartPointerSummaryProvider(
22+
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
23+
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
24+
if (!valobj_sp)
25+
return false;
26+
27+
ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("_Ptr"));
28+
ValueObjectSP ctrl_sp(valobj_sp->GetChildMemberWithName("_Rep"));
29+
if (!ctrl_sp || !ptr_sp)
30+
return false;
31+
32+
DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
33+
34+
bool success;
35+
uint64_t ctrl_addr = ctrl_sp->GetValueAsUnsigned(0, &success);
36+
// Empty control field (expired)
37+
if (!success || ctrl_addr == 0)
38+
return true;
39+
40+
uint64_t uses = 0;
41+
if (auto uses_sp = ctrl_sp->GetChildMemberWithName("_Uses")) {
42+
bool success;
43+
uses = uses_sp->GetValueAsUnsigned(0, &success);
44+
if (!success)
45+
return false;
46+
47+
stream.Printf(" strong=%" PRIu64, uses);
48+
}
49+
50+
// _Weaks is the number of weak references - (_Uses != 0).
51+
if (auto weak_count_sp = ctrl_sp->GetChildMemberWithName("_Weaks")) {
52+
bool success;
53+
uint64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);
54+
if (!success)
55+
return false;
56+
57+
stream.Printf(" weak=%" PRIu64, count - (uses != 0));
58+
}
59+
60+
return true;
61+
}
62+
63+
namespace lldb_private {
64+
namespace formatters {
65+
66+
class MsvcStlSmartPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
67+
public:
68+
MsvcStlSmartPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
69+
70+
llvm::Expected<uint32_t> CalculateNumChildren() override;
71+
72+
lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
73+
74+
lldb::ChildCacheState Update() override;
75+
76+
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
77+
78+
~MsvcStlSmartPointerSyntheticFrontEnd() override;
79+
80+
private:
81+
ValueObject *m_ptr_obj = nullptr;
82+
};
83+
84+
} // namespace formatters
85+
} // namespace lldb_private
86+
87+
lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::
88+
MsvcStlSmartPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
89+
: SyntheticChildrenFrontEnd(*valobj_sp) {
90+
if (valobj_sp)
91+
Update();
92+
}
93+
94+
llvm::Expected<uint32_t> lldb_private::formatters::
95+
MsvcStlSmartPointerSyntheticFrontEnd::CalculateNumChildren() {
96+
return (m_ptr_obj ? 1 : 0);
97+
}
98+
99+
lldb::ValueObjectSP
100+
lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::GetChildAtIndex(
101+
uint32_t idx) {
102+
if (!m_ptr_obj)
103+
return lldb::ValueObjectSP();
104+
105+
ValueObjectSP valobj_sp = m_backend.GetSP();
106+
if (!valobj_sp)
107+
return lldb::ValueObjectSP();
108+
109+
if (idx == 0)
110+
return m_ptr_obj->GetSP();
111+
112+
if (idx == 1) {
113+
Status status;
114+
ValueObjectSP value_sp = m_ptr_obj->Dereference(status);
115+
if (status.Success())
116+
return value_sp;
117+
}
118+
119+
return lldb::ValueObjectSP();
120+
}
121+
122+
lldb::ChildCacheState
123+
lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::Update() {
124+
m_ptr_obj = nullptr;
125+
126+
ValueObjectSP valobj_sp = m_backend.GetSP();
127+
if (!valobj_sp)
128+
return lldb::ChildCacheState::eRefetch;
129+
130+
auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("_Ptr");
131+
if (!ptr_obj_sp)
132+
return lldb::ChildCacheState::eRefetch;
133+
134+
auto cast_ptr_sp = GetDesugaredSmartPointerValue(*ptr_obj_sp, *valobj_sp);
135+
if (!cast_ptr_sp)
136+
return lldb::ChildCacheState::eRefetch;
137+
138+
m_ptr_obj = cast_ptr_sp->Clone(ConstString("pointer")).get();
139+
return lldb::ChildCacheState::eRefetch;
140+
}
141+
142+
llvm::Expected<size_t>
143+
lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::
144+
GetIndexOfChildWithName(ConstString name) {
145+
if (name == "pointer")
146+
return 0;
147+
148+
if (name == "object" || name == "$$dereference$$")
149+
return 1;
150+
151+
return llvm::createStringError("Type has no child named '%s'",
152+
name.AsCString());
153+
}
154+
155+
lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::
156+
~MsvcStlSmartPointerSyntheticFrontEnd() = default;
157+
158+
lldb_private::SyntheticChildrenFrontEnd *
159+
lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEndCreator(
160+
lldb::ValueObjectSP valobj_sp) {
161+
return new MsvcStlSmartPointerSyntheticFrontEnd(valobj_sp);
162+
}

lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,9 @@ def test_libcxx(self):
118118
def test_libstdcxx(self):
119119
self.build(dictionary={"USE_LIBSTDCPP": 1})
120120
self.do_test()
121+
122+
@add_test_categories(["msvcstl"])
123+
def test_msvcstl(self):
124+
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
125+
self.build()
126+
self.do_test()

0 commit comments

Comments
 (0)