https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/150097
>From ee9043309a33595e312bd7ee9ad0b05e164e4fe7 Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Tue, 22 Jul 2025 17:52:21 +0200 Subject: [PATCH] [LLDB] Add formatters for MSVC STL std::deque --- .../Plugins/Language/CPlusPlus/CMakeLists.txt | 1 + .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 28 ++- .../Plugins/Language/CPlusPlus/MsvcStl.h | 6 + .../Language/CPlusPlus/MsvcStlDeque.cpp | 174 ++++++++++++++++++ .../deque/TestDataFormatterGenericDeque.py | 32 ++-- 5 files changed, 225 insertions(+), 16 deletions(-) create mode 100644 lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index c10aa7482594c..cbc6f149ac303 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -35,6 +35,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LibStdcppUniquePointer.cpp MsvcStl.cpp MsvcStlAtomic.cpp + MsvcStlDeque.cpp MsvcStlSmartPointer.cpp MsvcStlTree.cpp MsvcStlTuple.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 4f62999adc1a7..e69f2f677e9ab 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1414,7 +1414,7 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::(__debug)?deque<.+>(( )?&)?$", eFormatterMatchRegex, + "^std::__debug::deque<.+>(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_deref_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider"))); @@ -1472,10 +1472,9 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "libstdc++ debug std::set summary provider", "^std::__debug::set<.+> >(( )?&)?$", stl_summary_flags, true); - AddCXXSummary( - cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::deque summary provider", - "^std::(__debug::)?deque<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "libstdc++ debug std::deque summary provider", + "^std::__debug::deque<.+>(( )?&)?$", stl_summary_flags, true); AddCXXSummary( cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, @@ -1684,6 +1683,18 @@ GenericMapLikeSyntheticFrontEndCreator(CXXSyntheticChildren *children, "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider", *valobj_sp); } +static SyntheticChildrenFrontEnd * +GenericDequeSyntheticFrontEndCreator(CXXSyntheticChildren *children, + ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + + if (IsMsvcStlDeque(*valobj_sp)) + return MsvcStlDequeSyntheticFrontEndCreator(children, valobj_sp); + return new ScriptedSyntheticChildren::FrontEnd( + "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider", *valobj_sp); +} + /// Load formatters that are formatting types from more than one STL static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { if (!cpp_category_sp) @@ -1761,6 +1772,10 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSynthetic(cpp_category_sp, GenericOptionalSyntheticFrontEndCreator, "std::optional synthetic children", "^std::optional<.+>(( )?&)?$", stl_deref_flags, true); + AddCXXSynthetic(cpp_category_sp, GenericDequeSyntheticFrontEndCreator, + "std::deque container synthetic children", + "^std::deque<.+>(( )?&)?$", stl_deref_flags, true); + AddCXXSynthetic(cpp_category_sp, GenericMapLikeSyntheticFrontEndCreator, "std::(multi)?map/set synthetic children", "^std::(multi)?(map|set)<.+>(( )?&)?$", stl_synth_flags, @@ -1806,6 +1821,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "MSVC STL/libstdc++ std::(multi)?map/set summary provider", "^std::(multi)?(map|set)<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "MSVC STL/libstd++ std::deque summary provider", + "^std::deque<.+>(( )?&)?$", 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 cce22a336c37b..490794ccbfb53 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -103,6 +103,12 @@ bool IsMsvcStlMapLike(ValueObject &valobj); lldb_private::SyntheticChildrenFrontEnd * MsvcStlMapLikeSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp); +// MSVC STL std::deque<> +bool IsMsvcStlDeque(ValueObject &valobj); +SyntheticChildrenFrontEnd * +MsvcStlDequeSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp new file mode 100644 index 0000000000000..873354381a6da --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp @@ -0,0 +1,174 @@ +//===-- MsvcStlDeque.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 "MsvcStl.h" + +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" + +using namespace lldb; + +namespace lldb_private { +namespace formatters { + +class MsvcStlDequeSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + MsvcStlDequeSyntheticFrontEnd(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: + ValueObject *m_map = nullptr; + ExecutionContextRef m_exe_ctx_ref; + + size_t m_block_size = 0; + size_t m_offset = 0; + size_t m_map_size = 0; + + size_t m_element_size = 0; + CompilerType m_element_type; + + uint32_t m_size = 0; +}; + +} // namespace formatters +} // namespace lldb_private + +lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd:: + MsvcStlDequeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> lldb_private::formatters:: + MsvcStlDequeSyntheticFrontEnd::CalculateNumChildren() { + if (!m_map) + return llvm::createStringError("Failed to read size"); + return m_size; +} + +lldb::ValueObjectSP +lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::GetChildAtIndex( + uint32_t idx) { + if (idx >= m_size || !m_map) + return nullptr; + ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP()); + if (!process_sp) + return nullptr; + + // _EEN_DS = _Block_size + // _Map[(($i + _Myoff) / _EEN_DS) % _Mapsize][($i + _Myoff) % _EEN_DS] + size_t first_idx = ((idx + m_offset) / m_block_size) % m_map_size; + lldb::addr_t first_address = m_map->GetValueAsUnsigned(0) + + first_idx * process_sp->GetAddressByteSize(); + + Status err; + lldb::addr_t second_base = + process_sp->ReadPointerFromMemory(first_address, err); + if (err.Fail()) + return nullptr; + + size_t second_idx = (idx + m_offset) % m_block_size; + size_t second_address = second_base + second_idx * m_element_size; + + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromAddress(name.GetString(), second_address, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState +lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::Update() { + m_size = 0; + m_map = nullptr; + m_element_type.Clear(); + + auto storage_sp = m_backend.GetChildAtNamePath({"_Mypair", "_Myval2"}); + if (!storage_sp) + return lldb::eRefetch; + + auto deque_type = m_backend.GetCompilerType(); + if (!deque_type) + return lldb::eRefetch; + + auto block_size_decl = deque_type.GetStaticFieldWithName("_Block_size"); + if (!block_size_decl) + return lldb::eRefetch; + auto block_size = block_size_decl.GetConstantValue(); + if (!block_size.IsValid()) + return lldb::eRefetch; + + auto element_type = deque_type.GetTypeTemplateArgument(0); + if (!element_type) + return lldb::eRefetch; + auto element_size = element_type.GetByteSize(nullptr); + if (!element_size) + return lldb::eRefetch; + + auto offset_sp = storage_sp->GetChildMemberWithName("_Myoff"); + auto map_size_sp = storage_sp->GetChildMemberWithName("_Mapsize"); + auto map_sp = storage_sp->GetChildMemberWithName("_Map"); + auto size_sp = storage_sp->GetChildMemberWithName("_Mysize"); + if (!offset_sp || !map_size_sp || !map_sp || !size_sp) + return lldb::eRefetch; + + bool ok = false; + uint64_t offset = offset_sp->GetValueAsUnsigned(0, &ok); + if (!ok) + return lldb::eRefetch; + + uint64_t map_size = map_size_sp->GetValueAsUnsigned(0, &ok); + if (!ok) + return lldb::eRefetch; + + uint64_t size = size_sp->GetValueAsUnsigned(0, &ok); + if (!ok) + return lldb::eRefetch; + + m_map = map_sp.get(); + m_exe_ctx_ref = m_backend.GetExecutionContextRef(); + m_block_size = block_size.ULongLong(); + m_offset = offset; + m_map_size = map_size; + m_element_size = *element_size; + m_element_type = element_type; + m_size = size; + return lldb::eRefetch; +} + +llvm::Expected<size_t> lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd:: + GetIndexOfChildWithName(ConstString name) { + if (!m_map) + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + if (auto optional_idx = ExtractIndexFromString(name.GetCString())) + return *optional_idx; + + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); +} + +bool lldb_private::formatters::IsMsvcStlDeque(ValueObject &valobj) { + if (auto valobj_sp = valobj.GetNonSyntheticValue()) + return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr; + return false; +} + +lldb_private::SyntheticChildrenFrontEnd * +lldb_private::formatters::MsvcStlDequeSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return new MsvcStlDequeSyntheticFrontEnd(valobj_sp); +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py index f52f6f7ec3ffb..2332eff7b10dd 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py @@ -3,9 +3,6 @@ from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil -USE_LIBSTDCPP = "USE_LIBSTDCPP" -USE_LIBCPP = "USE_LIBCPP" - class GenericDequeDataFormatterTestCase(TestBase): def findVariable(self, name): @@ -56,8 +53,7 @@ def check_numbers(self, var_name, show_ptr=False): ], ) - def do_test(self, stdlib_type): - self.build(dictionary={stdlib_type: "1"}) + def do_test(self): (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint( self, "break here", lldb.SBFileSpec("main.cpp") ) @@ -135,15 +131,22 @@ def do_test(self, stdlib_type): @add_test_categories(["libstdcxx"]) def test_libstdcpp(self): - self.do_test(USE_LIBSTDCPP) + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test() @add_test_categories(["libc++"]) def test_libcpp(self): - self.do_test(USE_LIBCPP) + 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_ref_and_ptr(self, stdlib_type: str): + def do_test_ref_and_ptr(self): """Test formatting of std::deque& and std::deque*""" - self.build(dictionary={stdlib_type: "1"}) (self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( self, "stop here", lldb.SBFileSpec("main.cpp", False) ) @@ -157,8 +160,15 @@ def do_test_ref_and_ptr(self, stdlib_type: str): @add_test_categories(["libstdcxx"]) def test_libstdcpp_ref_and_ptr(self): - self.do_test_ref_and_ptr(USE_LIBSTDCPP) + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test_ref_and_ptr() @add_test_categories(["libc++"]) def test_libcpp_ref_and_ptr(self): - self.do_test_ref_and_ptr(USE_LIBCPP) + self.build(dictionary={"USE_LIBCPP": 1}) + self.do_test_ref_and_ptr() + + @add_test_categories(["msvcstl"]) + def test_msvcstl_ref_and_ptr(self): + self.build() + self.do_test_ref_and_ptr() _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits