[Lldb-commits] [lldb] [lldb] Format `atomic<shared_ptr>` on MSVC STL (PR #237631)

via lldb-commits lldb-commits at lists.llvm.org
Mon Nov 2 01:40:46 PST 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Nerixyz (Nerixyz)

<details>
<summary>Changes</summary>

Continues #<!-- -->174218 and adds support for atomic shared and weak pointers.

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


4 Files Affected:

- (modified) lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h (+5-1) 
- (modified) lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp (+139-39) 
- (modified) lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp (+13-6) 
- (modified) lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/TestDataFormatterStdAtomic.py (+14-6) 


``````````diff
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index 4ec992b9ed21db..5c1325afdb5178 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -60,6 +60,10 @@ bool IsMsvcStlSmartPointer(ValueObject &valobj);
 bool MsvcStlSmartPointerSummaryProvider(ValueObject &valobj, Stream &stream,
                                         const TypeSummaryOptions &options);
 
+bool DumpMsvcStlSmartPointerSummary(ValueObject &ptr, ValueObject &rep,
+                                    uint64_t ctrl_addr, Stream &stream,
+                                    const TypeSummaryOptions &options);
+
 lldb_private::SyntheticChildrenFrontEnd *
 MsvcStlSmartPointerSyntheticFrontEndCreator(lldb::ValueObjectSP valobj_sp);
 
@@ -109,7 +113,7 @@ MsvcStlVariantSyntheticFrontEndCreator(CXXSyntheticChildren *,
                                        lldb::ValueObjectSP valobj_sp);
 
 // MSVC STL std::atomic<>
-bool IsMsvcStlAtomic(ValueObject &valobj);
+bool IsMsvcStlAtomic(ValueObject &valobj, bool *is_smart_ptr = nullptr);
 bool MsvcStlAtomicSummaryProvider(ValueObject &valobj, Stream &stream,
                                   const TypeSummaryOptions &options);
 SyntheticChildrenFrontEnd *
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp
index 5c3f11a154f285..a39fe74bc09ccc 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp
@@ -12,13 +12,15 @@
 #include "llvm/Support/ErrorExtras.h"
 
 using namespace lldb;
+using namespace lldb_private;
 
 namespace lldb_private {
 namespace formatters {
 
 class MsvcStlAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
 public:
-  MsvcStlAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
+  MsvcStlAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp,
+                                 bool is_smart_ptr);
 
   llvm::Expected<uint32_t> CalculateNumChildren() override;
 
@@ -29,20 +31,39 @@ class MsvcStlAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
   llvm::Expected<size_t> GetIndexOfChildWithName(llvm::StringRef name) override;
 
 private:
-  ValueObject *m_storage = nullptr;
-  CompilerType m_element_type;
+  ValueObjectSP m_storage;
+  bool m_is_smart_ptr = false;
 };
 
 } // namespace formatters
 } // namespace lldb_private
 
-lldb_private::formatters::MsvcStlAtomicSyntheticFrontEnd::
-    MsvcStlAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
-    : SyntheticChildrenFrontEnd(*valobj_sp), m_element_type() {
-  if (valobj_sp)
-    Update();
+static ValueObjectSP ValueOfAtomic(ValueObject &valobj) {
+  ValueObjectSP storage_sp = valobj.GetChildMemberWithName("_Storage");
+  if (!storage_sp)
+    return nullptr;
+
+  CompilerType backend_type = valobj.GetCompilerType();
+  if (!backend_type)
+    return nullptr;
+
+  CompilerType element_type = backend_type.GetTypeTemplateArgument(0);
+  if (!element_type) {
+    // PDB doesn't have info about templates, so use value_type which equals T.
+    element_type = backend_type.GetDirectNestedTypeWithName("value_type");
+
+    if (!element_type)
+      return nullptr;
+  }
+
+  return valobj.Cast(element_type);
 }
 
+lldb_private::formatters::MsvcStlAtomicSyntheticFrontEnd::
+    MsvcStlAtomicSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp,
+                                   bool is_smart_ptr)
+    : SyntheticChildrenFrontEnd(*valobj_sp), m_is_smart_ptr(is_smart_ptr) {}
+
 llvm::Expected<uint32_t> lldb_private::formatters::
     MsvcStlAtomicSyntheticFrontEnd::CalculateNumChildren() {
   return m_storage ? 1 : 0;
@@ -51,41 +72,34 @@ llvm::Expected<uint32_t> lldb_private::formatters::
 lldb::ValueObjectSP
 lldb_private::formatters::MsvcStlAtomicSyntheticFrontEnd::GetChildAtIndex(
     uint32_t idx) {
-  if (idx == 0 && m_storage && m_element_type.IsValid())
-    return m_storage->Cast(m_element_type)->Clone("Value");
+  if (idx == 0 && m_storage)
+    return m_storage;
   return nullptr;
 }
 
 lldb::ChildCacheState
 lldb_private::formatters::MsvcStlAtomicSyntheticFrontEnd::Update() {
-  m_storage = nullptr;
-  m_element_type.Clear();
-
-  ValueObjectSP storage_sp = m_backend.GetChildMemberWithName("_Storage");
-  if (!storage_sp)
-    return lldb::ChildCacheState::eRefetch;
-
-  CompilerType backend_type = m_backend.GetCompilerType();
-  if (!backend_type)
-    return lldb::ChildCacheState::eRefetch;
-
-  m_element_type = backend_type.GetTypeTemplateArgument(0);
-  if (!m_element_type) {
-    // PDB doesn't have info about templates, so use value_type which equals T.
-    m_element_type = backend_type.GetDirectNestedTypeWithName("value_type");
-
-    if (!m_element_type)
-      return lldb::ChildCacheState::eRefetch;
+  m_storage.reset();
+
+  if (m_is_smart_ptr) {
+    ValueObjectSP ptr = m_backend.GetChildMemberWithName("_Ptr");
+    if (!ptr)
+      return lldb::eRefetch;
+    m_storage = ValueOfAtomic(*ptr);
+  } else {
+    m_storage = ValueOfAtomic(m_backend);
   }
 
-  m_storage = storage_sp.get();
-  return lldb::ChildCacheState::eRefetch;
+  if (m_storage)
+    m_storage = m_storage->Clone("Value");
+
+  return lldb::eRefetch;
 }
 
 llvm::Expected<size_t>
 lldb_private::formatters::MsvcStlAtomicSyntheticFrontEnd::
     GetIndexOfChildWithName(llvm::StringRef name) {
-  if (name == "Value")
+  if (name == "Value" || (m_is_smart_ptr && name == "$$dereference$$"))
     return 0;
   return llvm::createStringErrorV("type has no child named '{0}'", name);
 }
@@ -93,18 +107,96 @@ lldb_private::formatters::MsvcStlAtomicSyntheticFrontEnd::
 lldb_private::SyntheticChildrenFrontEnd *
 lldb_private::formatters::MsvcStlAtomicSyntheticFrontEndCreator(
     CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
-  if (valobj_sp && IsMsvcStlAtomic(*valobj_sp))
-    return new MsvcStlAtomicSyntheticFrontEnd(valobj_sp);
+  bool is_smart_ptr = false;
+  if (valobj_sp && IsMsvcStlAtomic(*valobj_sp, &is_smart_ptr))
+    return new MsvcStlAtomicSyntheticFrontEnd(valobj_sp, is_smart_ptr);
   return nullptr;
 }
 
+static CompilerType FindLockedPointeeType(Target &target,
+                                          const CompilerType &locked_ptr_type) {
+  ConstString locked_name = locked_ptr_type.GetTypeName();
+  llvm::StringRef pointee_name = locked_name;
+  if (!pointee_name.consume_front("std::_Locked_pointer<"))
+    return {};
+  if (!pointee_name.consume_back(">"))
+    return {};
+
+  TypeQuery query(pointee_name, TypeQueryOptions::e_find_one);
+  TypeResults results;
+  target.GetImages().FindTypes(/*search_first=*/nullptr, query, results);
+  if (TypeSP type_sp = results.GetFirstType())
+    return type_sp->GetFullCompilerType();
+  return {};
+}
+
+static bool AtomicSharedPtrSummaryProvider(ValueObject &ptr,
+                                           ValueObject &rep_wrap,
+                                           Stream &stream,
+                                           const TypeSummaryOptions &options) {
+  // rep_wrap: _Locked_pointer<_Ref_count_base> {
+  //   static constexpr uintptr_t _Lock_mask = 3;
+  //   static constexpr uintptr_t _Ptr_value_mask = ~_Lock_mask;
+  //
+  //   atomic<uintptr_t> Storage_;
+  // }
+  ValueObjectSP rep_storage_sp = rep_wrap.GetChildAtIndex(0);
+  if (!rep_storage_sp)
+    return false;
+  ValueObjectSP rep_ptr_sp = ValueOfAtomic(*rep_storage_sp);
+  if (!rep_ptr_sp)
+    return false;
+
+  bool ok = false;
+  uint64_t rep_ptr_val = rep_ptr_sp->GetValueAsUnsigned(0, &ok);
+  if (!ok)
+    return false;
+
+  CompilerType rep_type = rep_wrap.GetCompilerType();
+  if (!rep_type)
+    return false;
+  CompilerDecl ptr_value_decl =
+      rep_type.GetStaticFieldWithName("_Ptr_value_mask");
+  if (!ptr_value_decl)
+    return false;
+  uint64_t rep_ptr_mask = ptr_value_decl.GetConstantValue().ULongLong(~(3ULL));
+  rep_ptr_val &= rep_ptr_mask;
+
+  CompilerType ref_count_base = rep_type.GetTypeTemplateArgument(0);
+  if (!ref_count_base) {
+    // PDB: Search for the type by name, as it doesn't know about templates.
+    // Unfortunately, there's no typedef inside the type.
+    TargetSP target = rep_wrap.GetTargetSP();
+    if (!target)
+      return false;
+    ref_count_base = FindLockedPointeeType(*target, rep_type);
+    if (!ref_count_base)
+      return false;
+  }
+
+  ValueObjectSP rep = ValueObject::CreateValueObjectFromAddress(
+      "", rep_ptr_val, rep_wrap.GetExecutionContextRef(), ref_count_base);
+  if (!rep)
+    return false;
+
+  return formatters::DumpMsvcStlSmartPointerSummary(ptr, *rep, rep_ptr_val,
+                                                    stream, options);
+}
+
 bool lldb_private::formatters::MsvcStlAtomicSummaryProvider(
     ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
-  ValueObjectSP synth_sp = valobj.GetSyntheticValue();
-  if (!synth_sp)
+  ValueObjectSP non_synth = valobj.GetNonSyntheticValue();
+  if (!non_synth)
     return false;
+  ValueObjectSP value_sp = valobj.GetChildAtIndex(0);
+  if (!value_sp)
+    return false;
+
+  ValueObjectSP smart_ptr_rep = non_synth->GetChildMemberWithName("_Repptr");
+  if (smart_ptr_rep)
+    return AtomicSharedPtrSummaryProvider(*value_sp, *smart_ptr_rep, stream,
+                                          options);
 
-  ValueObjectSP value_sp = synth_sp->GetChildAtIndex(0);
   std::string summary;
   if (value_sp->GetSummaryAsCString(summary, options) && !summary.empty()) {
     stream << summary;
@@ -113,8 +205,16 @@ bool lldb_private::formatters::MsvcStlAtomicSummaryProvider(
   return false;
 }
 
-bool lldb_private::formatters::IsMsvcStlAtomic(ValueObject &valobj) {
-  if (auto valobj_sp = valobj.GetNonSyntheticValue())
-    return valobj_sp->GetChildMemberWithName("_Storage") != nullptr;
+bool lldb_private::formatters::IsMsvcStlAtomic(ValueObject &valobj,
+                                               bool *is_smart_ptr) {
+  if (auto valobj_sp = valobj.GetNonSyntheticValue()) {
+    bool has_storage = valobj_sp->GetChildMemberWithName("_Storage") != nullptr;
+    bool has_smart =
+        !has_storage && valobj_sp->GetChildMemberWithName("_Ptr") != nullptr;
+    if (is_smart_ptr)
+      *is_smart_ptr = has_smart;
+
+    return has_storage || has_smart;
+  }
   return false;
 }
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp
index 690addf69f8b95..d8932c9fbcb651 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlSmartPointer.cpp
@@ -33,16 +33,23 @@ bool lldb_private::formatters::MsvcStlSmartPointerSummaryProvider(
   if (!ctrl_sp || !ptr_sp)
     return false;
 
-  DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);
+  uint64_t ctrl_addr = ctrl_sp->GetValueAsUnsigned(0);
+
+  return DumpMsvcStlSmartPointerSummary(*ptr_sp, *ctrl_sp, ctrl_addr, stream,
+                                        options);
+}
+
+bool lldb_private::formatters::DumpMsvcStlSmartPointerSummary(
+    ValueObject &ptr, ValueObject &rep, uint64_t ctrl_addr, Stream &stream,
+    const TypeSummaryOptions &options) {
+  DumpCxxSmartPtrPointerSummary(stream, ptr, options);
 
-  bool success;
-  uint64_t ctrl_addr = ctrl_sp->GetValueAsUnsigned(0, &success);
   // Empty control field (expired)
-  if (!success || ctrl_addr == 0)
+  if (ctrl_addr == 0)
     return true;
 
   uint64_t uses = 0;
-  if (auto uses_sp = ctrl_sp->GetChildMemberWithName("_Uses")) {
+  if (auto uses_sp = rep.GetChildMemberWithName("_Uses")) {
     bool success;
     uses = uses_sp->GetValueAsUnsigned(0, &success);
     if (!success)
@@ -52,7 +59,7 @@ bool lldb_private::formatters::MsvcStlSmartPointerSummaryProvider(
   }
 
   // _Weaks is the number of weak references - (_Uses != 0).
-  if (auto weak_count_sp = ctrl_sp->GetChildMemberWithName("_Weaks")) {
+  if (auto weak_count_sp = rep.GetChildMemberWithName("_Weaks")) {
     bool success;
     uint64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);
     if (!success)
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/TestDataFormatterStdAtomic.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/TestDataFormatterStdAtomic.py
index 958a0bc312b797..12997f3a5117d2 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/TestDataFormatterStdAtomic.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/TestDataFormatterStdAtomic.py
@@ -18,7 +18,7 @@ def get_variable(self, name):
         var.SetPreferSyntheticValue(True)
         return var
 
-    def do_test(self, check_shared_ptr: bool = True):
+    def do_test(self):
         """Test that std::atomic is correctly printed by LLDB"""
         self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
 
@@ -110,18 +110,26 @@ def do_test(self, check_shared_ptr: bool = True):
         )
 
         # Atomic smart pointers, only available if supported by the library.
-        atomic_shared = self.get_variable("atomic_shared")
-        if check_shared_ptr and atomic_shared.IsValid():
+        shared_original = self.get_variable("s_value")
+        if shared_original.IsValid():
+            og_summary = shared_original.GetSummary()
+            og_addr = shared_original.GetChildAtIndex(0).GetValueAsAddress()
+
             # shared_pointer
-            val_shared = atomic_shared.child[0]
+            atomic_shared = self.get_variable("atomic_shared")
+            val_shared = atomic_shared.GetChildAtIndex(0)
             self.assertIsNotNone(val_shared, msg="atomic_shared child is None.")
             self.assertEqual(300, val_shared.deref.unsigned)
+            self.assertEqual(og_summary, atomic_shared.GetSummary())
+            self.assertEqual(og_addr, val_shared.GetValueAsAddress())
 
             # weak_pointer
             atomic_weak = self.get_variable("atomic_weak")
-            val_weak = atomic_weak.child[0]
+            val_weak = atomic_weak.GetChildAtIndex(0)
             self.assertIsNotNone(val_weak, msg="atomic_weak child is None.")
             self.assertEqual(300, val_weak.deref.unsigned)
+            self.assertEqual(og_summary, atomic_weak.GetSummary())
+            self.assertEqual(og_addr, val_weak.GetValueAsAddress())
 
     def verify_floating_point_equal(self, value: str, expected: float):
         self.assertAlmostEqual(float(value), float(expected), places=4)
@@ -141,4 +149,4 @@ def test_libstdcxx(self):
     def test_msvcstl(self):
         # No flags, because the "msvcstl" category checks that the MSVC STL is used by default.
         self.build()
-        self.do_test(check_shared_ptr=False)
+        self.do_test()

``````````

</details>


https://github.com/llvm/llvm-project/pull/237631


More information about the lldb-commits mailing list