https://github.com/da-viper updated https://github.com/llvm/llvm-project/pull/174218
>From 686be087caaa3433b46278113f965c1f8bf3b51f Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Wed, 19 Nov 2025 18:14:43 +0000 Subject: [PATCH 1/4] [lldb] update the representation This patch adds a new synthetic child provider and a summary formatter for the libstdc++ implementation of std::atomic<>. [lldb] add test cases [lldb] add review changes # Conflicts: # lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp # lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h --- .../Plugins/Language/CPlusPlus/CMakeLists.txt | 1 + .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 31 ++++- .../Plugins/Language/CPlusPlus/LibStdcpp.h | 7 + .../Language/CPlusPlus/LibStdcppAtomic.cpp | 124 ++++++++++++++++++ .../atomic/TestDataFormatterStdAtomic.py | 48 +++++++ .../generic/atomic/main.cpp | 13 ++ 6 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index 79c0cc14ec644..ca1387ea94117 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -31,6 +31,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LibCxxValarray.cpp LibCxxVector.cpp LibStdcpp.cpp + LibStdcppAtomic.cpp LibStdcppSpan.cpp LibStdcppTuple.cpp LibStdcppUniquePointer.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index bdc67a004b06c..c685707e2fc4f 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1525,6 +1525,24 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { eTypeOptionHideChildren | eTypeOptionHideValue, false); } +static lldb_private::SyntheticChildrenFrontEnd * +GenericAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *children, + const lldb::ValueObjectSP &valobj_sp) { + if (!valobj_sp) + return nullptr; + + if (IsMsvcStlAtomic(*valobj_sp)) + return MsvcStlAtomicSyntheticFrontEndCreator(children, valobj_sp); + return LibStdcppAtomicSyntheticFrontEndCreator(children, valobj_sp); +} + +static bool GenericAtomicSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + if (IsMsvcStlAtomic(valobj)) + return MsvcStlAtomicSummaryProvider(valobj, stream, options); + return LibStdcppAtomicSummaryProvider(valobj, stream, options); +} + static lldb_private::SyntheticChildrenFrontEnd * GenericSmartPointerSyntheticFrontEndCreator(CXXSyntheticChildren *children, lldb::ValueObjectSP valobj_sp) { @@ -1797,6 +1815,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { }, "MSVC STL/libstdc++ std::wstring_view summary provider")); + AddCXXSynthetic(cpp_category_sp, GenericAtomicSyntheticFrontEndCreator, + "std::atomic synthetic children", "^std::atomic<.+>$", + stl_synth_flags, true); // NOTE: it is loaded as a common formatter because the libc++ version is not // in the `__1` namespace, hence we need to dispatch based on the class // layout. @@ -1854,6 +1875,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { AddCXXSummary(cpp_category_sp, ContainerSizeSummaryProvider, "std::initializer_list summary provider", "^std::initializer_list<.+>$", stl_summary_flags, true); + AddCXXSummary(cpp_category_sp, GenericAtomicSummaryProvider, + "std::atomic summary provider", "^std::atomic<.+>$", + stl_summary_flags, true); AddCXXSummary(cpp_category_sp, GenericSmartPointerSummaryProvider, "MSVC STL/libstdc++ std::shared_ptr summary provider", "^std::shared_ptr<.+>(( )?&)?$", stl_summary_flags, true); @@ -1941,13 +1965,6 @@ static void LoadMsvcStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_summary_flags.SetDontShowChildren(false); - AddCXXSynthetic(cpp_category_sp, MsvcStlAtomicSyntheticFrontEndCreator, - "MSVC STL std::atomic synthetic children", - "^std::atomic<.+>$", stl_synth_flags, true); - - 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, diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h index bc67885787bc2..f3b2408589e46 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -44,6 +44,9 @@ bool LibStdcppVariantSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // libstdc++ std::variant<> +bool LibStdcppAtomicSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + bool LibStdcppPartialOrderingSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // libstdc++ std::partial_ordering @@ -56,6 +59,10 @@ bool LibStdcppStrongOrderingSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // libstdc++ std::strong_ordering +SyntheticChildrenFrontEnd * +LibStdcppAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *, + const lldb::ValueObjectSP &); + SyntheticChildrenFrontEnd * LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp new file mode 100644 index 0000000000000..d2e6b82ffb8b9 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp @@ -0,0 +1,124 @@ +//===---------------------------------------------------------------------===// +// +// 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 "LibStdcpp.h" + +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/ValueObject/ValueObject.h" +#include "lldb/lldb-enumerations.h" +#include "llvm/Support/Error.h" + +using namespace lldb; + +namespace lldb_private::formatters { + +class LibStdcppAtomicSyntheticFrontEnd final + : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppAtomicSyntheticFrontEnd(ValueObject &valobj_sp) + : SyntheticChildrenFrontEnd(valobj_sp), + m_inner_name(ConstString("Value")) {} + + llvm::Expected<uint32_t> CalculateNumChildren() final { + if (!m_inner) + return llvm::createStringError("invalide atomic ValueObject"); + return 1; + } + + ValueObjectSP GetChildAtIndex(uint32_t idx) final { + if (idx == 0 && m_inner) + return m_inner->GetSP()->Clone(m_inner_name); + + return {}; + } + + lldb::ChildCacheState Update() final { + if (ValueObjectSP value = ContainerFieldName(m_backend)) { + // show the Type, instead of std::__atomic_base<Type>::__Type_type. + value = value->Cast(value->GetCompilerType().GetCanonicalType()); + m_inner = value.get(); + } + + return lldb::ChildCacheState::eRefetch; + } + + llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) final { + if (name == m_inner_name) + return 0; + + return llvm::createStringError("Type has no child named '%s'", + name.AsCString()); + } + + static ValueObjectSP ContainerFieldName(ValueObject &backend_syn) { + const ValueObjectSP non_synthetic = backend_syn.GetNonSyntheticValue(); + if (!non_synthetic) + return {}; + ValueObject &backend = *non_synthetic; + + const CompilerType type = backend.GetCompilerType(); + if (!type.IsValid() || type.GetNumTemplateArguments() < 1) + return {}; + + const CompilerType first_type = type.GetTypeTemplateArgument(0); + const lldb::BasicType basic_type = first_type.GetBasicTypeEnumeration(); + if (basic_type == eBasicTypeBool) + return backend.GetChildAtNamePath({"_M_base", "_M_i"}); + + const uint32_t float_mask = lldb::eTypeIsFloat | lldb::eTypeIsBuiltIn; + if (first_type.GetTypeInfo() & float_mask) { + // added float types specialization in c++17 + if (const auto child = backend.GetChildMemberWithName("_M_fp")) + return child; + + return backend.GetChildMemberWithName("_M_i"); + } + + if (first_type.IsPointerType()) + return backend.GetChildAtNamePath({"_M_b", "_M_p"}); + + return backend.GetChildMemberWithName("_M_i"); + } + +private: + ConstString m_inner_name; + ValueObject *m_inner = nullptr; +}; + +SyntheticChildrenFrontEnd * +LibStdcppAtomicSyntheticFrontEndCreator(CXXSyntheticChildren * /*unused*/, + const lldb::ValueObjectSP &valobj_sp) { + if (!valobj_sp) + return nullptr; + + const lldb::ValueObjectSP member = + LibStdcppAtomicSyntheticFrontEnd::ContainerFieldName(*valobj_sp); + if (!member) + return nullptr; + + return new LibStdcppAtomicSyntheticFrontEnd(*valobj_sp); +} + +bool LibStdcppAtomicSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options) { + + if (const ValueObjectSP atomic_value = + LibStdcppAtomicSyntheticFrontEnd::ContainerFieldName(valobj)) { + std::string summary; + if (atomic_value->GetSummaryAsCString(summary, options) && + !summary.empty()) { + stream << summary; + return true; + } + } + + return false; +} + +} // namespace lldb_private::formatters 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 67c2c359c9afb..ddad85b095362 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 @@ -56,6 +56,35 @@ def do_test(self): self.assertEqual(s.GetChildAtIndex(0).GetValueAsUnsigned(0), 1, "s.x == 1") self.assertEqual(s.GetChildAtIndex(1).GetValueAsUnsigned(0), 2, "s.y == 2") + # float + atomic_float = self.get_variable("atomic_float") + val_float = atomic_float.child[0] + self.assertIsNotNone(val_float, msg="atomic_float child is None.") + self.assertAlmostEqual(float(val_float.value), 3.14, places=2) + # double + atomic_double = self.get_variable("atomic_double") + val_float = atomic_double.child[0] + self.assertIsNotNone(val_float, msg="atomic_double child is None.") + self.assertAlmostEqual(float(val_float.value), 6.28, places=2) + + # bool + atomic_bool = self.get_variable("atomic_bool") + val_bool = atomic_bool.child[0] + self.assertIsNotNone(val_bool, msg="atomic_bool child is None.") + self.assertEqual(bool(val_bool.unsigned), True) + + # func + atomic_func = self.get_variable("atomic_func") + val_func = atomic_func.child[0] + self.assertIsNotNone(val_func, msg="atomic_func child is None.") + self.assertNotEqual(val_func.unsigned, 0) + + # pointer + atomic_pointer = self.get_variable("atomic_pointer") + val_pointer = atomic_pointer.child[0] + self.assertIsNotNone(val_pointer, msg="atomic_pointer child is None.") + self.assertNotEqual(val_pointer.unsigned, 0) + # Try printing the child that points to its own parent object. # This should just treat the atomic pointer as a normal pointer. self.expect("frame var p.child", substrs=["Value = 0x"]) @@ -64,12 +93,31 @@ def do_test(self): "frame var p.child.parent", substrs=["p.child.parent = {\n Value = 0x"] ) + def verify_floating_point_equal(self, value: str, expected: float): + self.assertAlmostEqual(float(value), float(expected), places=4) + @skipIf(compiler=["gcc"]) @add_test_categories(["libc++"]) def test_libcxx(self): self.build(dictionary={"USE_LIBCPP": 1}) self.do_test() + @add_test_categories(["libstdcxx"]) + def test_libstdcxx(self): + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test() + + # the data layout is different in new libstdc++ versions + @add_test_categories(["libstdcxx"]) + def test_libstdcxx_11(self): + self.build(dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": "-std=c++11"}) + self.do_test() + + @add_test_categories(["libstdcxx"]) + def test_libstdcxx_17(self): + self.build(dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": "-std=c++17"}) + 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. diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/main.cpp index ee77a880a0c5d..01d5db0a8d8c9 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/main.cpp @@ -17,6 +17,9 @@ struct S { int y = 2; }; +static void func() {} +using func_t = void (*)(); + int main() { std::atomic<S> s; s.store(S()); @@ -27,5 +30,15 @@ int main() { // Let the child node know what its parent is. p.child.parent = &p; + // libstdcpp has different layout depending on the data structure + std::atomic<bool> atomic_bool{true}; + std::atomic<float> atomic_float{3.14}; + std::atomic<double> atomic_double{6.28}; + + std::atomic<func_t> atomic_func(func); + + S data; + std::atomic<S *> atomic_pointer{&data}; + return 0; // Set break point at this line. } >From 4fc23c17e9c54876490c5bc4467e5c5872819c52 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Thu, 22 Jan 2026 23:01:37 +0000 Subject: [PATCH 2/4] add review changes and shared pointer version --- .../Plugins/Language/CPlusPlus/LibStdcpp.cpp | 53 ++++++++++++++++--- .../Plugins/Language/CPlusPlus/LibStdcpp.h | 6 +-- .../Language/CPlusPlus/LibStdcppAtomic.cpp | 16 +++++- .../atomic/TestDataFormatterStdAtomic.py | 22 +++++++- .../generic/atomic/main.cpp | 12 +++++ 5 files changed, 96 insertions(+), 13 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp index c06f50c1ca65d..c54c24cf5c5dc 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -403,9 +403,49 @@ lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator( return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp) : nullptr); } +static ValueObjectSP GetReferenceCountPointer(ValueObject &parent, + bool is_atomic_child) { + auto refcount = parent.GetChildMemberWithName("_M_refcount"); + if (!refcount) + return nullptr; + + if (!is_atomic_child) + return refcount->GetChildMemberWithName("_M_pi"); + + auto val = refcount->GetChildMemberWithName("_M_val"); + if (!val || !val->GetCompilerType()) + return nullptr; + + // May be a scalar value depending on libstdc++ version + if (!val->GetCompilerType().IsScalarType()) { + val = val->GetChildMemberWithName("_M_i"); + if (!val) + return nullptr; + } + + // Extract and align the raw value + bool is_success = false; + uint64_t raw_val = val->GetValueAsUnsigned(0, &is_success); + if (!is_success || raw_val == 0) + return nullptr; + // Clear the least significant bit. See + // https://github.com/gcc-mirror/gcc/blob/releases/gcc-15.2.0/libstdc%2B%2B-v3/include/bits/shared_ptr_atomic.h#L465 + raw_val &= ~1ULL; + + // Get the pointer type + auto ref_count_ptr = + refcount->GetCompilerType().GetDirectNestedTypeWithName("pointer"); + if (!ref_count_ptr) + return nullptr; + + return ValueObject::CreateValueObjectFromAddress( + "", raw_val, parent.GetExecutionContextRef(), ref_count_ptr, + /*do_deref=*/false); +} bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider( - ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options, + bool is_atomic_child) { ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue()); if (!valobj_sp) return false; @@ -416,11 +456,10 @@ bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider( DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options); - ValueObjectSP pi_sp = valobj_sp->GetChildAtNamePath({"_M_refcount", "_M_pi"}); + auto pi_sp = GetReferenceCountPointer(*valobj_sp, is_atomic_child); if (!pi_sp) return false; - - bool success; + bool success = false; uint64_t pi_addr = pi_sp->GetValueAsUnsigned(0, &success); // Empty control field. We're done. if (!success || pi_addr == 0) @@ -428,7 +467,7 @@ bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider( int64_t shared_count = 0; if (auto count_sp = pi_sp->GetChildMemberWithName("_M_use_count")) { - bool success; + bool success = false; shared_count = count_sp->GetValueAsSigned(0, &success); if (!success) return false; @@ -438,8 +477,8 @@ bool lldb_private::formatters::LibStdcppSmartPointerSummaryProvider( // _M_weak_count is the number of weak references + (_M_use_count != 0). if (auto weak_count_sp = pi_sp->GetChildMemberWithName("_M_weak_count")) { - bool success; - int64_t count = weak_count_sp->GetValueAsUnsigned(0, &success); + bool success = false; + uint64_t count = weak_count_sp->GetValueAsUnsigned(0, &success); if (!success) return false; diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h index f3b2408589e46..6b538805b2cdb 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -32,9 +32,9 @@ bool LibStdcppWStringViewSummaryProvider( const TypeSummaryOptions &options); // libstdc++ std::wstring_view bool LibStdcppSmartPointerSummaryProvider( - ValueObject &valobj, Stream &stream, - const TypeSummaryOptions - &options); // libstdc++ std::shared_ptr<> and std::weak_ptr<> + ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options, + bool is_atomic_child = + false); // libstdc++ std::shared_ptr<> and std::weak_ptr<> bool LibStdcppUniquePointerSummaryProvider( ValueObject &valobj, Stream &stream, diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp index d2e6b82ffb8b9..3e7c03bcaa7b1 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp @@ -63,10 +63,13 @@ class LibStdcppAtomicSyntheticFrontEnd final ValueObject &backend = *non_synthetic; const CompilerType type = backend.GetCompilerType(); - if (!type.IsValid() || type.GetNumTemplateArguments() < 1) + if (!type || type.GetNumTemplateArguments() < 1) return {}; const CompilerType first_type = type.GetTypeTemplateArgument(0); + if (!first_type) + return {}; + const lldb::BasicType basic_type = first_type.GetBasicTypeEnumeration(); if (basic_type == eBasicTypeBool) return backend.GetChildAtNamePath({"_M_base", "_M_i"}); @@ -83,6 +86,11 @@ class LibStdcppAtomicSyntheticFrontEnd final if (first_type.IsPointerType()) return backend.GetChildAtNamePath({"_M_b", "_M_p"}); + const auto first_typename = first_type.GetDisplayTypeName().GetStringRef(); + if (first_typename.starts_with("std::shared_ptr<") || + first_typename.starts_with("std::weak_ptr<")) + return backend.GetChildAtNamePath({"_M_impl", "_M_ptr"}); + return backend.GetChildMemberWithName("_M_i"); } @@ -116,6 +124,12 @@ bool LibStdcppAtomicSummaryProvider(ValueObject &valobj, Stream &stream, stream << summary; return true; } + + auto aparent = atomic_value->GetParent(); + if (aparent && aparent->GetName().GetStringRef() == "_M_impl") { + return LibStdcppSmartPointerSummaryProvider(*aparent, stream, options, + /*is_atomic_child=*/true); + } } return false; 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 ddad85b095362..f10517cb6b8f3 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): + def do_test(self, test_smart_pointers: bool = False): """Test that std::atomic is correctly printed by LLDB""" self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) @@ -73,7 +73,7 @@ def do_test(self): self.assertIsNotNone(val_bool, msg="atomic_bool child is None.") self.assertEqual(bool(val_bool.unsigned), True) - # func + # function atomic_func = self.get_variable("atomic_func") val_func = atomic_func.child[0] self.assertIsNotNone(val_func, msg="atomic_func child is None.") @@ -93,6 +93,19 @@ def do_test(self): "frame var p.child.parent", substrs=["p.child.parent = {\n Value = 0x"] ) + if test_smart_pointers: + # shared_pointer + atomic_shared = self.get_variable("atomic_shared") + val_shared = atomic_shared.child[0] + self.assertIsNotNone(val_shared, msg="atomic_shared child is None.") + self.assertEqual(300, val_shared.deref.unsigned) + + # weak_pointer + atomic_weak = self.get_variable("atomic_weak") + val_weak = atomic_weak.child[0] + self.assertIsNotNone(val_weak, msg="atomic_weak child is None.") + self.assertEqual(300, val_weak.deref.unsigned) + def verify_floating_point_equal(self, value: str, expected: float): self.assertAlmostEqual(float(value), float(expected), places=4) @@ -118,6 +131,11 @@ def test_libstdcxx_17(self): self.build(dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": "-std=c++17"}) self.do_test() + @add_test_categories(["libstdcxx"]) + def test_libstdcxx_20(self): + self.build(dictionary={"USE_LIBSTDCPP": 1, "CXXFLAGS_EXTRAS": "-std=c++20"}) + self.do_test(test_smart_pointers=True) + @add_test_categories(["msvcstl"]) def test_msvcstl(self): # No flags, because the "msvcstl" category checks that the MSVC STL is used by default. diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/main.cpp index 01d5db0a8d8c9..0b449c5879e45 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/atomic/main.cpp @@ -1,4 +1,9 @@ #include <atomic> +#include <version> + +#if __cpp_lib_atomic_shared_ptr +#include <memory> +#endif // __cpp_lib_atomic_shared_ptr // Define a Parent and Child struct that can point to each other. class Parent; @@ -40,5 +45,12 @@ int main() { S data; std::atomic<S *> atomic_pointer{&data}; + // smart atomic shared pointer was added in c++20 +#if __cpp_lib_atomic_shared_ptr + std::shared_ptr<int> s_value = std::make_shared<int>(300); + std::atomic<std::shared_ptr<int>> atomic_shared{s_value}; + std::atomic<std::weak_ptr<int>> atomic_weak{s_value}; +#endif // __cpp_lib_atomic_shared_ptr + return 0; // Set break point at this line. } >From 1a51a2944b21209ef17fa127042c2114fe025867 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Tue, 27 Jan 2026 09:31:15 +0000 Subject: [PATCH 3/4] add review changes --- .../Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp index 3e7c03bcaa7b1..8f8f79d7040c9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp @@ -21,13 +21,12 @@ namespace lldb_private::formatters { class LibStdcppAtomicSyntheticFrontEnd final : public SyntheticChildrenFrontEnd { public: - explicit LibStdcppAtomicSyntheticFrontEnd(ValueObject &valobj_sp) - : SyntheticChildrenFrontEnd(valobj_sp), - m_inner_name(ConstString("Value")) {} + explicit LibStdcppAtomicSyntheticFrontEnd(ValueObject &valobj) + : SyntheticChildrenFrontEnd(valobj), m_inner_name(ConstString("Value")) {} llvm::Expected<uint32_t> CalculateNumChildren() final { if (!m_inner) - return llvm::createStringError("invalide atomic ValueObject"); + return llvm::createStringError("Invalid atomic ValueObject."); return 1; } @@ -75,7 +74,7 @@ class LibStdcppAtomicSyntheticFrontEnd final return backend.GetChildAtNamePath({"_M_base", "_M_i"}); const uint32_t float_mask = lldb::eTypeIsFloat | lldb::eTypeIsBuiltIn; - if (first_type.GetTypeInfo() & float_mask) { + if ((first_type.GetTypeInfo() & float_mask) == float_mask) { // added float types specialization in c++17 if (const auto child = backend.GetChildMemberWithName("_M_fp")) return child; @@ -125,7 +124,7 @@ bool LibStdcppAtomicSummaryProvider(ValueObject &valobj, Stream &stream, return true; } - auto aparent = atomic_value->GetParent(); + ValueObject *aparent = atomic_value->GetParent(); if (aparent && aparent->GetName().GetStringRef() == "_M_impl") { return LibStdcppSmartPointerSummaryProvider(*aparent, stream, options, /*is_atomic_child=*/true); >From 905cb37e3ac8485ed6830d88a7faef9b1a0d1f35 Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Tue, 27 Jan 2026 11:56:45 +0000 Subject: [PATCH 4/4] add review changes --- lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp | 1 + .../Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp index c54c24cf5c5dc..028b27d866473 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -403,6 +403,7 @@ lldb_private::formatters::LibStdcppSharedPtrSyntheticFrontEndCreator( return (valobj_sp ? new LibStdcppSharedPtrSyntheticFrontEnd(valobj_sp) : nullptr); } + static ValueObjectSP GetReferenceCountPointer(ValueObject &parent, bool is_atomic_child) { auto refcount = parent.GetChildMemberWithName("_M_refcount"); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp index 8f8f79d7040c9..ec274993702ea 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp @@ -22,7 +22,7 @@ class LibStdcppAtomicSyntheticFrontEnd final : public SyntheticChildrenFrontEnd { public: explicit LibStdcppAtomicSyntheticFrontEnd(ValueObject &valobj) - : SyntheticChildrenFrontEnd(valobj), m_inner_name(ConstString("Value")) {} + : SyntheticChildrenFrontEnd(valobj) {} llvm::Expected<uint32_t> CalculateNumChildren() final { if (!m_inner) @@ -32,7 +32,7 @@ class LibStdcppAtomicSyntheticFrontEnd final ValueObjectSP GetChildAtIndex(uint32_t idx) final { if (idx == 0 && m_inner) - return m_inner->GetSP()->Clone(m_inner_name); + return m_inner->GetSP()->Clone(k_inner_name); return {}; } @@ -48,7 +48,7 @@ class LibStdcppAtomicSyntheticFrontEnd final } llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) final { - if (name == m_inner_name) + if (name == k_inner_name) return 0; return llvm::createStringError("Type has no child named '%s'", @@ -94,7 +94,7 @@ class LibStdcppAtomicSyntheticFrontEnd final } private: - ConstString m_inner_name; + inline static const ConstString k_inner_name = ConstString("Value"); ValueObject *m_inner = nullptr; }; @@ -104,7 +104,7 @@ LibStdcppAtomicSyntheticFrontEndCreator(CXXSyntheticChildren * /*unused*/, if (!valobj_sp) return nullptr; - const lldb::ValueObjectSP member = + const ValueObjectSP member = LibStdcppAtomicSyntheticFrontEnd::ContainerFieldName(*valobj_sp); if (!member) return nullptr; _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
