-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[LLDB] Add formatters for MSVC STL std::deque #150097
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-lldb Author: nerix (Nerixyz) ChangesThis PR adds synthetic children for std::deque from MSVC's STL. Similar to libstdc++ and libc++, the elements are in a Towards #24834. Full diff: https://github.com/llvm/llvm-project/pull/150097.diff 5 Files Affected:
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index ab9d991fd48f7..81ad7afe4e305 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -35,6 +35,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
LibStdcppUniquePointer.cpp
MsvcStl.cpp
MsvcStlAtomic.cpp
+ MsvcStlDeque.cpp
MsvcStlSmartPointer.cpp
MsvcStlTuple.cpp
MsvcStlUnordered.cpp
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 80dc4609f9b66..c749bf682391a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1414,7 +1414,7 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
cpp_category_sp->AddTypeSynthetic(
- "^std::(__debug)?deque<.+>(( )?&)?$", eFormatterMatchRegex,
+ "^std::__debug::deque<.+>(( )?&)?$", eFormatterMatchRegex,
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_deref_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider")));
@@ -1472,10 +1472,9 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
"libstdc++ std::set summary provider",
"^std::(__debug::)?set<.+> >(( )?&)?$", stl_summary_flags, true);
- AddCXXSummary(
- cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
- "libstdc++ std::deque summary provider",
- "^std::(__debug::)?deque<.+>(( )?&)?$", stl_summary_flags, true);
+ AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
+ "libstdc++ debug std::deque summary provider",
+ "^std::__debug::?deque<.+>(( )?&)?$", stl_summary_flags, true);
AddCXXSummary(
cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1672,6 +1671,18 @@ GenericUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *children,
*valobj_sp);
}
+static SyntheticChildrenFrontEnd *
+GenericDequeSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+ ValueObjectSP valobj_sp) {
+ if (!valobj_sp)
+ return nullptr;
+
+ if (IsMsvcStlDeque(*valobj_sp))
+ return MsvcStlDequeSyntheticFrontEndCreator(children, valobj_sp);
+ return new ScriptedSyntheticChildren::FrontEnd(
+ "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider", *valobj_sp);
+}
+
/// Load formatters that are formatting types from more than one STL
static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
if (!cpp_category_sp)
@@ -1743,6 +1754,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
"std::unordered container synthetic children",
"^std::unordered_(multi)?(map|set)<.+> ?>$", stl_synth_flags,
true);
+ AddCXXSynthetic(cpp_category_sp, GenericDequeSyntheticFrontEndCreator,
+ "std::deque container synthetic children",
+ "^std::deque<.+>(( )?&)?$", stl_synth_flags, true);
SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
stl_deref_flags.SetFrontEndWantsDereference();
@@ -1786,6 +1800,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
"MSVC STL/libstdc++ std unordered container summary provider",
"^std::unordered_(multi)?(map|set)<.+> ?>$", stl_summary_flags,
true);
+ AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
+ "MSVC STL/libstd++ std::deque summary provider",
+ "^std::deque<.+>(( )?&)?$", stl_summary_flags, true);
}
static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index e2a015a537868..f93d03d2a6230 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -92,6 +92,12 @@ SyntheticChildrenFrontEnd *
MsvcStlUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp);
+// MSVC STL std::deque<>
+bool IsMsvcStlDeque(ValueObject &valobj);
+SyntheticChildrenFrontEnd *
+MsvcStlDequeSyntheticFrontEndCreator(CXXSyntheticChildren *,
+ lldb::ValueObjectSP valobj_sp);
+
} // namespace formatters
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp
new file mode 100644
index 0000000000000..08467a8ae4eb6
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp
@@ -0,0 +1,176 @@
+//===-- MsvcStlDeque.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MsvcStl.h"
+
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+
+using namespace lldb;
+
+namespace lldb_private {
+namespace formatters {
+
+class MsvcStlDequeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+ MsvcStlDequeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+ llvm::Expected<uint32_t> CalculateNumChildren() override;
+
+ lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
+
+ lldb::ChildCacheState Update() override;
+
+ llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
+
+private:
+ ValueObject *m_map = nullptr;
+ ExecutionContextRef m_exe_ctx_ref;
+
+ size_t m_block_size = 0;
+ size_t m_offset = 0;
+ size_t m_map_size = 0;
+
+ size_t m_element_size = 0;
+ CompilerType m_element_type;
+
+ uint32_t m_size = 0;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::
+ MsvcStlDequeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+ : SyntheticChildrenFrontEnd(*valobj_sp) {
+ if (valobj_sp)
+ Update();
+}
+
+llvm::Expected<uint32_t> lldb_private::formatters::
+ MsvcStlDequeSyntheticFrontEnd::CalculateNumChildren() {
+ if (!m_map)
+ return llvm::createStringError("Failed to read size");
+ return m_size;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::GetChildAtIndex(
+ uint32_t idx) {
+ if (idx >= m_size || !m_map)
+ return nullptr;
+ ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
+ if (!process_sp)
+ return nullptr;
+
+ // _EEN_DS = _Block_size
+ // _Map[(($i + _Myoff) / _EEN_DS) % _Mapsize][($i + _Myoff) % _EEN_DS]
+ size_t first_idx = ((idx + m_offset) / m_block_size) % m_map_size;
+ lldb::addr_t first_address =
+ m_map->GetValueAsUnsigned(0) + first_idx * process_sp->GetAddressByteSize();
+
+ Status err;
+ lldb::addr_t second_base =
+ process_sp->ReadPointerFromMemory(first_address, err);
+ if (err.Fail())
+ return nullptr;
+
+ size_t second_idx = (idx + m_offset) % m_block_size;
+ size_t second_address = second_base + second_idx * m_element_size;
+
+ StreamString name;
+ name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+ return CreateValueObjectFromAddress(name.GetString(), second_address,
+ m_backend.GetExecutionContextRef(),
+ m_element_type);
+}
+
+lldb::ChildCacheState
+lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::Update() {
+ m_size = 0;
+ m_map = nullptr;
+ m_element_type.Clear();
+
+ auto storage_sp = m_backend.GetChildAtNamePath({"_Mypair", "_Myval2"});
+ if (!storage_sp)
+ return lldb::eRefetch;
+
+ auto deque_type = m_backend.GetCompilerType().GetNonReferenceType();
+ if (deque_type.IsPointerType())
+ deque_type = deque_type.GetPointeeType();
+ if (!deque_type)
+ return lldb::eRefetch;
+
+ auto block_size_decl = deque_type.GetStaticFieldWithName("_Block_size");
+ if (!block_size_decl)
+ return lldb::eRefetch;
+ auto block_size = block_size_decl.GetConstantValue();
+ if (!block_size.IsValid())
+ return lldb::eRefetch;
+
+ auto element_type = deque_type.GetTypeTemplateArgument(0);
+ if (!element_type)
+ return lldb::eRefetch;
+ auto element_size = element_type.GetByteSize(nullptr);
+ if (!element_size)
+ return lldb::eRefetch;
+
+ auto offset_sp = storage_sp->GetChildMemberWithName("_Myoff");
+ auto map_size_sp = storage_sp->GetChildMemberWithName("_Mapsize");
+ auto map_sp = storage_sp->GetChildMemberWithName("_Map");
+ auto size_sp = storage_sp->GetChildMemberWithName("_Mysize");
+ if (!offset_sp || !map_size_sp || !map_sp || !size_sp)
+ return lldb::eRefetch;
+
+ bool ok = false;
+ uint64_t offset = offset_sp->GetValueAsUnsigned(0, &ok);
+ if (!ok)
+ return lldb::eRefetch;
+
+ uint64_t map_size = map_size_sp->GetValueAsUnsigned(0, &ok);
+ if (!ok)
+ return lldb::eRefetch;
+
+ uint64_t size = size_sp->GetValueAsUnsigned(0, &ok);
+ if (!ok)
+ return lldb::eRefetch;
+
+ m_map = map_sp.get();
+ m_exe_ctx_ref = m_backend.GetExecutionContextRef();
+ m_block_size = block_size.ULongLong();
+ m_offset = offset;
+ m_map_size = map_size;
+ m_element_size = *element_size;
+ m_element_type = element_type;
+ m_size = size;
+ return lldb::eRefetch;
+}
+
+llvm::Expected<size_t> lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::
+ GetIndexOfChildWithName(ConstString name) {
+ if (!m_map)
+ return llvm::createStringError("Type has no child named '%s'",
+ name.AsCString());
+ if (auto optional_idx = ExtractIndexFromString(name.GetCString()))
+ return *optional_idx;
+
+ return llvm::createStringError("Type has no child named '%s'",
+ name.AsCString());
+}
+
+bool lldb_private::formatters::IsMsvcStlDeque(ValueObject &valobj) {
+ if (auto valobj_sp = valobj.GetNonSyntheticValue())
+ return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr;
+ return false;
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::MsvcStlDequeSyntheticFrontEndCreator(
+ CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+ return new MsvcStlDequeSyntheticFrontEnd(valobj_sp);
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
index f52f6f7ec3ffb..2332eff7b10dd 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
@@ -3,9 +3,6 @@
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
-USE_LIBSTDCPP = "USE_LIBSTDCPP"
-USE_LIBCPP = "USE_LIBCPP"
-
class GenericDequeDataFormatterTestCase(TestBase):
def findVariable(self, name):
@@ -56,8 +53,7 @@ def check_numbers(self, var_name, show_ptr=False):
],
)
- def do_test(self, stdlib_type):
- self.build(dictionary={stdlib_type: "1"})
+ def do_test(self):
(_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
@@ -135,15 +131,22 @@ def do_test(self, stdlib_type):
@add_test_categories(["libstdcxx"])
def test_libstdcpp(self):
- self.do_test(USE_LIBSTDCPP)
+ self.build(dictionary={"USE_LIBSTDCPP": 1})
+ self.do_test()
@add_test_categories(["libc++"])
def test_libcpp(self):
- self.do_test(USE_LIBCPP)
+ self.build(dictionary={"USE_LIBCPP": 1})
+ self.do_test()
+
+ @add_test_categories(["msvcstl"])
+ def test_msvcstl(self):
+ # No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
+ self.build()
+ self.do_test()
- def do_test_ref_and_ptr(self, stdlib_type: str):
+ def do_test_ref_and_ptr(self):
"""Test formatting of std::deque& and std::deque*"""
- self.build(dictionary={stdlib_type: "1"})
(self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "stop here", lldb.SBFileSpec("main.cpp", False)
)
@@ -157,8 +160,15 @@ def do_test_ref_and_ptr(self, stdlib_type: str):
@add_test_categories(["libstdcxx"])
def test_libstdcpp_ref_and_ptr(self):
- self.do_test_ref_and_ptr(USE_LIBSTDCPP)
+ self.build(dictionary={"USE_LIBSTDCPP": 1})
+ self.do_test_ref_and_ptr()
@add_test_categories(["libc++"])
def test_libcpp_ref_and_ptr(self):
- self.do_test_ref_and_ptr(USE_LIBCPP)
+ self.build(dictionary={"USE_LIBCPP": 1})
+ self.do_test_ref_and_ptr()
+
+ @add_test_categories(["msvcstl"])
+ def test_msvcstl_ref_and_ptr(self):
+ self.build()
+ self.do_test_ref_and_ptr()
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
8baaa09
to
0c9798c
Compare
0c9798c
to
4f7b6c7
Compare
This PR adds synthetic children for std::deque from MSVC's STL.
Similar to libstdc++ and libc++, the elements are in a
T**
, so we need to "subscript" twice. The NatVis for deque uses_EEN_DS
which contains the block size. We can't access this, but we can access the constexpr_Block_size
.Towards #24834.