Skip to content

Commit 00e7e4c

Browse files
committed
[LLDB] Add type summaries for MSVC STL strings
1 parent c2ea940 commit 00e7e4c

File tree

20 files changed

+766
-180
lines changed

20 files changed

+766
-180
lines changed

lldb/include/lldb/DataFormatters/FormatClasses.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,16 @@ class FormattersMatchData {
149149
CandidateLanguagesVector m_candidate_languages;
150150
};
151151

152+
using CxxTypeCandidateMatchFn = bool(const FormattersMatchCandidate &);
153+
152154
class TypeNameSpecifierImpl {
153155
public:
154156
TypeNameSpecifierImpl() = default;
155157

156158
TypeNameSpecifierImpl(llvm::StringRef name,
157-
lldb::FormatterMatchType match_type)
158-
: m_match_type(match_type) {
159+
lldb::FormatterMatchType match_type,
160+
CxxTypeCandidateMatchFn *match_fn = nullptr)
161+
: m_match_type(match_type), m_match_fn(match_fn) {
159162
m_type.m_type_name = std::string(name);
160163
}
161164

@@ -188,6 +191,8 @@ class TypeNameSpecifierImpl {
188191
return CompilerType();
189192
}
190193

194+
CxxTypeCandidateMatchFn *GetCxxMatchFunction() const { return m_match_fn; }
195+
191196
lldb::FormatterMatchType GetMatchType() { return m_match_type; }
192197

193198
bool IsRegex() { return m_match_type == lldb::eFormatterMatchRegex; }
@@ -200,6 +205,7 @@ class TypeNameSpecifierImpl {
200205
CompilerType m_compiler_type;
201206
};
202207
TypeOrName m_type;
208+
CxxTypeCandidateMatchFn *m_match_fn = nullptr;
203209

204210
TypeNameSpecifierImpl(const TypeNameSpecifierImpl &) = delete;
205211
const TypeNameSpecifierImpl &

lldb/include/lldb/DataFormatters/FormattersContainer.h

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class TypeMatcher {
5050
/// - eFormatterMatchCallback: run the function in m_name to decide if a type
5151
/// matches or not.
5252
lldb::FormatterMatchType m_match_type;
53+
CxxTypeCandidateMatchFn *m_match_fn = nullptr;
5354

5455
// if the user tries to add formatters for, say, "struct Foo" those will not
5556
// match any type because of the way we strip qualifiers from typenames this
@@ -86,42 +87,29 @@ class TypeMatcher {
8687
/// name specifier.
8788
TypeMatcher(lldb::TypeNameSpecifierImplSP type_specifier)
8889
: m_name(type_specifier->GetName()),
89-
m_match_type(type_specifier->GetMatchType()) {
90+
m_match_type(type_specifier->GetMatchType()),
91+
m_match_fn(type_specifier->GetCxxMatchFunction()) {
9092
if (m_match_type == lldb::eFormatterMatchRegex)
9193
m_type_name_regex = RegularExpression(type_specifier->GetName());
9294
}
9395

9496
/// True iff this matches the given type.
9597
bool Matches(FormattersMatchCandidate candidate_type) const {
96-
ConstString type_name = candidate_type.GetTypeName();
97-
switch (m_match_type) {
98-
case lldb::eFormatterMatchExact:
99-
return m_name == type_name ||
100-
StripTypeName(m_name) == StripTypeName(type_name);
101-
case lldb::eFormatterMatchRegex:
102-
return m_type_name_regex.Execute(type_name.GetStringRef());
103-
case lldb::eFormatterMatchCallback:
104-
// CommandObjectType{Synth,Filter}Add tries to prevent the user from
105-
// creating both a synthetic child provider and a filter for the same type
106-
// in the same category, but we don't have a type object at that point, so
107-
// it creates a dummy candidate without type or script interpreter.
108-
// Skip callback matching in these cases.
109-
if (candidate_type.GetScriptInterpreter())
110-
return candidate_type.GetScriptInterpreter()->FormatterCallbackFunction(
111-
m_name.AsCString(),
112-
std::make_shared<TypeImpl>(candidate_type.GetType()));
113-
}
114-
return false;
98+
if (!MatchesName(candidate_type))
99+
return false;
100+
if (m_match_fn && !m_match_fn(candidate_type))
101+
return false;
102+
return true;
115103
}
116104

117105
lldb::FormatterMatchType GetMatchType() const { return m_match_type; }
118106

119107
/// Returns the underlying match string for this TypeMatcher.
120108
ConstString GetMatchString() const {
121109
if (m_match_type == lldb::eFormatterMatchExact)
122-
return StripTypeName(m_name);
110+
return StripTypeName(m_name);
123111
if (m_match_type == lldb::eFormatterMatchRegex)
124-
return ConstString(m_type_name_regex.GetText());
112+
return ConstString(m_type_name_regex.GetText());
125113
return m_name;
126114
}
127115

@@ -134,7 +122,31 @@ class TypeMatcher {
134122
/// (lldb) type summary add --summary-string \"A\" -x TypeName
135123
/// (lldb) type summary delete TypeName
136124
bool CreatedBySameMatchString(TypeMatcher other) const {
137-
return GetMatchString() == other.GetMatchString();
125+
return GetMatchString() == other.GetMatchString() &&
126+
m_match_fn == other.m_match_fn;
127+
}
128+
129+
private:
130+
bool MatchesName(FormattersMatchCandidate candidate_type) const {
131+
ConstString type_name = candidate_type.GetTypeName();
132+
switch (m_match_type) {
133+
case lldb::eFormatterMatchExact:
134+
return m_name == type_name ||
135+
StripTypeName(m_name) == StripTypeName(type_name);
136+
case lldb::eFormatterMatchRegex:
137+
return m_type_name_regex.Execute(type_name.GetStringRef());
138+
case lldb::eFormatterMatchCallback:
139+
// CommandObjectType{Synth,Filter}Add tries to prevent the user from
140+
// creating both a synthetic child provider and a filter for the same type
141+
// in the same category, but we don't have a type object at that point, so
142+
// it creates a dummy candidate without type or script interpreter.
143+
// Skip callback matching in these cases.
144+
if (candidate_type.GetScriptInterpreter())
145+
return candidate_type.GetScriptInterpreter()->FormatterCallbackFunction(
146+
m_name.AsCString(),
147+
std::make_shared<TypeImpl>(candidate_type.GetType()));
148+
}
149+
return false;
138150
}
139151
};
140152

lldb/include/lldb/DataFormatters/StringPrinter.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,21 @@ class StringPrinter {
152152
template <StringElementType element_type>
153153
static bool
154154
ReadBufferAndDumpToStream(const ReadBufferAndDumpToStreamOptions &options);
155+
156+
template <StringElementType element_type>
157+
static constexpr uint64_t ElementByteSize() {
158+
switch (element_type) {
159+
case StringElementType::ASCII:
160+
case StringElementType::UTF8:
161+
return 1;
162+
case StringElementType::UTF16:
163+
return 2;
164+
case StringElementType::UTF32:
165+
return 3;
166+
default:
167+
return 0;
168+
}
169+
}
155170
};
156171

157172
} // namespace formatters

lldb/packages/Python/lldbsuite/test/dotest.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,26 @@ def checkLibstdcxxSupport():
831831
configuration.skip_categories.append("libstdcxx")
832832

833833

834+
def canRunMsvcStlTests():
835+
from lldbsuite.test import lldbplatformutil
836+
837+
platform = lldbplatformutil.getPlatform()
838+
if platform == "windows":
839+
return True, "MSVC STL is present on Windows"
840+
return False, f"Don't know how to build with MSVC's STL on {platform}"
841+
842+
843+
def checkMsvcStlSupport():
844+
result, reason = canRunMsvcStlTests()
845+
if result:
846+
return # msvcstl supported
847+
if "msvcstl" in configuration.categories_list:
848+
return # msvcstl category explicitly requested, let it run.
849+
if configuration.verbose:
850+
print(f"msvcstl tests will not be run because: {reason}")
851+
configuration.skip_categories.append("msvcstl")
852+
853+
834854
def canRunWatchpointTests():
835855
from lldbsuite.test import lldbplatformutil
836856

@@ -1044,6 +1064,7 @@ def run_suite():
10441064

10451065
checkLibcxxSupport()
10461066
checkLibstdcxxSupport()
1067+
checkMsvcStlSupport()
10471068
checkWatchpointSupport()
10481069
checkDebugInfoSupport()
10491070
checkDebugServerSupport()

lldb/packages/Python/lldbsuite/test/test_categories.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"lldb-server": "Tests related to lldb-server",
3434
"lldb-dap": "Tests for the Debug Adapter Protocol with lldb-dap",
3535
"llgs": "Tests for the gdb-server functionality of lldb-server",
36+
"msvcstl": "Test for MSVC STL data formatters",
3637
"pexpect": "Tests requiring the pexpect library to be available",
3738
"objc": "Tests related to the Objective-C programming language support",
3839
"pyapi": "Tests related to the Python API",

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
3232
LibStdcpp.cpp
3333
LibStdcppTuple.cpp
3434
LibStdcppUniquePointer.cpp
35+
MsvcStl.cpp
3536
MSVCUndecoratedNameParser.cpp
3637

3738
LINK_COMPONENTS

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

Lines changed: 102 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "LibCxxVariant.h"
4747
#include "LibStdcpp.h"
4848
#include "MSVCUndecoratedNameParser.h"
49+
#include "MsvcStl.h"
4950
#include "lldb/lldb-enumerations.h"
5051

5152
using namespace lldb;
@@ -1372,6 +1373,52 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
13721373
"${var.__y_} ${var.__m_} ${var.__wdl_}")));
13731374
}
13741375

1376+
static bool IsMsvcStdStringType(const FormattersMatchCandidate &candidate) {
1377+
std::vector<uint32_t> indexes;
1378+
return candidate.GetType()
1379+
.GetCompilerType(true)
1380+
.GetIndexOfChildMemberWithName("_Mypair", true, indexes) > 0;
1381+
}
1382+
1383+
static bool
1384+
IsLibstdcppStdStringType(const FormattersMatchCandidate &candidate) {
1385+
std::vector<uint32_t> indexes;
1386+
return candidate.GetType()
1387+
.GetCompilerType(true)
1388+
.GetIndexOfChildMemberWithName("_M_dataplus", true, indexes) > 0;
1389+
}
1390+
1391+
static void RegisterStdStringSummaryProvider(
1392+
const lldb::TypeCategoryImplSP &category_sp, llvm::StringRef string_ty,
1393+
llvm::StringRef char_ty, CxxTypeCandidateMatchFn *match_fn,
1394+
lldb::TypeSummaryImplSP summary_sp) {
1395+
auto makeSpecifier = [match_fn](llvm::StringRef name) {
1396+
return std::make_shared<lldb_private::TypeNameSpecifierImpl>(
1397+
name, eFormatterMatchExact, match_fn);
1398+
};
1399+
1400+
category_sp->AddTypeSummary(makeSpecifier(string_ty), summary_sp);
1401+
1402+
// std::basic_string<char>
1403+
category_sp->AddTypeSummary(
1404+
makeSpecifier((llvm::Twine("std::basic_string<") + char_ty + ">").str()),
1405+
summary_sp);
1406+
// std::basic_string<char,std::char_traits<char>,std::allocator<char> >
1407+
category_sp->AddTypeSummary(
1408+
makeSpecifier((llvm::Twine("std::basic_string<") + char_ty +
1409+
",std::char_traits<" + char_ty + ">,std::allocator<" +
1410+
char_ty + "> >")
1411+
.str()),
1412+
summary_sp);
1413+
// std::basic_string<char, std::char_traits<char>, std::allocator<char> >
1414+
category_sp->AddTypeSummary(
1415+
makeSpecifier((llvm::Twine("std::basic_string<") + char_ty +
1416+
", std::char_traits<" + char_ty + ">, std::allocator<" +
1417+
char_ty + "> >")
1418+
.str()),
1419+
summary_sp);
1420+
}
1421+
13751422
static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
13761423
if (!cpp_category_sp)
13771424
return;
@@ -1385,26 +1432,17 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
13851432
.SetShowMembersOneLiner(false)
13861433
.SetHideItemNames(false);
13871434

1388-
lldb::TypeSummaryImplSP std_string_summary_sp(
1389-
new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p}"));
1390-
13911435
lldb::TypeSummaryImplSP cxx11_string_summary_sp(new CXXFunctionSummaryFormat(
13921436
stl_summary_flags, LibStdcppStringSummaryProvider,
13931437
"libstdc++ c++11 std::string summary provider"));
13941438
lldb::TypeSummaryImplSP cxx11_wstring_summary_sp(new CXXFunctionSummaryFormat(
13951439
stl_summary_flags, LibStdcppWStringSummaryProvider,
13961440
"libstdc++ c++11 std::wstring summary provider"));
13971441

1398-
cpp_category_sp->AddTypeSummary("std::string", eFormatterMatchExact,
1399-
std_string_summary_sp);
1400-
cpp_category_sp->AddTypeSummary("std::basic_string<char>",
1401-
eFormatterMatchExact, std_string_summary_sp);
1402-
cpp_category_sp->AddTypeSummary(
1403-
"std::basic_string<char,std::char_traits<char>,std::allocator<char> >",
1404-
eFormatterMatchExact, std_string_summary_sp);
1405-
cpp_category_sp->AddTypeSummary(
1406-
"std::basic_string<char, std::char_traits<char>, std::allocator<char> >",
1407-
eFormatterMatchExact, std_string_summary_sp);
1442+
RegisterStdStringSummaryProvider(
1443+
cpp_category_sp, "std::string", "char", IsLibstdcppStdStringType,
1444+
std::make_shared<StringSummaryFormat>(stl_summary_flags,
1445+
"${var._M_dataplus._M_p}"));
14081446

14091447
cpp_category_sp->AddTypeSummary("std::__cxx11::string", eFormatterMatchExact,
14101448
cxx11_string_summary_sp);
@@ -1420,20 +1458,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14201458

14211459
// making sure we force-pick the summary for printing wstring (_M_p is a
14221460
// wchar_t*)
1423-
lldb::TypeSummaryImplSP std_wstring_summary_sp(
1424-
new StringSummaryFormat(stl_summary_flags, "${var._M_dataplus._M_p%S}"));
1425-
1426-
cpp_category_sp->AddTypeSummary("std::wstring", eFormatterMatchExact,
1427-
std_wstring_summary_sp);
1428-
cpp_category_sp->AddTypeSummary("std::basic_string<wchar_t>",
1429-
eFormatterMatchExact, std_wstring_summary_sp);
1430-
cpp_category_sp->AddTypeSummary("std::basic_string<wchar_t,std::char_traits<"
1431-
"wchar_t>,std::allocator<wchar_t> >",
1432-
eFormatterMatchExact, std_wstring_summary_sp);
1433-
cpp_category_sp->AddTypeSummary(
1434-
"std::basic_string<wchar_t, std::char_traits<wchar_t>, "
1435-
"std::allocator<wchar_t> >",
1436-
eFormatterMatchExact, std_wstring_summary_sp);
1461+
RegisterStdStringSummaryProvider(
1462+
cpp_category_sp, "std::wstring", "wchar_t", IsLibstdcppStdStringType,
1463+
std::make_shared<StringSummaryFormat>(stl_summary_flags,
1464+
"${var._M_dataplus._M_p%S}"));
14371465

14381466
cpp_category_sp->AddTypeSummary("std::__cxx11::wstring", eFormatterMatchExact,
14391467
cxx11_wstring_summary_sp);
@@ -1629,6 +1657,52 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16291657
"^std::optional<.+>(( )?&)?$", stl_summary_flags, true);
16301658
}
16311659

1660+
static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
1661+
if (!cpp_category_sp)
1662+
return;
1663+
1664+
TypeSummaryImpl::Flags stl_summary_flags;
1665+
stl_summary_flags.SetCascades(true)
1666+
.SetSkipPointers(false)
1667+
.SetSkipReferences(false)
1668+
.SetDontShowChildren(true)
1669+
.SetDontShowValue(false)
1670+
.SetShowMembersOneLiner(false)
1671+
.SetHideItemNames(false);
1672+
1673+
using StringElementType = StringPrinter::StringElementType;
1674+
1675+
RegisterStdStringSummaryProvider(
1676+
cpp_category_sp, "std::string", "char", IsMsvcStdStringType,
1677+
std::make_shared<CXXFunctionSummaryFormat>(
1678+
stl_summary_flags,
1679+
MsvcStlStringSummaryProvider<StringElementType::ASCII>,
1680+
"MSVC STL std::string summary provider"));
1681+
RegisterStdStringSummaryProvider(
1682+
cpp_category_sp, "std::wstring", "wchar_t", IsMsvcStdStringType,
1683+
std::make_shared<CXXFunctionSummaryFormat>(
1684+
stl_summary_flags, MsvcStlWStringSummaryProvider,
1685+
"MSVC STL std::wstring summary provider"));
1686+
RegisterStdStringSummaryProvider(
1687+
cpp_category_sp, "std::u8string", "char8_t", IsMsvcStdStringType,
1688+
std::make_shared<CXXFunctionSummaryFormat>(
1689+
stl_summary_flags,
1690+
MsvcStlStringSummaryProvider<StringElementType::UTF8>,
1691+
"MSVC STL std::u8string summary provider"));
1692+
RegisterStdStringSummaryProvider(
1693+
cpp_category_sp, "std::u16string", "char16_t", IsMsvcStdStringType,
1694+
std::make_shared<CXXFunctionSummaryFormat>(
1695+
stl_summary_flags,
1696+
MsvcStlStringSummaryProvider<StringElementType::UTF16>,
1697+
"MSVC STL std::u16string summary provider"));
1698+
RegisterStdStringSummaryProvider(
1699+
cpp_category_sp, "std::u32string", "char32_t", IsMsvcStdStringType,
1700+
std::make_shared<CXXFunctionSummaryFormat>(
1701+
stl_summary_flags,
1702+
MsvcStlStringSummaryProvider<StringElementType::UTF32>,
1703+
"MSVC STL std::u32string summary provider"));
1704+
}
1705+
16321706
static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16331707
if (!cpp_category_sp)
16341708
return;
@@ -1743,6 +1817,7 @@ lldb::TypeCategoryImplSP CPlusPlusLanguage::GetFormatters() {
17431817
// LLDB prioritizes the last loaded matching formatter.
17441818
LoadLibCxxFormatters(g_category);
17451819
LoadLibStdcppFormatters(g_category);
1820+
LoadMsvcStlFormatters(g_category);
17461821
LoadSystemFormatters(g_category);
17471822
}
17481823
});

0 commit comments

Comments
 (0)