https://github.com/da-viper created https://github.com/llvm/llvm-project/pull/174218
None >From 6d80ae77fea736fbaa638e768880a4cd2c2e37cc Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Wed, 19 Nov 2025 18:14:43 +0000 Subject: [PATCH 1/2] [lldb] update the representation --- .../Plugins/Language/CPlusPlus/CMakeLists.txt | 1 + .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 32 +++-- .../Plugins/Language/CPlusPlus/LibStdcpp.h | 7 ++ .../Language/CPlusPlus/LibStdcppAtomic.cpp | 112 ++++++++++++++++++ 4 files changed, 145 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 c52d3bdb31284..159dfc59e4d9a 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 dd3b84e47dec3..6d30551e1c85b 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -27,6 +27,7 @@ #include "lldb/DataFormatters/CXXFunctionPointer.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/TypeSynthetic.h" #include "lldb/DataFormatters/VectorType.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/SymbolFile.h" @@ -1517,6 +1518,24 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { stl_summary_flags, true); } +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) { @@ -1714,6 +1733,9 @@ static void LoadCommonStlFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { }, "MSVC STL/libstdc++ std::wstring 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. @@ -1768,6 +1790,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); @@ -1882,13 +1907,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 8d2c81f2bbcbb..91904a62a0771 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -33,10 +33,17 @@ bool LibStdcppVariantSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options); // libstdc++ std::variant<> +bool LibStdcppAtomicSummaryProvider(ValueObject &valobj, Stream &stream, + const TypeSummaryOptions &options); + SyntheticChildrenFrontEnd * LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +SyntheticChildrenFrontEnd * +LibStdcppAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + SyntheticChildrenFrontEnd * LibStdcppSpanSyntheticFrontEndCreator(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..7eed6d2c5b59e --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp @@ -0,0 +1,112 @@ +//===---------------------------------------------------------------------===// +// +// 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" + +using namespace lldb; + +namespace lldb_private::formatters { + +class LibStdcppAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd { +public: + explicit LibStdcppAtomicSyntheticFrontEnd(const ValueObjectSP &valobj_sp) + : SyntheticChildrenFrontEnd(*valobj_sp), + m_inner_name(ConstString("Value")) {} + + llvm::Expected<uint32_t> CalculateNumChildren() final { + return m_inner ? 1 : 0; + } + + 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_sythetic = backend_syn.GetNonSyntheticValue(); + if (!non_sythetic) + return {}; + ValueObject &backend = *non_sythetic; + + 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"}); + + if (first_type.GetTypeInfo() & lldb::eTypeIsFloat) { + // the location changed in c++ 20 + 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{}; +}; + +SyntheticChildrenFrontEnd * +LibStdcppAtomicSyntheticFrontEndCreator(CXXSyntheticChildren * /*unused*/, + lldb::ValueObjectSP valobj_sp) { + return (valobj_sp ? new LibStdcppAtomicSyntheticFrontEnd(valobj_sp) + : nullptr); +} + +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 >From edca8bf4132d7d57c45b6c9b6a15e3f692921cfd Mon Sep 17 00:00:00 2001 From: Ebuka Ezike <[email protected]> Date: Wed, 31 Dec 2025 18:26:20 +0000 Subject: [PATCH 2/2] [lldb] add test cases --- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 1 - .../Plugins/Language/CPlusPlus/LibStdcpp.h | 2 +- .../Language/CPlusPlus/LibStdcppAtomic.cpp | 24 +++++++--- .../atomic/TestDataFormatterStdAtomic.py | 48 +++++++++++++++++++ .../generic/atomic/main.cpp | 13 +++++ 5 files changed, 79 insertions(+), 9 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 6d30551e1c85b..8c4a1335dddf3 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -27,7 +27,6 @@ #include "lldb/DataFormatters/CXXFunctionPointer.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormattersHelpers.h" -#include "lldb/DataFormatters/TypeSynthetic.h" #include "lldb/DataFormatters/VectorType.h" #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/SymbolFile.h" diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h index 91904a62a0771..e91f91181425a 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.h @@ -42,7 +42,7 @@ LibstdcppMapIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *, SyntheticChildrenFrontEnd * LibStdcppAtomicSyntheticFrontEndCreator(CXXSyntheticChildren *, - lldb::ValueObjectSP); + const lldb::ValueObjectSP &); SyntheticChildrenFrontEnd * LibStdcppSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp index 7eed6d2c5b59e..ff7ee13343873 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppAtomic.cpp @@ -53,10 +53,10 @@ class LibStdcppAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd { } static ValueObjectSP ContainerFieldName(ValueObject &backend_syn) { - const ValueObjectSP non_sythetic = backend_syn.GetNonSyntheticValue(); - if (!non_sythetic) + const ValueObjectSP non_synthetic = backend_syn.GetNonSyntheticValue(); + if (!non_synthetic || !non_synthetic.get()) return {}; - ValueObject &backend = *non_sythetic; + ValueObject &backend = *non_synthetic; const CompilerType type = backend.GetCompilerType(); if (!type.IsValid() || type.GetNumTemplateArguments() < 1) @@ -68,7 +68,7 @@ class LibStdcppAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd { return backend.GetChildAtNamePath({"_M_base", "_M_i"}); if (first_type.GetTypeInfo() & lldb::eTypeIsFloat) { - // the location changed in c++ 20 + // added float types specialization in c++17 if (const auto child = backend.GetChildMemberWithName("_M_fp")) return child; @@ -88,9 +88,19 @@ class LibStdcppAtomicSyntheticFrontEnd : public SyntheticChildrenFrontEnd { SyntheticChildrenFrontEnd * LibStdcppAtomicSyntheticFrontEndCreator(CXXSyntheticChildren * /*unused*/, - lldb::ValueObjectSP valobj_sp) { - return (valobj_sp ? new LibStdcppAtomicSyntheticFrontEnd(valobj_sp) - : nullptr); + const lldb::ValueObjectSP &valobj_sp) { + if (!valobj_sp) + return nullptr; + + auto valobj = valobj_sp->GetNonSyntheticValue(); + if (!valobj || !valobj.get()) + return nullptr; + + auto member = LibStdcppAtomicSyntheticFrontEnd::ContainerFieldName(*valobj); + if (!member || !member.get()) + return nullptr; + + return new LibStdcppAtomicSyntheticFrontEnd(valobj_sp); } bool LibStdcppAtomicSummaryProvider(ValueObject &valobj, Stream &stream, 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. } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
