[Lldb-commits] [lldb] 88a498c - [LLDB] Add formatters for MSVC STL std::(forward_)list (#148285)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 16 06:00:21 PDT 2025
Author: nerix
Date: 2025-07-16T14:00:18+01:00
New Revision: 88a498c3b110b73c10362df8c18ca13fe1873744
URL: https://github.com/llvm/llvm-project/commit/88a498c3b110b73c10362df8c18ca13fe1873744
DIFF: https://github.com/llvm/llvm-project/commit/88a498c3b110b73c10362df8c18ca13fe1873744.diff
LOG: [LLDB] Add formatters for MSVC STL std::(forward_)list (#148285)
Adds synthetic providers for MSVC's `std::forward_list` and `std::list`.
It refactors `LibCxxList` to be generic over the STL type (currently
libc++ or MSVC STL). The libstdc++ synthetic providers use something
similar in Python
[here](https://github.com/llvm/llvm-project/blob/3092b765ba0b2d20bd716944dda86ea8e4ad12e3/lldb/examples/synthetic/gnu_libstdcpp.py#L134).
Eventually, this could be ported to C++ as well.
Towards #24834.
Added:
lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
Modified:
lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/main.cpp
Removed:
lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
################################################################################
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..a8ebde0b55815 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,15 +1499,13 @@ 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++ debug std::list summary provider",
+ "^std::__(debug|cxx11)::list<.+>(( )?&)?$", stl_summary_flags, true);
cpp_category_sp->AddTypeSummary(
- "^std::((__debug::)?|(__cxx11::)?)forward_list<.+>(( )?&)?$",
- eFormatterMatchRegex,
+ "^std::__(debug|cxx11)::forward_list<.+>(( )?&)?$", eFormatterMatchRegex,
TypeSummaryImplSP(new ScriptSummaryFormat(
stl_summary_flags,
"lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider")));
@@ -1627,6 +1623,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 +1706,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 +1731,14 @@ 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);
+ 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) {
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>
More information about the lldb-commits
mailing list