llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: nerix (Nerixyz) <details> <summary>Changes</summary> This PR adds synthetic children for std::deque from MSVC's STL. Similar to libstdc++ and libc++, the elements are in a `T**`, so we need to "subscript" twice. The [NatVis for deque](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/debugger/STL.natvis#L1103-L1112) uses `_EEN_DS` which contains the block size. We can't access this, but we can access the [constexpr `_Block_size`](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/inc/deque#L641). Towards #<!-- -->24834. --- Full diff: https://github.com/llvm/llvm-project/pull/150097.diff 5 Files Affected: - (modified) lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt (+1) - (modified) lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp (+22-5) - (modified) lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h (+6) - (added) lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp (+176) - (modified) lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py (+21-11) ``````````diff diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index ab9d991fd48f7..81ad7afe4e305 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 MsvcStlTuple.cpp MsvcStlUnordered.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 80dc4609f9b66..c749bf682391a 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++ 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, @@ -1672,6 +1671,18 @@ GenericUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *children, *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) @@ -1743,6 +1754,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "std::unordered container synthetic children", "^std::unordered_(multi)?(map|set)<.+> ?>$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, GenericDequeSyntheticFrontEndCreator, + "std::deque container synthetic children", + "^std::deque<.+>(( )?&)?$", stl_synth_flags, true); SyntheticChildren::Flags stl_deref_flags = stl_synth_flags; stl_deref_flags.SetFrontEndWantsDereference(); @@ -1786,6 +1800,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "MSVC STL/libstdc++ std unordered container summary provider", "^std::unordered_(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 e2a015a537868..f93d03d2a6230 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -92,6 +92,12 @@ SyntheticChildrenFrontEnd * MsvcStlUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *, 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..08467a8ae4eb6 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp @@ -0,0 +1,176 @@ +//===-- 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().GetNonReferenceType(); + if (deque_type.IsPointerType()) + deque_type = deque_type.GetPointeeType(); + 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() `````````` </details> https://github.com/llvm/llvm-project/pull/150097 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits