llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: nerix (Nerixyz)

<details>
<summary>Changes</summary>

This PR adds synthetic children for std::deque from MSVC's STL.

Similar to libstdc++ and libc++, the elements are in a `T**`, so we need to 
"subscript" twice. The [NatVis for 
deque](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/debugger/STL.natvis#L1103-L1112)
 uses `_EEN_DS` which contains the block size. We can't access this, but we can 
access the [constexpr 
`_Block_size`](https://github.com/microsoft/STL/blob/313964b78a8fd5a52e7965e13781f735bcce13c5/stl/inc/deque#L641).

Towards #<!-- -->24834.

---
Full diff: https://github.com/llvm/llvm-project/pull/150097.diff


5 Files Affected:

- (modified) lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt (+1) 
- (modified) lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
(+22-5) 
- (modified) lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h (+6) 
- (added) lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp (+176) 
- (modified) 
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
 (+21-11) 


``````````diff
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt 
b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index ab9d991fd48f7..81ad7afe4e305 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -35,6 +35,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
   LibStdcppUniquePointer.cpp
   MsvcStl.cpp
   MsvcStlAtomic.cpp
+  MsvcStlDeque.cpp
   MsvcStlSmartPointer.cpp
   MsvcStlTuple.cpp
   MsvcStlUnordered.cpp
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 80dc4609f9b66..c749bf682391a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1414,7 +1414,7 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
           stl_synth_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
   cpp_category_sp->AddTypeSynthetic(
-      "^std::(__debug)?deque<.+>(( )?&)?$", eFormatterMatchRegex,
+      "^std::__debug::deque<.+>(( )?&)?$", eFormatterMatchRegex,
       SyntheticChildrenSP(new ScriptedSyntheticChildren(
           stl_deref_flags,
           "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider")));
@@ -1472,10 +1472,9 @@ static void 
LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
       "libstdc++ std::set summary provider",
       "^std::(__debug::)?set<.+> >(( )?&)?$", stl_summary_flags, true);
 
-  AddCXXSummary(
-      cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
-      "libstdc++ std::deque summary provider",
-      "^std::(__debug::)?deque<.+>(( )?&)?$", stl_summary_flags, true);
+  AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
+                "libstdc++ debug std::deque summary provider",
+                "^std::__debug::?deque<.+>(( )?&)?$", stl_summary_flags, true);
 
   AddCXXSummary(
       cpp_category_sp, lldb_private::formatters::ContainerSizeSummaryProvider,
@@ -1672,6 +1671,18 @@ 
GenericUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *children,
       *valobj_sp);
 }
 
+static SyntheticChildrenFrontEnd *
+GenericDequeSyntheticFrontEndCreator(CXXSyntheticChildren *children,
+                                     ValueObjectSP valobj_sp) {
+  if (!valobj_sp)
+    return nullptr;
+
+  if (IsMsvcStlDeque(*valobj_sp))
+    return MsvcStlDequeSyntheticFrontEndCreator(children, valobj_sp);
+  return new ScriptedSyntheticChildren::FrontEnd(
+      "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider", *valobj_sp);
+}
+
 /// Load formatters that are formatting types from more than one STL
 static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
   if (!cpp_category_sp)
@@ -1743,6 +1754,9 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
                   "std::unordered container synthetic children",
                   "^std::unordered_(multi)?(map|set)<.+> ?>$", stl_synth_flags,
                   true);
+  AddCXXSynthetic(cpp_category_sp, GenericDequeSyntheticFrontEndCreator,
+                  "std::deque container synthetic children",
+                  "^std::deque<.+>(( )?&)?$", stl_synth_flags, true);
 
   SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
   stl_deref_flags.SetFrontEndWantsDereference();
@@ -1786,6 +1800,9 @@ static void 
LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
                 "MSVC STL/libstdc++ std unordered container summary provider",
                 "^std::unordered_(multi)?(map|set)<.+> ?>$", stl_summary_flags,
                 true);
+  AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider,
+                "MSVC STL/libstd++ std::deque summary provider",
+                "^std::deque<.+>(( )?&)?$", stl_summary_flags, true);
 }
 
 static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index e2a015a537868..f93d03d2a6230 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -92,6 +92,12 @@ SyntheticChildrenFrontEnd *
 MsvcStlUnorderedSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                          lldb::ValueObjectSP valobj_sp);
 
+// MSVC STL std::deque<>
+bool IsMsvcStlDeque(ValueObject &valobj);
+SyntheticChildrenFrontEnd *
+MsvcStlDequeSyntheticFrontEndCreator(CXXSyntheticChildren *,
+                                     lldb::ValueObjectSP valobj_sp);
+
 } // namespace formatters
 } // namespace lldb_private
 
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp
new file mode 100644
index 0000000000000..08467a8ae4eb6
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlDeque.cpp
@@ -0,0 +1,176 @@
+//===-- MsvcStlDeque.cpp 
--------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MsvcStl.h"
+
+#include "lldb/DataFormatters/FormattersHelpers.h"
+#include "lldb/DataFormatters/TypeSynthetic.h"
+
+using namespace lldb;
+
+namespace lldb_private {
+namespace formatters {
+
+class MsvcStlDequeSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
+public:
+  MsvcStlDequeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+
+  llvm::Expected<uint32_t> CalculateNumChildren() override;
+
+  lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override;
+
+  lldb::ChildCacheState Update() override;
+
+  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
+
+private:
+  ValueObject *m_map = nullptr;
+  ExecutionContextRef m_exe_ctx_ref;
+
+  size_t m_block_size = 0;
+  size_t m_offset = 0;
+  size_t m_map_size = 0;
+
+  size_t m_element_size = 0;
+  CompilerType m_element_type;
+
+  uint32_t m_size = 0;
+};
+
+} // namespace formatters
+} // namespace lldb_private
+
+lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::
+    MsvcStlDequeSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
+    : SyntheticChildrenFrontEnd(*valobj_sp) {
+  if (valobj_sp)
+    Update();
+}
+
+llvm::Expected<uint32_t> lldb_private::formatters::
+    MsvcStlDequeSyntheticFrontEnd::CalculateNumChildren() {
+  if (!m_map)
+    return llvm::createStringError("Failed to read size");
+  return m_size;
+}
+
+lldb::ValueObjectSP
+lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::GetChildAtIndex(
+    uint32_t idx) {
+  if (idx >= m_size || !m_map)
+    return nullptr;
+  ProcessSP process_sp(m_exe_ctx_ref.GetProcessSP());
+  if (!process_sp)
+    return nullptr;
+
+  // _EEN_DS = _Block_size
+  // _Map[(($i + _Myoff) / _EEN_DS) % _Mapsize][($i + _Myoff) % _EEN_DS]
+  size_t first_idx = ((idx + m_offset) / m_block_size) % m_map_size;
+  lldb::addr_t first_address =
+      m_map->GetValueAsUnsigned(0) + first_idx * 
process_sp->GetAddressByteSize();
+
+  Status err;
+  lldb::addr_t second_base =
+      process_sp->ReadPointerFromMemory(first_address, err);
+  if (err.Fail())
+    return nullptr;
+
+  size_t second_idx = (idx + m_offset) % m_block_size;
+  size_t second_address = second_base + second_idx * m_element_size;
+
+  StreamString name;
+  name.Printf("[%" PRIu64 "]", (uint64_t)idx);
+  return CreateValueObjectFromAddress(name.GetString(), second_address,
+                                      m_backend.GetExecutionContextRef(),
+                                      m_element_type);
+}
+
+lldb::ChildCacheState
+lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::Update() {
+  m_size = 0;
+  m_map = nullptr;
+  m_element_type.Clear();
+
+  auto storage_sp = m_backend.GetChildAtNamePath({"_Mypair", "_Myval2"});
+  if (!storage_sp)
+    return lldb::eRefetch;
+
+  auto deque_type = m_backend.GetCompilerType().GetNonReferenceType();
+  if (deque_type.IsPointerType())
+    deque_type = deque_type.GetPointeeType();
+  if (!deque_type)
+    return lldb::eRefetch;
+
+  auto block_size_decl = deque_type.GetStaticFieldWithName("_Block_size");
+  if (!block_size_decl)
+    return lldb::eRefetch;
+  auto block_size = block_size_decl.GetConstantValue();
+  if (!block_size.IsValid())
+    return lldb::eRefetch;
+
+  auto element_type = deque_type.GetTypeTemplateArgument(0);
+  if (!element_type)
+    return lldb::eRefetch;
+  auto element_size = element_type.GetByteSize(nullptr);
+  if (!element_size)
+    return lldb::eRefetch;
+
+  auto offset_sp = storage_sp->GetChildMemberWithName("_Myoff");
+  auto map_size_sp = storage_sp->GetChildMemberWithName("_Mapsize");
+  auto map_sp = storage_sp->GetChildMemberWithName("_Map");
+  auto size_sp = storage_sp->GetChildMemberWithName("_Mysize");
+  if (!offset_sp || !map_size_sp || !map_sp || !size_sp)
+    return lldb::eRefetch;
+
+  bool ok = false;
+  uint64_t offset = offset_sp->GetValueAsUnsigned(0, &ok);
+  if (!ok)
+    return lldb::eRefetch;
+
+  uint64_t map_size = map_size_sp->GetValueAsUnsigned(0, &ok);
+  if (!ok)
+    return lldb::eRefetch;
+
+  uint64_t size = size_sp->GetValueAsUnsigned(0, &ok);
+  if (!ok)
+    return lldb::eRefetch;
+
+  m_map = map_sp.get();
+  m_exe_ctx_ref = m_backend.GetExecutionContextRef();
+  m_block_size = block_size.ULongLong();
+  m_offset = offset;
+  m_map_size = map_size;
+  m_element_size = *element_size;
+  m_element_type = element_type;
+  m_size = size;
+  return lldb::eRefetch;
+}
+
+llvm::Expected<size_t> 
lldb_private::formatters::MsvcStlDequeSyntheticFrontEnd::
+    GetIndexOfChildWithName(ConstString name) {
+  if (!m_map)
+    return llvm::createStringError("Type has no child named '%s'",
+                                   name.AsCString());
+  if (auto optional_idx = ExtractIndexFromString(name.GetCString()))
+    return *optional_idx;
+
+  return llvm::createStringError("Type has no child named '%s'",
+                                 name.AsCString());
+}
+
+bool lldb_private::formatters::IsMsvcStlDeque(ValueObject &valobj) {
+  if (auto valobj_sp = valobj.GetNonSyntheticValue())
+    return valobj_sp->GetChildMemberWithName("_Mypair") != nullptr;
+  return false;
+}
+
+lldb_private::SyntheticChildrenFrontEnd *
+lldb_private::formatters::MsvcStlDequeSyntheticFrontEndCreator(
+    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
+  return new MsvcStlDequeSyntheticFrontEnd(valobj_sp);
+}
diff --git 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
index f52f6f7ec3ffb..2332eff7b10dd 100644
--- 
a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
+++ 
b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
@@ -3,9 +3,6 @@
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 
-USE_LIBSTDCPP = "USE_LIBSTDCPP"
-USE_LIBCPP = "USE_LIBCPP"
-
 
 class GenericDequeDataFormatterTestCase(TestBase):
     def findVariable(self, name):
@@ -56,8 +53,7 @@ def check_numbers(self, var_name, show_ptr=False):
             ],
         )
 
-    def do_test(self, stdlib_type):
-        self.build(dictionary={stdlib_type: "1"})
+    def do_test(self):
         (_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(
             self, "break here", lldb.SBFileSpec("main.cpp")
         )
@@ -135,15 +131,22 @@ def do_test(self, stdlib_type):
 
     @add_test_categories(["libstdcxx"])
     def test_libstdcpp(self):
-        self.do_test(USE_LIBSTDCPP)
+        self.build(dictionary={"USE_LIBSTDCPP": 1})
+        self.do_test()
 
     @add_test_categories(["libc++"])
     def test_libcpp(self):
-        self.do_test(USE_LIBCPP)
+        self.build(dictionary={"USE_LIBCPP": 1})
+        self.do_test()
+
+    @add_test_categories(["msvcstl"])
+    def test_msvcstl(self):
+        # No flags, because the "msvcstl" category checks that the MSVC STL is 
used by default.
+        self.build()
+        self.do_test()
 
-    def do_test_ref_and_ptr(self, stdlib_type: str):
+    def do_test_ref_and_ptr(self):
         """Test formatting of std::deque& and std::deque*"""
-        self.build(dictionary={stdlib_type: "1"})
         (self.target, process, thread, bkpt) = 
lldbutil.run_to_source_breakpoint(
             self, "stop here", lldb.SBFileSpec("main.cpp", False)
         )
@@ -157,8 +160,15 @@ def do_test_ref_and_ptr(self, stdlib_type: str):
 
     @add_test_categories(["libstdcxx"])
     def test_libstdcpp_ref_and_ptr(self):
-        self.do_test_ref_and_ptr(USE_LIBSTDCPP)
+        self.build(dictionary={"USE_LIBSTDCPP": 1})
+        self.do_test_ref_and_ptr()
 
     @add_test_categories(["libc++"])
     def test_libcpp_ref_and_ptr(self):
-        self.do_test_ref_and_ptr(USE_LIBCPP)
+        self.build(dictionary={"USE_LIBCPP": 1})
+        self.do_test_ref_and_ptr()
+
+    @add_test_categories(["msvcstl"])
+    def test_msvcstl_ref_and_ptr(self):
+        self.build()
+        self.do_test_ref_and_ptr()

``````````

</details>


https://github.com/llvm/llvm-project/pull/150097
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to