Skip to content

Commit 287b944

Browse files
authored
[LLDB] Add formatters for MSVC STL unordered containers (#149519)
Adds formatters for MSVC STL's unordered containers. This one is relatively simple, because it can reuse the `std::list` synthetic children. The unordered containers (aka [`_Hash`](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/inc/xhash#L327)) contain a [`_List`](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/inc/xhash#L2012) which contains all elements (and is used for iterating through the container). Towards #24834.
1 parent 38318dd commit 287b944

File tree

5 files changed

+118
-11
lines changed

5 files changed

+118
-11
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
3737
MsvcStlAtomic.cpp
3838
MsvcStlSmartPointer.cpp
3939
MsvcStlTuple.cpp
40+
MsvcStlUnordered.cpp
4041
MsvcStlVariant.cpp
4142
MsvcStlVector.cpp
4243
MSVCUndecoratedNameParser.cpp

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,8 +1434,7 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14341434
stl_deref_flags,
14351435
"lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
14361436
cpp_category_sp->AddTypeSynthetic(
1437-
"^std::(__debug::)?unordered_(multi)?(map|set)<.+> >$",
1438-
eFormatterMatchRegex,
1437+
"^std::__debug::unordered_(multi)?(map|set)<.+> >$", eFormatterMatchRegex,
14391438
SyntheticChildrenSP(new ScriptedSyntheticChildren(
14401439
stl_deref_flags,
14411440
"lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider")));
@@ -1490,8 +1489,8 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
14901489

14911490
AddCXXSummary(cpp_category_sp,
14921491
lldb_private::formatters::ContainerSizeSummaryProvider,
1493-
"libstdc++ std unordered container summary provider",
1494-
"^std::(__debug::)?unordered_(multi)?(map|set)<.+> >$",
1492+
"libstdc++ debug std unordered container summary provider",
1493+
"^std::__debug::unordered_(multi)?(map|set)<.+> >$",
14951494
stl_summary_flags, true);
14961495

14971496
AddCXXSummary(
@@ -1660,6 +1659,19 @@ static bool GenericVariantSummaryProvider(ValueObject &valobj, Stream &stream,
16601659
return LibStdcppVariantSummaryProvider(valobj, stream, options);
16611660
}
16621661

1662+
static SyntheticChildrenFrontEnd *
1663+
GenericUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *children,
1664+
ValueObjectSP valobj_sp) {
1665+
if (!valobj_sp)
1666+
return nullptr;
1667+
1668+
if (IsMsvcStlUnordered(*valobj_sp))
1669+
return MsvcStlUnorderedSyntheticFrontEndCreator(children, valobj_sp);
1670+
return new ScriptedSyntheticChildren::FrontEnd(
1671+
"lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider",
1672+
*valobj_sp);
1673+
}
1674+
16631675
/// Load formatters that are formatting types from more than one STL
16641676
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
16651677
if (!cpp_category_sp)
@@ -1727,6 +1739,10 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
17271739
AddCXXSynthetic(cpp_category_sp, GenericVariantSyntheticFrontEndCreator,
17281740
"std::variant synthetic children", "^std::variant<.*>$",
17291741
stl_synth_flags, true);
1742+
AddCXXSynthetic(cpp_category_sp, GenericUnorderedSyntheticFrontEndCreator,
1743+
"std::unordered container synthetic children",
1744+
"^std::unordered_(multi)?(map|set)<.+> ?>$", stl_synth_flags,
1745+
true);
17301746

17311747
SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
17321748
stl_deref_flags.SetFrontEndWantsDereference();
@@ -1766,6 +1782,10 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
17661782
AddCXXSummary(cpp_category_sp, GenericVariantSummaryProvider,
17671783
"MSVC STL/libstdc++ std::variant summary provider",
17681784
"^std::variant<.*>$", stl_summary_flags, true);
1785+
AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
1786+
"MSVC STL/libstdc++ std unordered container summary provider",
1787+
"^std::unordered_(multi)?(map|set)<.+> ?>$", stl_summary_flags,
1788+
true);
17691789
}
17701790

17711791
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
@@ -86,6 +86,12 @@ SyntheticChildrenFrontEnd *
8686
MsvcStlAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *,
8787
lldb::ValueObjectSP valobj_sp);
8888

89+
// MSVC STL std::unordered_(multi){map|set}<>
90+
bool IsMsvcStlUnordered(ValueObject &valobj);
91+
SyntheticChildrenFrontEnd *
92+
MsvcStlUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *,
93+
lldb::ValueObjectSP valobj_sp);
94+
8995
} // namespace formatters
9096
} // namespace lldb_private
9197

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//===-- MsvcStlUnordered.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+
#include "lldb/DataFormatters/TypeSynthetic.h"
11+
12+
using namespace lldb;
13+
using namespace lldb_private;
14+
15+
namespace {
16+
17+
class UnorderedFrontEnd : public SyntheticChildrenFrontEnd {
18+
public:
19+
UnorderedFrontEnd(ValueObject &valobj) : SyntheticChildrenFrontEnd(valobj) {
20+
Update();
21+
}
22+
23+
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
24+
if (!m_list_sp)
25+
return llvm::createStringError("Missing _List");
26+
return m_list_sp->GetIndexOfChildWithName(name);
27+
}
28+
29+
lldb::ChildCacheState Update() override;
30+
31+
llvm::Expected<uint32_t> CalculateNumChildren() override {
32+
if (!m_list_sp)
33+
return llvm::createStringError("Missing _List");
34+
return m_list_sp->GetNumChildren();
35+
}
36+
37+
ValueObjectSP GetChildAtIndex(uint32_t idx) override {
38+
if (!m_list_sp)
39+
return nullptr;
40+
return m_list_sp->GetChildAtIndex(idx);
41+
}
42+
43+
private:
44+
ValueObjectSP m_list_sp;
45+
};
46+
47+
} // namespace
48+
49+
lldb::ChildCacheState UnorderedFrontEnd::Update() {
50+
m_list_sp = nullptr;
51+
ValueObjectSP list_sp = m_backend.GetChildMemberWithName("_List");
52+
if (!list_sp)
53+
return lldb::ChildCacheState::eRefetch;
54+
m_list_sp = list_sp->GetSyntheticValue();
55+
return lldb::ChildCacheState::eRefetch;
56+
}
57+
58+
bool formatters::IsMsvcStlUnordered(ValueObject &valobj) {
59+
if (auto valobj_sp = valobj.GetNonSyntheticValue())
60+
return valobj_sp->GetChildMemberWithName("_List") != nullptr;
61+
return false;
62+
}
63+
64+
SyntheticChildrenFrontEnd *formatters::MsvcStlUnorderedSyntheticFrontEndCreator(
65+
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
66+
if (valobj_sp)
67+
return new UnorderedFrontEnd(*valobj_sp);
68+
return nullptr;
69+
}

lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@
22
from lldbsuite.test.lldbtest import *
33
from lldbsuite.test import lldbutil
44

5-
USE_LIBSTDCPP = "USE_LIBSTDCPP"
6-
USE_LIBCPP = "USE_LIBCPP"
7-
85

96
class GenericUnorderedDataFormatterTestCase(TestBase):
107
def setUp(self):
118
TestBase.setUp(self)
129
self.namespace = "std"
1310

14-
def do_test_with_run_command(self, stdlib_type):
15-
self.build(dictionary={stdlib_type: "1"})
11+
def do_test_with_run_command(self):
1612
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
1713

1814
lldbutil.run_break_set_by_source_regexp(self, "Set break point at this line.")
@@ -127,8 +123,23 @@ def look_for_content_and_continue(self, var_name, patterns):
127123

128124
@add_test_categories(["libstdcxx"])
129125
def test_with_run_command_libstdcpp(self):
130-
self.do_test_with_run_command(USE_LIBSTDCPP)
126+
self.build(dictionary={"USE_LIBSTDCPP": 1})
127+
self.do_test_with_run_command()
128+
129+
@add_test_categories(["libstdcxx"])
130+
def test_with_run_command_libstdcxx_debug(self):
131+
self.build(
132+
dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": "-D_GLIBCXX_DEBUG"}
133+
)
134+
self.do_test_with_run_command()
131135

132136
@add_test_categories(["libc++"])
133137
def test_with_run_command_libcpp(self):
134-
self.do_test_with_run_command(USE_LIBCPP)
138+
self.build(dictionary={"USE_LIBCPP": 1})
139+
self.do_test_with_run_command()
140+
141+
@add_test_categories(["msvcstl"])
142+
def test_with_run_command_msvcstl(self):
143+
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
144+
self.build()
145+
self.do_test_with_run_command()

0 commit comments

Comments
 (0)