https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/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. >From 905944e6251c938a799aa881e40ef9a9ce576bab Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Fri, 11 Jul 2025 16:21:21 +0200 Subject: [PATCH] [LLDB] Add formatters for MSVC STL std::unique_ptr --- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 31 ++++- .../Plugins/Language/CPlusPlus/MsvcStl.h | 8 ++ .../CPlusPlus/MsvcStlSmartPointer.cpp | 115 ++++++++++++++++++ .../TestDataFormatterStdUniquePtr.py | 11 ++ 4 files changed, 161 insertions(+), 4 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 2db3e6f0ca315..9a869f3ea0289 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1566,10 +1566,6 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "std::optional synthetic child", "^std::optional<.+>(( )?&)?$", stl_deref_flags, true); - AddCXXSummary(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::StdlibCoroutineHandleSummaryProvider, "libstdc++ std::coroutine_handle summary provider", @@ -1599,6 +1595,24 @@ GenericSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream, return LibStdcppSmartPointerSummaryProvider(valobj, stream, options); } +static lldb_private::SyntheticChildrenFrontEnd * +GenericUniquePtrSyntheticFrontEndCreator(CXXSyntheticChildren *children, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + + if (IsMsvcStlUniquePtr(*valobj_sp)) + return MsvcStlUniquePtrSyntheticFrontEndCreator(valobj_sp); + return LibStdcppUniquePtrSyntheticFrontEndCreator(children, valobj_sp); +} + +static bool GenericUniquePtrSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + if (IsMsvcStlUniquePtr(valobj)) + return MsvcStlUniquePtrSummaryProvider(valobj, stream, options); + return LibStdcppUniquePointerSummaryProvider(valobj, stream, options); +} + /// Load formatters that are formatting types from more than one STL static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) @@ -1642,12 +1656,18 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { }, "MSVC STL/libstdc++ std::wstring summary provider")); + stl_summary_flags.SetDontShowChildren(false); + stl_summary_flags.SetSkipPointers(false); + AddCXXSynthetic(cpp_category_sp, GenericSmartPointerSyntheticFrontEndCreator, "std::shared_ptr synthetic children", "^std::shared_ptr<.+>(( )?&)?$", stl_synth_flags, true); AddCXXSynthetic(cpp_category_sp, GenericSmartPointerSyntheticFrontEndCreator, "std::weak_ptr synthetic children", "^std::weak_ptr<.+>(( )?&)?$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, GenericUniquePtrSyntheticFrontEndCreator, + "std::unique_ptr synthetic children", + "^std::unique_ptr<.+>(( )?&)?$", stl_synth_flags, true); AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider, "MSVC STL/libstdc++ std::shared_ptr summary provider", @@ -1655,6 +1675,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider, "MSVC STL/libstdc++ std::weak_ptr summary provider", "^std::weak_ptr<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, GenericUniquePtrSummaryProvider, + "MSVC STL/libstdc++ std::unique_ptr summary provider", + "^std::unique_ptr<.+>(( )?&)?$", stl_summary_flags, true); } static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h index edf3f4e8a5387..fe75bf275f8e2 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -37,6 +37,14 @@ bool MsvcStlSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream, lldb_private::SyntheticChildrenFrontEnd * MsvcStlSmartPointerSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp); +// MSVC STL std::unique_ptr<> +bool IsMsvcStlUniquePtr(ValueObject &valobj); +bool MsvcStlUniquePtrSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + +lldb_private::SyntheticChildrenFrontEnd * +MsvcStlUniquePtrSyntheticFrontEndCreator(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 index b1aecc4b6611a..6f66540f3cba9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp @@ -84,6 +84,23 @@ class MsvcStlSmartPointerSyntheticFrontEnd : public SyntheticChildrenFrontEnd { ValueObject *m_ptr_obj = nullptr; }; +class MsvcStlUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + MsvcStlUniquePtrSyntheticFrontEnd(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; + +private: + lldb::ValueObjectSP m_value_ptr_sp; + lldb::ValueObjectSP m_deleter_sp; +}; + } // namespace formatters } // namespace lldb_private @@ -163,3 +180,101 @@ lldb_private::formatters::MsvcStlSmartPointerSyntheticFrontEndCreator( lldb::ValueObjectSP valobj_sp) { return new MsvcStlSmartPointerSyntheticFrontEnd(valobj_sp); } + +bool lldb_private::formatters::IsMsvcStlUniquePtr(ValueObject &valobj) { + if (auto valobj_sp = valobj.GetNonSyntheticValue()) + return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr; + + return false; +} + +bool lldb_private::formatters::MsvcStlUniquePtrSummaryProvider( + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); + if (!valobj_sp) + return false; + + ValueObjectSP ptr_sp(valobj_sp->GetChildAtNamePath({"_Mypair", "_Myval2"})); + if (!ptr_sp) + return false; + + DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options); + + return true; +} + +lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEnd:: + MsvcStlUniquePtrSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + MsvcStlUniquePtrSyntheticFrontEnd::CalculateNumChildren() { + if (m_value_ptr_sp) + return m_deleter_sp ? 2 : 1; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (!m_value_ptr_sp) + return lldb::ValueObjectSP(); + + if (idx == 0) + return m_value_ptr_sp; + + if (idx == 1) + return m_deleter_sp; + + if (idx == 2) { + Status status; + auto value_sp = m_value_ptr_sp->Dereference(status); + if (status.Success()) { + return value_sp; + } + } + + return lldb::ValueObjectSP(); +} + +lldb::ChildCacheState +lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEnd::Update() { + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return lldb::ChildCacheState::eRefetch; + + ValueObjectSP pair_sp = valobj_sp->GetChildMemberWithName("_Mypair"); + if (!pair_sp) + return lldb::ChildCacheState::eRefetch; + + if (auto value_ptr_sp = pair_sp->GetChildMemberWithName("_Myval2")) + m_value_ptr_sp = value_ptr_sp->Clone(ConstString("pointer")); + + // Only present if the deleter is non-empty + if (auto deleter_sp = pair_sp->GetChildMemberWithName("_Myval1")) + m_deleter_sp = deleter_sp->Clone(ConstString("deleter")); + + return lldb::ChildCacheState::eRefetch; +} + +llvm::Expected<size_t> +lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (name == "pointer") + return 0; + if (name == "deleter") + return 1; + if (name == "obj" || name == "object" || name == "$$dereference$$") + return 2; + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::MsvcStlUniquePtrSyntheticFrontEndCreator( + lldb::ValueObjectSP valobj_sp) { + return new MsvcStlUniquePtrSyntheticFrontEnd(valobj_sp); +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py index 1b204c7d6ef02..0b68b1b532bb0 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py @@ -102,6 +102,12 @@ def test_libcxx(self): self.build(dictionary={"USE_LIBCPP": 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() + self.do_test() + def do_test_recursive_unique_ptr(self): # Tests that LLDB can handle when we have a loop in the unique_ptr # reference chain and that it correctly handles the different options @@ -155,3 +161,8 @@ def test_recursive_unique_ptr_libstdcxx(self): def test_recursive_unique_ptr_libcxx(self): self.build(dictionary={"USE_LIBCPP": 1}) self.do_test_recursive_unique_ptr() + + @add_test_categories(["msvcstl"]) + def test_recursive_unique_ptr_msvcstl(self): + self.build() + self.do_test_recursive_unique_ptr() _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits