aboutsummaryrefslogtreecommitdiff
path: root/lldb/source
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source')
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt2
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp88
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h17
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp174
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp407
5 files changed, 667 insertions, 21 deletions
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index ab9d991..cbc6f14 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -35,7 +35,9 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
LibStdcppUniquePointer.cpp
MsvcStl.cpp
MsvcStlAtomic.cpp
+ MsvcStlDeque.cpp
MsvcStlSmartPointer.cpp
+ MsvcStlTree.cpp
MsvcStlTuple.cpp
MsvcStlUnordered.cpp
MsvcStlVariant.cpp
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 80dc460..e69f2f6 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1409,27 +1409,27 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider")));
cpp_category_sp->AddTypeSynthetic(
- "^std::(__debug::)?map<.+> >(( )?&)?$", eFormatterMatchRegex,
+ "^std::__debug::map<.+> >(( )?&)?$", eFormatterMatchRegex,
SyntheticChildrenSP(new ScriptedSyntheticChildren(
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")));
cpp_category_sp->AddTypeSynthetic(
- "^std::(__debug::)?set<.+> >(( )?&)?$", eFormatterMatchRegex,
+ "^std::__debug::set<.+> >(( )?&)?$", eFormatterMatchRegex,
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_deref_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
cpp_category_sp->AddTypeSynthetic(
- "^std::(__debug::)?multimap<.+> >(( )?&)?$", eFormatterMatchRegex,
+ "^std::__debug::multimap<.+> >(( )?&)?$", eFormatterMatchRegex,
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_deref_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
cpp_category_sp->AddTypeSynthetic(
- "^std::(__debug::)?multiset<.+> >(( )?&)?$", eFormatterMatchRegex,
+ "^std::__debug::multiset<.+> >(( )?&)?$", eFormatterMatchRegex,
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_deref_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
@@ -1462,30 +1462,29 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
"libstdc++ std::__debug::vector summary provider",
"^std::__debug::vector<.+>(( )?&)?$", stl_summary_flags, true);
- AddCXXSummary(
- cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
- "libstdc++ std::map summary provider",
- "^std::(__debug::)?map<.+> >(( )?&)?$", stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::ContainerSizeSummaryProvider,
+ "libstdc++ debug std::map summary provider",
+ "^std::__debug::map<.+> >(( )?&)?$", stl_summary_flags, true);
- AddCXXSummary(
- cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
- "libstdc++ std::set summary provider",
- "^std::(__debug::)?set<.+> >(( )?&)?$", stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp,
+ lldb_private::formatters::ContainerSizeSummaryProvider,
+ "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,
- "libstdc++ std::multimap summary provider",
- "^std::(__debug::)?multimap<.+> >(( )?&)?$", stl_summary_flags, true);
+ "libstdc++ debug std::multimap summary provider",
+ "^std::__debug::multimap<.+> >(( )?&)?$", stl_summary_flags, true);
AddCXXSummary(
cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
- "libstdc++ std::multiset summary provider",
- "^std::(__debug::)?multiset<.+> >(( )?&)?$", stl_summary_flags, true);
+ "libstdc++ debug std::multiset summary provider",
+ "^std::__debug::multiset<.+> >(( )?&)?$", stl_summary_flags, true);
AddCXXSummary(cpp_category_sp,
lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1672,6 +1671,30 @@ GenericUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *children,
*valobj_sp);
}
+static SyntheticChildrenFrontEnd *
+GenericMapLikeSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+ ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+
+ if (IsMsvcStlMapLike(*valobj_sp))
+ return MsvcStlMapLikeSyntheticFrontEndCreator(valobj_sp);
+ return new ScriptedSyntheticChildren::FrontEnd(
+ "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)
@@ -1749,6 +1772,14 @@ 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,
+ true);
AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider,
"MSVC STL/libstdc++ std::shared_ptr summary provider",
@@ -1786,6 +1817,13 @@ 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/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) {
@@ -1834,6 +1872,14 @@ static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
AddCXXSummary(cpp_category_sp, MsvcStlAtomicSummaryProvider,
"MSVC STL std::atomic summary provider", "^std::atomic<.+>$",
stl_summary_flags, true);
+ AddCXXSynthetic(cpp_category_sp, MsvcStlTreeIterSyntheticFrontEndCreator,
+ "MSVC STL tree iterator synthetic children",
+ "^std::_Tree(_const)?_iterator<.+>(( )?&)?$", stl_synth_flags,
+ true);
+ AddCXXSummary(cpp_category_sp, MsvcStlTreeIterSummaryProvider,
+ "MSVC STL tree iterator summary",
+ "^std::_Tree(_const)?_iterator<.+>(( )?&)?$", stl_summary_flags,
+ true);
}
static void LoadSystemFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index e2a015a..490794c 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -91,6 +91,23 @@ bool IsMsvcStlUnordered(ValueObject &valobj);
SyntheticChildrenFrontEnd *
MsvcStlUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp);
+bool IsMsvcStlTreeIter(ValueObject &valobj);
+bool MsvcStlTreeIterSummaryProvider(ValueObject &valobj, Stream &stream,
+ const TypeSummaryOptions &options);
+lldb_private::SyntheticChildrenFrontEnd *
+MsvcStlTreeIterSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
+// std::map,set,multimap,multiset
+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 0000000..8733543
--- /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/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp
new file mode 100644
index 0000000..ddf6c27
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlTree.cpp
@@ -0,0 +1,407 @@
+//===-- MsvcStlTree.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/Utility/Status.h"
+#include "lldb/ValueObject/ValueObject.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include <cstdint>
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::formatters;
+
+// A Node looks as follows:
+// struct _Tree_node {
+// _Tree_node *_Left;
+// _Tree_node *_Parent;
+// _Tree_node *_Right;
+// char _Color;
+// char _Isnil; // true (!= 0) if head or nil node
+// value_type _Myval;
+// };
+
+namespace {
+
+class MapEntry {
+public:
+ MapEntry() = default;
+ explicit MapEntry(ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {}
+ explicit MapEntry(ValueObject *entry)
+ : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {}
+
+ ValueObjectSP left() const {
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ 0, m_entry_sp->GetCompilerType(), true);
+ }
+
+ ValueObjectSP right() const {
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ 2 * m_entry_sp->GetProcessSP()->GetAddressByteSize(),
+ m_entry_sp->GetCompilerType(), true);
+ }
+
+ ValueObjectSP parent() const {
+ if (!m_entry_sp)
+ return m_entry_sp;
+ return m_entry_sp->GetSyntheticChildAtOffset(
+ m_entry_sp->GetProcessSP()->GetAddressByteSize(),
+ m_entry_sp->GetCompilerType(), true);
+ }
+
+ uint64_t value() const {
+ if (!m_entry_sp)
+ return 0;
+ return m_entry_sp->GetValueAsUnsigned(0);
+ }
+
+ bool is_nil() const {
+ if (!m_entry_sp)
+ return true;
+ auto isnil_sp = m_entry_sp->GetChildMemberWithName("_Isnil");
+ if (!isnil_sp)
+ return true;
+ return isnil_sp->GetValueAsUnsigned(1) != 0;
+ }
+
+ bool error() const {
+ if (!m_entry_sp)
+ return true;
+ return m_entry_sp->GetError().Fail();
+ }
+
+ bool is_nullptr() const { return (value() == 0); }
+
+ ValueObjectSP GetEntry() const { return m_entry_sp; }
+
+ void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
+
+ bool operator==(const MapEntry &rhs) const {
+ return (rhs.m_entry_sp.get() == m_entry_sp.get());
+ }
+
+private:
+ ValueObjectSP m_entry_sp;
+};
+
+class MapIterator {
+public:
+ MapIterator(ValueObject *entry, size_t depth = 0)
+ : m_entry(entry), m_max_depth(depth) {}
+
+ MapIterator() = default;
+
+ ValueObjectSP value() { return m_entry.GetEntry(); }
+
+ ValueObjectSP advance(size_t count) {
+ ValueObjectSP fail;
+ if (m_error)
+ return fail;
+ size_t steps = 0;
+ while (count > 0) {
+ next();
+ count--, steps++;
+ if (m_error || m_entry.is_nullptr() || (steps > m_max_depth))
+ return fail;
+ }
+ return m_entry.GetEntry();
+ }
+
+private:
+ /// Mimicks _Tree_unchecked_const_iterator::operator++()
+ void next() {
+ if (m_entry.is_nullptr())
+ return;
+ MapEntry right(m_entry.right());
+ if (!right.is_nil()) {
+ m_entry = tree_min(std::move(right));
+ return;
+ }
+ size_t steps = 0;
+ MapEntry pnode(m_entry.parent());
+ while (!pnode.is_nil() &&
+ m_entry.value() == MapEntry(pnode.right()).value()) {
+ m_entry = pnode;
+ steps++;
+ if (steps > m_max_depth) {
+ m_entry = MapEntry();
+ return;
+ }
+ pnode.SetEntry(m_entry.parent());
+ }
+ m_entry = std::move(pnode);
+ }
+
+ /// Mimicks MSVC STL's _Min() algorithm (finding the leftmost node in the
+ /// subtree).
+ MapEntry tree_min(MapEntry pnode) {
+ if (pnode.is_nullptr())
+ return MapEntry();
+ MapEntry left(pnode.left());
+ size_t steps = 0;
+ while (!left.is_nil()) {
+ if (left.error()) {
+ m_error = true;
+ return MapEntry();
+ }
+ pnode = left;
+ left.SetEntry(pnode.left());
+ steps++;
+ if (steps > m_max_depth)
+ return MapEntry();
+ }
+ return pnode;
+ }
+
+ MapEntry m_entry;
+ size_t m_max_depth = 0;
+ bool m_error = false;
+};
+
+} // namespace
+
+namespace lldb_private {
+namespace formatters {
+class MsvcStlTreeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ MsvcStlTreeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ ~MsvcStlTreeSyntheticFrontEnd() override = default;
+
+ 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:
+ /// Returns the ValueObject for the _Tree_node at index \ref idx.
+ ///
+ /// \param[in] idx The child index that we're looking to get the value for.
+ ///
+ /// \param[in] max_depth The maximum search depth after which we stop trying
+ /// to find the node for.
+ ///
+ /// \returns On success, returns the ValueObjectSP corresponding to the
+ /// _Tree_node's _Myval member.
+ /// On failure, nullptr is returned.
+ ValueObjectSP GetValueAt(size_t idx, size_t max_depth);
+
+ ValueObject *m_tree = nullptr;
+ ValueObject *m_begin_node = nullptr;
+ size_t m_count = UINT32_MAX;
+ std::map<size_t, MapIterator> m_iterators;
+};
+
+class MsvcStlTreeIterSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ MsvcStlTreeIterSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {}
+
+ llvm::Expected<uint32_t> CalculateNumChildren() override {
+ if (!m_inner_sp)
+ return 0;
+ return m_inner_sp->GetNumChildren();
+ }
+
+ lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
+ if (!m_inner_sp)
+ return nullptr;
+ return m_inner_sp->GetChildAtIndex(idx);
+ }
+
+ lldb::ChildCacheState Update() override;
+
+ llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {
+ if (!m_inner_sp)
+ return llvm::createStringError("There are no children.");
+ return m_inner_sp->GetIndexOfChildWithName(name);
+ }
+
+ lldb::ValueObjectSP GetSyntheticValue() override { return m_inner_sp; }
+
+private:
+ ValueObjectSP m_inner_sp;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::
+ MsvcStlTreeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ if (valobj_sp)
+ Update();
+}
+
+llvm::Expected<uint32_t>
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::CalculateNumChildren() {
+ if (m_count != UINT32_MAX)
+ return m_count;
+
+ if (m_tree == nullptr)
+ return 0;
+
+ if (auto node_sp = m_tree->GetChildMemberWithName("_Mysize")) {
+ m_count = node_sp->GetValueAsUnsigned(0);
+ return m_count;
+ }
+
+ return llvm::createStringError("Failed to read size.");
+}
+
+ValueObjectSP
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetValueAt(
+ size_t idx, size_t max_depth) {
+ MapIterator iterator(m_begin_node, max_depth);
+
+ size_t advance_by = idx;
+ if (idx > 0) {
+ // If we have already created the iterator for the previous
+ // index, we can start from there and advance by 1.
+ auto cached_iterator = m_iterators.find(idx - 1);
+ if (cached_iterator != m_iterators.end()) {
+ iterator = cached_iterator->second;
+ advance_by = 1;
+ }
+ }
+
+ ValueObjectSP iterated_sp(iterator.advance(advance_by));
+ if (!iterated_sp)
+ // this tree is garbage - stop
+ return nullptr;
+
+ ValueObjectSP value_sp = iterated_sp->GetChildMemberWithName("_Myval");
+ if (!value_sp)
+ return nullptr;
+
+ m_iterators[idx] = iterator;
+
+ return value_sp;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetChildAtIndex(
+ uint32_t idx) {
+ uint32_t num_children = CalculateNumChildrenIgnoringErrors();
+ if (idx >= num_children)
+ return nullptr;
+
+ if (m_tree == nullptr || m_begin_node == nullptr)
+ return nullptr;
+
+ ValueObjectSP val_sp = GetValueAt(idx, /*max_depth=*/num_children);
+ if (!val_sp) {
+ // this will stop all future searches until an Update() happens
+ m_tree = nullptr;
+ return nullptr;
+ }
+
+ // at this point we have a valid pair
+ // we need to copy current_sp into a new object otherwise we will end up with
+ // all items named _Myval
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return val_sp->Clone(ConstString(name.GetString()));
+}
+
+lldb::ChildCacheState
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::Update() {
+ m_count = UINT32_MAX;
+ m_tree = m_begin_node = nullptr;
+ m_iterators.clear();
+ m_tree =
+ m_backend.GetChildAtNamePath({"_Mypair", "_Myval2", "_Myval2"}).get();
+ if (!m_tree)
+ return lldb::ChildCacheState::eRefetch;
+
+ m_begin_node = m_tree->GetChildAtNamePath({"_Myhead", "_Left"}).get();
+
+ return lldb::ChildCacheState::eRefetch;
+}
+
+llvm::Expected<size_t>
+lldb_private::formatters::MsvcStlTreeSyntheticFrontEnd::GetIndexOfChildWithName(
+ ConstString name) {
+ auto optional_idx = formatters::ExtractIndexFromString(name.GetCString());
+ if (!optional_idx) {
+ return llvm::createStringError("Type has no child named '%s'",
+ name.AsCString());
+ }
+ return *optional_idx;
+}
+
+lldb::ChildCacheState MsvcStlTreeIterSyntheticFrontEnd::Update() {
+ m_inner_sp = nullptr;
+ auto node_sp = m_backend.GetChildMemberWithName("_Ptr");
+ if (!node_sp)
+ return lldb::eRefetch;
+
+ MapEntry entry(node_sp.get());
+ if (entry.is_nil())
+ return lldb::eRefetch; // end
+
+ m_inner_sp = node_sp->GetChildMemberWithName("_Myval");
+ return lldb::eRefetch;
+}
+
+bool formatters::IsMsvcStlTreeIter(ValueObject &valobj) {
+ return valobj.GetChildMemberWithName("_Ptr") != nullptr;
+}
+
+bool formatters::MsvcStlTreeIterSummaryProvider(
+ ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
+ auto valobj_sp = valobj.GetNonSyntheticValue();
+ if (!valobj_sp)
+ return false;
+ auto node_sp = valobj_sp->GetChildMemberWithName("_Ptr");
+ if (!node_sp)
+ return false;
+
+ MapEntry entry(node_sp.get());
+ if (entry.is_nil()) {
+ stream.Printf("end");
+ return true;
+ }
+
+ auto value_sp = node_sp->GetChildMemberWithName("_Myval");
+ if (!value_sp)
+ return false;
+
+ auto *summary = value_sp->GetSummaryAsCString();
+ if (summary)
+ stream << summary;
+ return true;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::MsvcStlTreeIterSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new MsvcStlTreeIterSyntheticFrontEnd(valobj_sp)
+ : nullptr);
+}
+
+bool formatters::IsMsvcStlMapLike(ValueObject &valobj) {
+ return valobj.GetChildMemberWithName("_Mypair") != nullptr;
+}
+
+SyntheticChildrenFrontEnd *
+lldb_private::formatters::MsvcStlMapLikeSyntheticFrontEndCreator(
+ lldb::ValueObjectSP valobj_sp) {
+ return (valobj_sp ? new MsvcStlTreeSyntheticFrontEnd(valobj_sp) : nullptr);
+}