https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/148285
>From a341b83b07f8ccb50036b2ab09c29d1c5946bd19 Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Fri, 11 Jul 2025 22:06:47 +0200 Subject: [PATCH 1/3] [LLDB] Add formatters for MSVC STL std::(forward_)list --- lldb/examples/synthetic/gnu_libstdcpp.py | 9 - .../Plugins/Language/CPlusPlus/CMakeLists.txt | 2 +- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 62 +++- .../{LibCxxList.cpp => GenericList.cpp} | 294 +++++++++++++++--- .../Plugins/Language/CPlusPlus/MsvcStl.h | 9 + .../TestDataFormatterGenericForwardList.py | 33 +- .../list/TestDataFormatterGenericList.py | 35 ++- .../loop/TestDataFormatterGenericListLoop.py | 18 +- .../generic/list/loop/main.cpp | 5 - 9 files changed, 356 insertions(+), 111 deletions(-) rename lldb/source/Plugins/Language/CPlusPlus/{LibCxxList.cpp => GenericList.cpp} (58%) diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py index f42a009c21f48..2996b9ed0fa40 100644 --- a/lldb/examples/synthetic/gnu_libstdcpp.py +++ b/lldb/examples/synthetic/gnu_libstdcpp.py @@ -6,15 +6,6 @@ # thing for your setup -def ForwardListSummaryProvider(valobj, dict): - list_capping_size = valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay() - text = "size=" + str(valobj.GetNumChildren()) - if valobj.GetNumChildren() > list_capping_size: - return "(capped) " + text - else: - return text - - def StdOptionalSummaryProvider(valobj, dict): has_value = valobj.GetNumChildren() > 0 # We add wrapping spaces for consistency with the libcxx formatter diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index 8ee6e2a246c55..5905d9b9a6d03 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -14,11 +14,11 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN CxxStringTypes.cpp Generic.cpp GenericBitset.cpp + GenericList.cpp GenericOptional.cpp LibCxx.cpp LibCxxAtomic.cpp LibCxxInitializerList.cpp - LibCxxList.cpp LibCxxMap.cpp LibCxxQueue.cpp LibCxxRangesRefView.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 4a3fdede84d32..9ac2b93b8a31e 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1440,14 +1440,12 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_deref_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::((__debug::)?|(__cxx11::)?)list<.+>(( )?&)?$", - eFormatterMatchRegex, + "^std::__(debug|cxx11)::list<.+>(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_deref_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); cpp_category_sp->AddTypeSynthetic( - "^std::((__debug::)?|(__cxx11::)?)forward_list<.+>(( )?&)?$", - eFormatterMatchRegex, + "^std::__(debug|cxx11)::forward_list<.+>(( )?&)?$", eFormatterMatchRegex, SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider"))); @@ -1501,18 +1499,15 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "^std::(__debug::)?unordered_(multi)?(map|set)<.+> >$", stl_summary_flags, true); - AddCXXSummary(cpp_category_sp, - lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::list summary provider", - "^std::((__debug::)?|(__cxx11::)?)list<.+>(( )?&)?$", - stl_summary_flags, true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, + "libstdc++ std::list summary provider", + "^std::__(debug|cxx11)::list<.+>(( )?&)?$", stl_summary_flags, true); - cpp_category_sp->AddTypeSummary( - "^std::((__debug::)?|(__cxx11::)?)forward_list<.+>(( )?&)?$", - eFormatterMatchRegex, - TypeSummaryImplSP(new ScriptSummaryFormat( - stl_summary_flags, - "lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider"))); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "libstdc++ std::forward_list summary provider", + "^std::__(debug|cxx11)::forward_list<.+>(( )?&)?$", + stl_summary_flags, true); AddCXXSummary(cpp_category_sp, LibStdcppVariantSummaryProvider, "libstdc++ std::variant summary provider", "^std::variant<.+>$", stl_summary_flags, true); @@ -1627,6 +1622,31 @@ GenericVectorSyntheticFrontEndCreator(CXXSyntheticChildren *children, "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider", *valobj_sp); } +static SyntheticChildrenFrontEnd * +GenericListSyntheticFrontEndCreator(CXXSyntheticChildren *children, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + + if (IsMsvcStlList(*valobj_sp)) + return MsvcStlListSyntheticFrontEndCreator(children, valobj_sp); + return new ScriptedSyntheticChildren::FrontEnd( + "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider", *valobj_sp); +} + +static SyntheticChildrenFrontEnd * +GenericForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *children, + lldb::ValueObjectSP valobj_sp) { + if (!valobj_sp) + return nullptr; + + if (IsMsvcStlList(*valobj_sp)) + return MsvcStlForwardListSyntheticFrontEndCreator(children, valobj_sp); + return new ScriptedSyntheticChildren::FrontEnd( + "lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider", + *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) @@ -1685,6 +1705,12 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSynthetic(cpp_category_sp, GenericTupleSyntheticFrontEndCreator, "std::tuple synthetic children", "^std::tuple<.*>(( )?&)?$", stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, GenericListSyntheticFrontEndCreator, + "std::list synthetic children", "^std::list<.+>(( )?&)?$", + stl_synth_flags, true); + AddCXXSynthetic(cpp_category_sp, GenericForwardListSyntheticFrontEndCreator, + "std::forward_list synthetic children", + "^std::forward_list<.+>(( )?&)?$", stl_synth_flags, true); AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider, "MSVC STL/libstdc++ std::shared_ptr summary provider", @@ -1704,6 +1730,12 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSynthetic(cpp_category_sp, GenericVectorSyntheticFrontEndCreator, "MSVC/libstdc++ std::vector synthetic provider", "^std::vector<.+>(( )?&)?$", stl_synth_flags, true); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "MSVC STL/libstdc++ std::list summary provider", + "^std::list<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, + "MSVC STL/libstdc++ std::forward_list summary provider", + "^std::forward_list<.+>(( )?&)?$", stl_summary_flags, true); } static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp similarity index 58% rename from lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp rename to lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp index 826e6ab090e10..ea1edbfd3ac9b 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp @@ -1,4 +1,4 @@ -//===-- LibCxxList.cpp ----------------------------------------------------===// +//===-- GenericList.cpp ---------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,14 +7,11 @@ //===----------------------------------------------------------------------===// #include "LibCxx.h" +#include "MsvcStl.h" -#include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/Target/Target.h" -#include "lldb/Utility/DataBufferHeap.h" -#include "lldb/Utility/Endian.h" #include "lldb/Utility/Status.h" -#include "lldb/Utility/Stream.h" #include "lldb/ValueObject/ValueObject.h" #include "lldb/ValueObject/ValueObjectConstResult.h" #include "lldb/lldb-enumerations.h" @@ -25,31 +22,27 @@ using namespace lldb_private::formatters; namespace { -class ListEntry { +enum class StlType { + LibCxx, + MsvcStl, +}; + +template <StlType Stl> class ListEntry { public: ListEntry() = default; ListEntry(ValueObjectSP entry_sp) : m_entry_sp(std::move(entry_sp)) {} ListEntry(ValueObject *entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} - ListEntry next() { - if (!m_entry_sp) - return ListEntry(); - return ListEntry(m_entry_sp->GetChildMemberWithName("__next_")); - } - - ListEntry prev() { - if (!m_entry_sp) - return ListEntry(); - return ListEntry(m_entry_sp->GetChildMemberWithName("__prev_")); - } - uint64_t value() const { if (!m_entry_sp) return 0; return m_entry_sp->GetValueAsUnsigned(0); } + ListEntry next(); + ListEntry prev(); + bool null() { return (value() == 0); } explicit operator bool() { return GetEntry() && !null(); } @@ -66,10 +59,34 @@ class ListEntry { ValueObjectSP m_entry_sp; }; -class ListIterator { +template <> ListEntry<StlType::LibCxx> ListEntry<StlType::LibCxx>::next() { + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName("__next_")); +} + +template <> ListEntry<StlType::LibCxx> ListEntry<StlType::LibCxx>::prev() { + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName("__prev_")); +} + +template <> ListEntry<StlType::MsvcStl> ListEntry<StlType::MsvcStl>::next() { + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName("_Next")); +} + +template <> ListEntry<StlType::MsvcStl> ListEntry<StlType::MsvcStl>::prev() { + if (!m_entry_sp) + return ListEntry(); + return ListEntry(m_entry_sp->GetChildMemberWithName("_Prev")); +} + +template <StlType Stl> class ListIterator { public: ListIterator() = default; - ListIterator(ListEntry entry) : m_entry(std::move(entry)) {} + ListIterator(ListEntry<Stl> entry) : m_entry(std::move(entry)) {} ListIterator(ValueObjectSP entry) : m_entry(std::move(entry)) {} ListIterator(ValueObject *entry) : m_entry(entry) {} @@ -101,9 +118,10 @@ class ListIterator { void prev() { m_entry = m_entry.prev(); } private: - ListEntry m_entry; + ListEntry<Stl> m_entry; }; +template <StlType Stl> class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { public: llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override { @@ -124,33 +142,31 @@ class AbstractListFrontEnd : public SyntheticChildrenFrontEnd { ValueObject *m_head = nullptr; static constexpr bool g_use_loop_detect = true; - size_t m_loop_detected = 0; // The number of elements that have had loop - // detection run over them. - ListEntry m_slow_runner; // Used for loop detection - ListEntry m_fast_runner; // Used for loop detection + size_t m_loop_detected = 0; // The number of elements that have had loop + // detection run over them. + ListEntry<Stl> m_slow_runner; // Used for loop detection + ListEntry<Stl> m_fast_runner; // Used for loop detection size_t m_list_capping_size = 0; CompilerType m_element_type; - std::map<size_t, ListIterator> m_iterators; + std::map<size_t, ListIterator<Stl>> m_iterators; bool HasLoop(size_t count); ValueObjectSP GetItem(size_t idx); }; -class ForwardListFrontEnd : public AbstractListFrontEnd { +class LibCxxForwardListFrontEnd : public AbstractListFrontEnd<StlType::LibCxx> { public: - ForwardListFrontEnd(ValueObject &valobj); + LibCxxForwardListFrontEnd(ValueObject &valobj); llvm::Expected<uint32_t> CalculateNumChildren() override; ValueObjectSP GetChildAtIndex(uint32_t idx) override; lldb::ChildCacheState Update() override; }; -class ListFrontEnd : public AbstractListFrontEnd { +class LibCxxListFrontEnd : public AbstractListFrontEnd<StlType::LibCxx> { public: - ListFrontEnd(lldb::ValueObjectSP valobj_sp); - - ~ListFrontEnd() override = default; + LibCxxListFrontEnd(lldb::ValueObjectSP valobj_sp); llvm::Expected<uint32_t> CalculateNumChildren() override; @@ -163,9 +179,34 @@ class ListFrontEnd : public AbstractListFrontEnd { ValueObject *m_tail = nullptr; }; +class MsvcStlForwardListFrontEnd + : public AbstractListFrontEnd<StlType::MsvcStl> { +public: + MsvcStlForwardListFrontEnd(ValueObject &valobj); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + ValueObjectSP GetChildAtIndex(uint32_t idx) override; + lldb::ChildCacheState Update() override; +}; + +class MsvcStlListFrontEnd : public AbstractListFrontEnd<StlType::MsvcStl> { +public: + MsvcStlListFrontEnd(lldb::ValueObjectSP valobj_sp); + + llvm::Expected<uint32_t> CalculateNumChildren() override; + + lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override; + + lldb::ChildCacheState Update() override; + +private: + ValueObject *m_tail = nullptr; +}; + } // end anonymous namespace -lldb::ChildCacheState AbstractListFrontEnd::Update() { +template <StlType Stl> +lldb::ChildCacheState AbstractListFrontEnd<Stl>::Update() { m_loop_detected = 0; m_count = UINT32_MAX; m_head = nullptr; @@ -191,7 +232,7 @@ lldb::ChildCacheState AbstractListFrontEnd::Update() { return lldb::ChildCacheState::eRefetch; } -bool AbstractListFrontEnd::HasLoop(size_t count) { +template <StlType Stl> bool AbstractListFrontEnd<Stl>::HasLoop(size_t count) { if (!g_use_loop_detect) return false; // don't bother checking for a loop if we won't actually need to jump nodes @@ -201,7 +242,7 @@ bool AbstractListFrontEnd::HasLoop(size_t count) { if (m_loop_detected == 0) { // This is the first time we are being run (after the last update). Set up // the loop invariant for the first element. - m_slow_runner = ListEntry(m_head).next(); + m_slow_runner = ListEntry<Stl>(m_head).next(); m_fast_runner = m_slow_runner.next(); m_loop_detected = 1; } @@ -225,9 +266,10 @@ bool AbstractListFrontEnd::HasLoop(size_t count) { return m_slow_runner == m_fast_runner; } -ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) { +template <StlType Stl> +ValueObjectSP AbstractListFrontEnd<Stl>::GetItem(size_t idx) { size_t advance = idx; - ListIterator current(m_head); + ListIterator<Stl> current(m_head); if (idx > 0) { auto cached_iterator = m_iterators.find(idx - 1); if (cached_iterator != m_iterators.end()) { @@ -240,16 +282,16 @@ ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) { return value_sp; } -ForwardListFrontEnd::ForwardListFrontEnd(ValueObject &valobj) +LibCxxForwardListFrontEnd::LibCxxForwardListFrontEnd(ValueObject &valobj) : AbstractListFrontEnd(valobj) { Update(); } -llvm::Expected<uint32_t> ForwardListFrontEnd::CalculateNumChildren() { +llvm::Expected<uint32_t> LibCxxForwardListFrontEnd::CalculateNumChildren() { if (m_count != UINT32_MAX) return m_count; - ListEntry current(m_head); + ListEntry<StlType::LibCxx> current(m_head); m_count = 0; while (current && m_count < m_list_capping_size) { ++m_count; @@ -258,7 +300,7 @@ llvm::Expected<uint32_t> ForwardListFrontEnd::CalculateNumChildren() { return m_count; } -ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(uint32_t idx) { +ValueObjectSP LibCxxForwardListFrontEnd::GetChildAtIndex(uint32_t idx) { if (idx >= CalculateNumChildrenIgnoringErrors()) return nullptr; @@ -289,7 +331,7 @@ ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(uint32_t idx) { m_element_type); } -lldb::ChildCacheState ForwardListFrontEnd::Update() { +lldb::ChildCacheState LibCxxForwardListFrontEnd::Update() { AbstractListFrontEnd::Update(); Status err; @@ -312,13 +354,13 @@ lldb::ChildCacheState ForwardListFrontEnd::Update() { return ChildCacheState::eRefetch; } -ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp) +LibCxxListFrontEnd::LibCxxListFrontEnd(lldb::ValueObjectSP valobj_sp) : AbstractListFrontEnd(*valobj_sp) { if (valobj_sp) Update(); } -llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() { +llvm::Expected<uint32_t> LibCxxListFrontEnd::CalculateNumChildren() { if (m_count != UINT32_MAX) return m_count; if (!m_head || !m_tail || m_node_address == 0) @@ -351,7 +393,7 @@ llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() { if (next_val == prev_val) return 1; uint64_t size = 2; - ListEntry current(m_head); + ListEntry<StlType::LibCxx> current(m_head); while (current.next() && current.next().value() != m_node_address) { size++; current = current.next(); @@ -361,7 +403,7 @@ llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() { return m_count = (size - 1); } -lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(uint32_t idx) { +lldb::ValueObjectSP LibCxxListFrontEnd::GetChildAtIndex(uint32_t idx) { static ConstString g_value("__value_"); static ConstString g_next("__next_"); @@ -412,7 +454,7 @@ lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(uint32_t idx) { m_element_type); } -lldb::ChildCacheState ListFrontEnd::Update() { +lldb::ChildCacheState LibCxxListFrontEnd::Update() { AbstractListFrontEnd::Update(); m_tail = nullptr; m_node_address = 0; @@ -432,13 +474,167 @@ lldb::ChildCacheState ListFrontEnd::Update() { return lldb::ChildCacheState::eRefetch; } +MsvcStlForwardListFrontEnd::MsvcStlForwardListFrontEnd(ValueObject &valobj) + : AbstractListFrontEnd(valobj) { + Update(); +} + +llvm::Expected<uint32_t> MsvcStlForwardListFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + + ListEntry<StlType::MsvcStl> current(m_head); + m_count = 0; + while (current && m_count < m_list_capping_size) { + ++m_count; + current = current.next(); + } + return m_count; +} + +ValueObjectSP MsvcStlForwardListFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= CalculateNumChildrenIgnoringErrors()) + return nullptr; + + if (!m_head) + return nullptr; + + if (HasLoop(idx + 1)) + return nullptr; + + ValueObjectSP current_sp = GetItem(idx); + if (!current_sp) + return nullptr; + + current_sp = current_sp->GetChildAtIndex(1); // get the _Myval child + if (!current_sp) + return nullptr; + + // we need to copy current_sp into a new object otherwise we will end up with + // all items named _Myval + DataExtractor data; + Status error; + current_sp->GetData(data, error); + if (error.Fail()) + return nullptr; + + return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState MsvcStlForwardListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + + if (auto head_sp = + m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myhead"})) + m_head = head_sp.get(); + + return ChildCacheState::eRefetch; +} + +MsvcStlListFrontEnd::MsvcStlListFrontEnd(lldb::ValueObjectSP valobj_sp) + : AbstractListFrontEnd(*valobj_sp) { + if (valobj_sp) + Update(); +} + +llvm::Expected<uint32_t> MsvcStlListFrontEnd::CalculateNumChildren() { + if (m_count != UINT32_MAX) + return m_count; + if (!m_head || !m_tail) + return 0; + + auto size_sp = + m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Mysize"}); + if (!size_sp) + return llvm::createStringError("Failed to resolve size."); + + m_count = size_sp->GetValueAsUnsigned(UINT32_MAX); + if (m_count == UINT32_MAX) + return llvm::createStringError("Failed to read size value."); + + return m_count; +} + +lldb::ValueObjectSP MsvcStlListFrontEnd::GetChildAtIndex(uint32_t idx) { + if (idx >= CalculateNumChildrenIgnoringErrors()) + return lldb::ValueObjectSP(); + + if (!m_head || !m_tail) + return lldb::ValueObjectSP(); + + if (HasLoop(idx + 1)) + return lldb::ValueObjectSP(); + + ValueObjectSP current_sp = GetItem(idx); + if (!current_sp) + return lldb::ValueObjectSP(); + + current_sp = current_sp->GetChildAtIndex(2); // get the _Myval child + if (!current_sp) + return lldb::ValueObjectSP(); + + // we need to copy current_sp into a new object otherwise we will end up with + // all items named _Myval + DataExtractor data; + Status error; + current_sp->GetData(data, error); + if (error.Fail()) + return lldb::ValueObjectSP(); + + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + return CreateValueObjectFromData(name.GetString(), data, + m_backend.GetExecutionContextRef(), + m_element_type); +} + +lldb::ChildCacheState MsvcStlListFrontEnd::Update() { + AbstractListFrontEnd::Update(); + m_tail = nullptr; + m_head = nullptr; + + ValueObjectSP last = + m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myhead"}); + if (!last) + return lldb::ChildCacheState::eRefetch; + ValueObjectSP first = last->GetChildMemberWithName("_Next"); + if (!first) + return lldb::ChildCacheState::eRefetch; + + m_head = first.get(); + m_tail = last.get(); + + return lldb::ChildCacheState::eRefetch; +} + SyntheticChildrenFrontEnd *formatters::LibcxxStdListSyntheticFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { - return (valobj_sp ? new ListFrontEnd(valobj_sp) : nullptr); + return (valobj_sp ? new LibCxxListFrontEnd(valobj_sp) : nullptr); } SyntheticChildrenFrontEnd * formatters::LibcxxStdForwardListSyntheticFrontEndCreator( CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { - return valobj_sp ? new ForwardListFrontEnd(*valobj_sp) : nullptr; + return valobj_sp ? new LibCxxForwardListFrontEnd(*valobj_sp) : nullptr; +} + +bool formatters::IsMsvcStlList(ValueObject &valobj) { + if (auto valobj_sp = valobj.GetNonSyntheticValue()) + return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr; + + return false; +} + +SyntheticChildrenFrontEnd * +formatters::MsvcStlListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new MsvcStlListFrontEnd(valobj_sp) : nullptr); +} + +SyntheticChildrenFrontEnd * +formatters::MsvcStlForwardListSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + return valobj_sp ? new MsvcStlForwardListFrontEnd(*valobj_sp) : nullptr; } diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h index 81397851b6010..0f3db4b50eeaf 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h +++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h @@ -56,6 +56,15 @@ bool IsMsvcStlVector(ValueObject &valobj); lldb_private::SyntheticChildrenFrontEnd * MsvcStlVectorSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp); +// MSVC STL std::list and std::forward_list +bool IsMsvcStlList(ValueObject &valobj); +SyntheticChildrenFrontEnd * +MsvcStlForwardListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); +SyntheticChildrenFrontEnd * +MsvcStlListSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp); + } // namespace formatters } // namespace lldb_private diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py index f63f8fe1d6a62..45695c43b42a9 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py @@ -7,9 +7,6 @@ from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil -USE_LIBSTDCPP = "USE_LIBSTDCPP" -USE_LIBCPP = "USE_LIBCPP" - class TestDataFormatterGenericForwardList(TestBase): def setUp(self): @@ -17,9 +14,8 @@ def setUp(self): self.line = line_number("main.cpp", "// break here") self.namespace = "std" - def do_test(self, stdlib_type): + def do_test(self): """Test that std::forward_list is displayed correctly""" - self.build(dictionary={stdlib_type: "1"}) lldbutil.run_to_source_breakpoint( self, "// break here", lldb.SBFileSpec("main.cpp", False) ) @@ -76,10 +72,8 @@ def do_test(self, stdlib_type): substrs=["size=24", "[0]", "[1]", "[2]", "..."], ) - def do_test_ptr_and_ref(self, stdlib_type): + def do_test_ptr_and_ref(self): """Test that ref and ptr to std::forward_list is displayed correctly""" - self.build(dictionary={stdlib_type: "1"}) - (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint( self, "Check ref and ptr", lldb.SBFileSpec("main.cpp", False) ) @@ -158,16 +152,31 @@ def do_test_ptr_and_ref(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(["libstdcxx"]) def test_ptr_and_ref_libstdcpp(self): - self.do_test_ptr_and_ref(USE_LIBSTDCPP) + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test_ptr_and_ref() @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(["libc++"]) def test_ptr_and_ref_libcpp(self): - self.do_test_ptr_and_ref(USE_LIBCPP) + self.build(dictionary={"USE_LIBCPP": 1}) + self.do_test_ptr_and_ref() + + @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() + + @add_test_categories(["msvcstl"]) + def test_ptr_and_ref_msvcstl(self): + self.build() + self.do_test_ptr_and_ref() diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py index 78c93b1e3caea..c0207e6ab5911 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py @@ -8,9 +8,6 @@ from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil -USE_LIBSTDCPP = "USE_LIBSTDCPP" -USE_LIBCPP = "USE_LIBCPP" - class GenericListDataFormatterTestCase(TestBase): def setUp(self): @@ -25,9 +22,8 @@ def setUp(self): "main.cpp", "// Set final break point at this line." ) - def do_test_with_run_command(self, stdlib_type): + def do_test_with_run_command(self, *, is_libstdcpp=False): """Test that that file and class static variables display correctly.""" - self.build(dictionary={stdlib_type: "1"}) self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) lldbutil.run_break_set_by_file_and_line( @@ -62,7 +58,7 @@ def cleanup(): "frame variable numbers_list --raw", matching=False, substrs=["size=0"] ) - if stdlib_type == USE_LIBSTDCPP: + if is_libstdcpp: self.expect( "frame variable &numbers_list._M_impl._M_node --raw", matching=False, @@ -230,10 +226,8 @@ def cleanup(): "text_list.MightHaveChildren() says False for non empty!", ) - def do_test_ptr_and_ref(self, stdlib_type): + def do_test_ptr_and_ref(self): """Test that ref and ptr to std::list is displayed correctly""" - self.build(dictionary={stdlib_type: "1"}) - (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint( self, "Check ref and ptr", lldb.SBFileSpec("main.cpp", False) ) @@ -302,16 +296,31 @@ def do_test_ptr_and_ref(self, stdlib_type): @add_test_categories(["libstdcxx"]) def test_with_run_command_libstdcpp(self): - self.do_test_with_run_command(USE_LIBSTDCPP) + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test_with_run_command(is_libstdcpp=True) @add_test_categories(["libstdcxx"]) def test_ptr_and_ref_libstdcpp(self): - self.do_test_ptr_and_ref(USE_LIBSTDCPP) + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test_ptr_and_ref() @add_test_categories(["libc++"]) def test_with_run_command_libcpp(self): - self.do_test_with_run_command(USE_LIBCPP) + self.build(dictionary={"USE_LIBCPP": 1}) + self.do_test_with_run_command() @add_test_categories(["libc++"]) def test_ptr_and_ref_libcpp(self): - self.do_test_ptr_and_ref(USE_LIBCPP) + self.build(dictionary={"USE_LIBCPP": 1}) + self.do_test_ptr_and_ref() + + @add_test_categories(["msvcstl"]) + def test_with_run_command_msvcstl(self): + # No flags, because the "msvcstl" category checks that the MSVC STL is used by default. + self.build() + self.do_test_with_run_command() + + @add_test_categories(["msvcstl"]) + def test_ptr_and_ref_msvcstl(self): + self.build() + self.do_test_ptr_and_ref() diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py index 039c703491759..f6174dd786380 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py @@ -9,15 +9,11 @@ from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil -USE_LIBSTDCPP = "USE_LIBSTDCPP" -USE_LIBCPP = "USE_LIBCPP" - class GenericListDataFormatterTestCase(TestBase): NO_DEBUG_INFO_TESTCASE = True - def do_test_with_run_command(self, stdlib_type): - self.build(dictionary={stdlib_type: "1"}) + def do_test_with_run_command(self): exe = self.getBuildArtifact("a.out") target = self.dbg.CreateTarget(exe) self.assertTrue(target and target.IsValid(), "Target is valid") @@ -64,8 +60,16 @@ def do_test_with_run_command(self, stdlib_type): @add_test_categories(["libstdcxx"]) def test_with_run_command_libstdcpp(self): - self.do_test_with_run_command(USE_LIBSTDCPP) + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test_with_run_command() @add_test_categories(["libc++"]) def test_with_run_command_libcpp(self): - self.do_test_with_run_command(USE_LIBCPP) + self.build(dictionary={"USE_LIBCPP": 1}) + self.do_test_with_run_command() + + @add_test_categories(["msvcstl"]) + def test_with_run_command_msvcstl(self): + # No flags, because the "msvcstl" category checks that the MSVC STL is used by default. + self.build() + self.do_test_with_run_command() diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/main.cpp index e797b3d04dd6b..b31d4ca909ecb 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/main.cpp @@ -1,8 +1,3 @@ -// Evil hack: To simulate memory corruption, we want to fiddle with some internals of std::list. -// Make those accessible to us. -#define private public -#define protected public - #include <list> #include <stdio.h> #include <assert.h> >From ad0a1ec611ef8b8832084021a58b3102d3462a14 Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Tue, 15 Jul 2025 16:36:22 +0200 Subject: [PATCH 2/3] fix: mention that it's a debug summary --- .../Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 9ac2b93b8a31e..771b1b0a5a85d 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1499,10 +1499,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "^std::(__debug::)?unordered_(multi)?(map|set)<.+> >$", stl_summary_flags, true); - AddCXXSummary( - cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, - "libstdc++ std::list summary provider", - "^std::__(debug|cxx11)::list<.+>(( )?&)?$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, GenericCappedContainerSummaryProvider, + "libstdc++ debug std::list summary provider", + "^std::__(debug|cxx11)::list<.+>(( )?&)?$", stl_summary_flags, + true); AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, "libstdc++ std::forward_list summary provider", >From 56f5d6b6fcb591c6a414c490299a3b21cbdb0e5b Mon Sep 17 00:00:00 2001 From: Nerixyz <nerix...@outlook.de> Date: Tue, 15 Jul 2025 17:59:25 +0200 Subject: [PATCH 3/3] fix: use libstdc++ summary for forward list --- lldb/examples/synthetic/gnu_libstdcpp.py | 9 +++++++ .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 25 +++++++++++-------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py index 2996b9ed0fa40..f42a009c21f48 100644 --- a/lldb/examples/synthetic/gnu_libstdcpp.py +++ b/lldb/examples/synthetic/gnu_libstdcpp.py @@ -6,6 +6,15 @@ # thing for your setup +def ForwardListSummaryProvider(valobj, dict): + list_capping_size = valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay() + text = "size=" + str(valobj.GetNumChildren()) + if valobj.GetNumChildren() > list_capping_size: + return "(capped) " + text + else: + return text + + def StdOptionalSummaryProvider(valobj, dict): has_value = valobj.GetNumChildren() > 0 # We add wrapping spaces for consistency with the libcxx formatter diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 771b1b0a5a85d..a8ebde0b55815 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1499,15 +1499,16 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "^std::(__debug::)?unordered_(multi)?(map|set)<.+> >$", stl_summary_flags, true); - AddCXXSummary(cpp_category_sp, GenericCappedContainerSummaryProvider, - "libstdc++ debug std::list summary provider", - "^std::__(debug|cxx11)::list<.+>(( )?&)?$", stl_summary_flags, - true); + AddCXXSummary( + cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider, + "libstdc++ debug std::list summary provider", + "^std::__(debug|cxx11)::list<.+>(( )?&)?$", stl_summary_flags, true); - AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, - "libstdc++ std::forward_list summary provider", - "^std::__(debug|cxx11)::forward_list<.+>(( )?&)?$", - stl_summary_flags, true); + cpp_category_sp->AddTypeSummary( + "^std::__(debug|cxx11)::forward_list<.+>(( )?&)?$", eFormatterMatchRegex, + TypeSummaryImplSP(new ScriptSummaryFormat( + stl_summary_flags, + "lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider"))); AddCXXSummary(cpp_category_sp, LibStdcppVariantSummaryProvider, "libstdc++ std::variant summary provider", "^std::variant<.+>$", stl_summary_flags, true); @@ -1733,9 +1734,11 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, "MSVC STL/libstdc++ std::list summary provider", "^std::list<.+>(( )?&)?$", stl_summary_flags, true); - AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, - "MSVC STL/libstdc++ std::forward_list summary provider", - "^std::forward_list<.+>(( )?&)?$", stl_summary_flags, true); + cpp_category_sp->AddTypeSummary( + "^std::forward_list<.+>(( )?&)?$", eFormatterMatchRegex, + TypeSummaryImplSP(new ScriptSummaryFormat( + stl_summary_flags, + "lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider"))); } static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits