-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[LLDB] Add formatters for MSVC STL std::shared_ptr #147575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-lldb Author: nerix (Nerixyz) ChangesThis PR adds formatters for To support debugging with PDB debug info, I had to add an early exit in 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 Towards #24834. Full diff: https://github.com/llvm/llvm-project/pull/147575.diff 6 Files Affected:
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index 3ec3cad4b8178..296159ea28407 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -34,6 +34,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
LibStdcppTuple.cpp
LibStdcppUniquePointer.cpp
MsvcStl.cpp
+ MsvcStlSmartPointer.cpp
MSVCUndecoratedNameParser.cpp
LINK_COMPONENTS
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 17963c0273ba8..345c03bd019e8 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1540,16 +1540,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
lldb_private::formatters::LibStdcppUniquePtrSyntheticFrontEndCreator,
"std::unique_ptr synthetic children", "^std::unique_ptr<.+>(( )?&)?$",
stl_synth_flags, true);
- AddCXXSynthetic(
- cpp_category_sp,
- lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
- "std::shared_ptr synthetic children", "^std::shared_ptr<.+>(( )?&)?$",
- stl_synth_flags, true);
- AddCXXSynthetic(
- cpp_category_sp,
- lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator,
- "std::weak_ptr synthetic children", "^std::weak_ptr<.+>(( )?&)?$",
- stl_synth_flags, true);
AddCXXSynthetic(
cpp_category_sp,
lldb_private::formatters::LibStdcppTupleSyntheticFrontEndCreator,
@@ -1580,14 +1570,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
lldb_private::formatters::LibStdcppUniquePointerSummaryProvider,
"libstdc++ std::unique_ptr summary provider",
"^std::unique_ptr<.+>(( )?&)?$", stl_summary_flags, true);
- AddCXXSummary(cpp_category_sp,
- lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
- "libstdc++ std::shared_ptr summary provider",
- "^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true);
- AddCXXSummary(cpp_category_sp,
- lldb_private::formatters::LibStdcppSmartPointerSummaryProvider,
- "libstdc++ std::weak_ptr summary provider",
- "^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true);
AddCXXSummary(cpp_category_sp,
lldb_private::formatters::StdlibCoroutineHandleSummaryProvider,
"libstdc++ std::coroutine_handle summary provider",
@@ -1611,6 +1593,10 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
.SetDontShowValue(false)
.SetShowMembersOneLiner(false)
.SetHideItemNames(false);
+ SyntheticChildren::Flags stl_synth_flags;
+ stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
+ false);
+
using StringElementType = StringPrinter::StringElementType;
RegisterStdStringSummaryProvider(
@@ -1636,6 +1622,36 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
return LibStdcppStringSummaryProvider(valobj, stream, options);
},
"MSVC STL/libstdc++ std::wstring summary provider"));
+
+ auto smart_ptr_creator =
+ [](CXXSyntheticChildren *children,
+ ValueObjectSP valobj_sp) -> SyntheticChildrenFrontEnd * {
+ if (!valobj_sp)
+ return nullptr;
+
+ if (auto *msvc = MsvcStlSmartPointerSyntheticFrontEndCreator(valobj_sp))
+ return msvc;
+
+ return LibStdcppSharedPtrSyntheticFrontEndCreator(children, valobj_sp);
+ };
+ AddCXXSynthetic(cpp_category_sp, smart_ptr_creator,
+ "std::shared_ptr synthetic children",
+ "^std::shared_ptr<.+>(( )?&)?$", stl_synth_flags, true);
+ AddCXXSynthetic(cpp_category_sp, smart_ptr_creator,
+ "std::weak_ptr synthetic children",
+ "^std::weak_ptr<.+>(( )?&)?$", stl_synth_flags, true);
+
+ auto smart_ptr_summary = [](ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options) {
+ return MsvcStlSmartPointerSummaryProvider(valobj, stream, options) ||
+ LibStdcppSmartPointerSummaryProvider(valobj, stream, options);
+ };
+ AddCXXSummary(cpp_category_sp, smart_ptr_summary,
+ "MSVC STL/libstdc++ std::shared_ptr summary provider",
+ "^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp, smart_ptr_summary,
+ "MSVC STL/libstdc++ std::weak_ptr summary provider",
+ "^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true);
}
static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/Generic.cpp b/lldb/source/Plugins/Language/CPlusPlus/Generic.cpp
index b237a8a27090c..a6f8645fa41a1 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/Generic.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/Generic.cpp
@@ -16,7 +16,7 @@ lldb::ValueObjectSP lldb_private::formatters::GetDesugaredSmartPointerValue(
auto arg = container_type.GetTypeTemplateArgument(0);
if (!arg)
- return nullptr;
+ return ptr.GetSP(); // FIXME: PDB doesn't have info about template arguments
return ptr.Cast(arg.GetPointerType());
}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index e4ed923033aa7..edf3f4e8a5387 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -29,6 +29,14 @@ bool MsvcStlWStringSummaryProvider(
ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options); // VC 2015+ std::wstring
+// MSVC STL std::shared_ptr<> and std::weak_ptr<>
+bool IsMsvcStlSmartPointer(ValueObject &valobj);
+bool MsvcStlSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+
+lldb_private::SyntheticChildrenFrontEnd *
+MsvcStlSmartPointerSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
+
} // namespace formatters
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp
new file mode 100644
index 0000000000000..0dc35be768ad4
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp
@@ -0,0 +1,169 @@
+//===-- MsvcStlSmartPointer.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Generic.h"
+#include "MsvcStl.h"
+
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+
+using namespace lldb;
+
+bool lldb_private::formatters::IsMsvcStlSmartPointer(ValueObject &valobj) {
+ std::vector<uint32_t> indexes;
+ return valobj.GetCompilerType().GetIndexOfChildMemberWithName("_Ptr", true,
+ indexes) > 0;
+}
+
+bool lldb_private::formatters::MsvcStlSmartPointerSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
+ if (!valobj_sp)
+ return false;
+
+ ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("_Ptr"));
+ ValueObjectSP ctrl_sp(valobj_sp->GetChildMemberWithName("_Rep"));
+ if (!ctrl_sp || !ptr_sp)
+ return false;
+
+ DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
+
+ bool success;
+ uint64_t ctrl_addr = ctrl_sp->GetValueAsUnsigned(0, &success);
+ // Empty control field (expired)
+ if (!success || ctrl_addr == 0)
+ return true;
+
+ uint64_t uses = 0;
+ if (auto uses_sp = ctrl_sp->GetChildMemberWithName("_Uses")) {
+ bool success;
+ uses = uses_sp->GetValueAsUnsigned(0, &success);
+ if (!success)
+ return false;
+
+ stream.Printf(" strong=%" PRIu64, uses);
+ }
+
+ // _Weaks is the number of weak references - (_Uses != 0).
+ if (auto weak_count_sp = ctrl_sp->GetChildMemberWithName("_Weaks")) {
+ bool success;
+ uint64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);
+ if (!success)
+ return false;
+
+ stream.Printf(" weak=%" PRIu64, count - (uses != 0));
+ }
+
+ return true;
+}
+
+namespace lldb_private {
+namespace formatters {
+
+class MsvcStlSmartPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ MsvcStlSmartPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ llvm::Expected<uint32_t> CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
+
+ lldb::ChildCacheState Update() override;
+
+ llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
+
+ ~MsvcStlSmartPointerSyntheticFrontEnd() override;
+
+private:
+ ValueObject *m_ptr_obj = nullptr;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::
+ MsvcStlSmartPointerSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ if (valobj_sp)
+ Update();
+}
+
+llvm::Expected<uint32_t> lldb_private::formatters::
+ MsvcStlSmartPointerSyntheticFrontEnd::CalculateNumChildren() {
+ return (m_ptr_obj ? 1 : 0);
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::GetChildAtIndex(
+ uint32_t idx) {
+ if (!m_ptr_obj)
+ return lldb::ValueObjectSP();
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return lldb::ValueObjectSP();
+
+ if (idx == 0)
+ return m_ptr_obj->GetSP();
+
+ if (idx == 1) {
+ Status status;
+ ValueObjectSP value_sp = m_ptr_obj->Dereference(status);
+ if (status.Success())
+ return value_sp;
+ }
+
+ return lldb::ValueObjectSP();
+}
+
+lldb::ChildCacheState
+lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::Update() {
+ m_ptr_obj = nullptr;
+
+ ValueObjectSP valobj_sp = m_backend.GetSP();
+ if (!valobj_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ auto ptr_obj_sp = valobj_sp->GetChildMemberWithName("_Ptr");
+ if (!ptr_obj_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ auto cast_ptr_sp = GetDesugaredSmartPointerValue(*ptr_obj_sp, *valobj_sp);
+ if (!cast_ptr_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ m_ptr_obj = cast_ptr_sp->Clone(ConstString("pointer")).get();
+ return lldb::ChildCacheState::eRefetch;
+}
+
+llvm::Expected<size_t>
+lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (name == "_Ptr" || name == "pointer")
+ return 0;
+
+ if (name == "object" || name == "$$dereference$$")
+ return 1;
+
+ return llvm::createStringError("Type has no child named '%s'",
+ name.AsCString());
+}
+
+lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEnd::
+ ~MsvcStlSmartPointerSyntheticFrontEnd() = default;
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+
+ if (!IsMsvcStlSmartPointer(*valobj_sp))
+ return nullptr;
+
+ return new MsvcStlSmartPointerSyntheticFrontEnd(valobj_sp);
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py
index 3d8569da0332e..b52227b3611ab 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/shared_ptr/TestDataFormatterStdSharedPtr.py
@@ -118,3 +118,9 @@ def test_libcxx(self):
def test_libstdcxx(self):
self.build(dictionary={"USE_LIBSTDCPP": 1})
self.do_test()
+
+ @add_test_categories(["msvcstl"])
+ def test_msvcstl(self):
+ # No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
+ self.build(dictionary={})
+ self.do_test()
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
ad7fbd6
to
8894b65
Compare
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default. | ||
self.build(dictionary={}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default. | |
self.build(dictionary={}) | |
# No flags, because the "msvcstl" category checks that the MSVC STL is used by default. | |
self.build() |
@@ -16,7 +16,7 @@ lldb::ValueObjectSP lldb_private::formatters::GetDesugaredSmartPointerValue( | |||
|
|||
auto arg = container_type.GetTypeTemplateArgument(0); | |||
if (!arg) | |||
return nullptr; | |||
return ptr.GetSP(); // FIXME: PDB doesn't have info about template arguments |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of adding a workaround here can we just not call this at all in MsvcStlSmartPointer.cpp
? And add a FIXME
there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah but this only is a problem with PDB. So for MSVC STL+DWARF this would pass (which I guess is what the bots are running?).
I don't think we need the FIXME in that case tbh. I think it's reasonable to say that if we don't have enough debug-info to look at the template argument, then let the pointer type be whatever we got from debug-info. Worth adding a comment about that though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you confirm what configuration the bots are running though? Are we testing DWARF and PDB somewhere? Or just one of them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you confirm what configuration the bots are running though?
The API tests always run with DWARF: Makefile.rules. That's why it's a bit more complicated to add a PDB setting there - some tests would break with PDB.
Are we testing DWARF and PDB somewhere?
Not in API tests. The shell tests do use PDB (when passing --compiler=clang-cl
), because /Z7
is passed in build.py
. msstl_smoke.cpp
does this, for example.
@@ -1636,6 +1622,36 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { | |||
return LibStdcppStringSummaryProvider(valobj, stream, options); | |||
}, | |||
"MSVC STL/libstdc++ std::wstring summary provider")); | |||
|
|||
auto smart_ptr_creator = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These registrations are getting a bit noisy in my opinion. Could we move all this to a separate helper called RegisterCommonStlSmartPtrFormatters
(or something like that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be a problem for the other types as well. What about having a Common{Type}SyntheticFrontEndCreator
and Common{Type}SummaryProvider
in Generic.h
(or maybe a new header)?
std::vector<uint32_t> indexes; | ||
return valobj.GetCompilerType().GetIndexOfChildMemberWithName("_Ptr", true, | ||
indexes) > 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::vector<uint32_t> indexes; | |
return valobj.GetCompilerType().GetIndexOfChildMemberWithName("_Ptr", true, | |
indexes) > 0; | |
return valobj.GetChildMemberWithName("_Ptr") != nullptr; |
?
This PR adds formatters for
std::shared_ptr
andstd::weak_ptr
. They are similar to the ones from libc++ and libstdc++.Section from MSVC STL NatVis.
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 withUser
andstd::string
withstd::basic_string<char,std::char_traits<char>,std::allocator<char> >
.Towards #24834.