Author: Michael Buch Date: 2025-07-07T10:13:03+01:00 New Revision: 074ccde3b092ed231d344d4ffad50de93438196c
URL: https://github.com/llvm/llvm-project/commit/074ccde3b092ed231d344d4ffad50de93438196c DIFF: https://github.com/llvm/llvm-project/commit/074ccde3b092ed231d344d4ffad50de93438196c.diff LOG: [lldb][Formatter] Consolidate libstdc++ and libc++ unique_ptr formatter tests into generic test (#147031) The libc++ test was a subset of the tests in libstdc++. This test moves the libc++ test into `generic` and somne additional test-cases from `libstdc++` (specifically the recursive unique_ptr case). It turns out the libstdc++ formatter supports dereferencing using the "object" or "obj" names. We could either drop those from the tests or support the same for libc++. I took the latter approach but don't have strong opinions on this. Split out from https://github.com/llvm/llvm-project/pull/146740 Added: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/Makefile lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/main.cpp Modified: lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp Removed: lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/Makefile lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/Makefile lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/invalid/TestDataFormatterInvalidStdUniquePtr.py lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/invalid/main.cpp lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp ################################################################################ diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index 7ecb484e35474..3079de4936a85 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -411,7 +411,7 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd:: return 0; if (name == "deleter") return 1; - if (name == "$$dereference$$") + if (name == "obj" || name == "object" || name == "$$dereference$$") return 2; return llvm::createStringError("Type has no child named '%s'", name.AsCString()); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp index e570a4bb1a886..4daba2e364168 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp @@ -42,8 +42,7 @@ class LibStdcppUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd { // objects are only destroyed when every shared pointer to any of them // is destroyed, so we must not store a shared pointer to any ValueObject // derived from our backend ValueObject (since we're in the same cluster). - ValueObject* m_ptr_obj = nullptr; - ValueObject* m_obj_obj = nullptr; + ValueObject *m_ptr_obj = nullptr; ValueObject* m_del_obj = nullptr; ValueObjectSP GetTuple(); @@ -107,7 +106,6 @@ lldb::ChildCacheState LibStdcppUniquePtrSyntheticFrontEnd::Update() { if (del_obj) m_del_obj = del_obj->Clone(ConstString("deleter")).get(); } - m_obj_obj = nullptr; return lldb::ChildCacheState::eRefetch; } @@ -119,15 +117,13 @@ LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) { if (idx == 1 && m_del_obj) return m_del_obj->GetSP(); if (idx == 2) { - if (m_ptr_obj && !m_obj_obj) { - Status error; - ValueObjectSP obj_obj = m_ptr_obj->Dereference(error); - if (error.Success()) { - m_obj_obj = obj_obj->Clone(ConstString("object")).get(); + if (m_ptr_obj) { + Status status; + auto value_sp = m_ptr_obj->Dereference(status); + if (status.Success()) { + return value_sp; } } - if (m_obj_obj) - return m_obj_obj->GetSP(); } return lldb::ValueObjectSP(); } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/Makefile similarity index 61% rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/Makefile rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/Makefile index bf8e6b8703f36..a27336ffd9acd 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/Makefile +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/Makefile @@ -1,5 +1,4 @@ CXX_SOURCES := main.cpp - -USE_LIBSTDCPP := 1 +CXXFLAGS_EXTRAS := -std=c++14 include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py similarity index 55% rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py index 25a1cd82a4baa..1b204c7d6ef02 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/TestDataFormatterStdUniquePtr.py @@ -1,8 +1,7 @@ """ -Test lldb data formatter for libc++ std::unique_ptr. +Test lldb data formatter for std::unique_ptr. """ - import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -10,32 +9,8 @@ class TestCase(TestBase): - def make_expected_type(self, pointee_type: str, qualifiers: str = "") -> str: - if qualifiers: - qualifiers = " " + qualifiers - - if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion( - [">", "16.0"] - ): - return f"std::unique_ptr<{pointee_type}>{qualifiers}" - else: - return f"std::unique_ptr<{pointee_type}, std::default_delete<{pointee_type}> >{qualifiers}" - - def make_expected_basic_string_ptr(self) -> str: - if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion( - [">", "16.0"] - ): - return f"std::unique_ptr<std::string>" - else: - return ( - "std::unique_ptr<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, " - "std::default_delete<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >" - ) - - @add_test_categories(["libc++"]) - def test_unique_ptr_variables(self): + def do_test(self): """Test `frame variable` output for `std::unique_ptr` types.""" - self.build() lldbutil.run_to_source_breakpoint( self, "// break here", lldb.SBFileSpec("main.cpp") @@ -43,7 +18,6 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_empty", - type=self.make_expected_type("int"), summary="nullptr", children=[ValueCheck(name="pointer")], ) @@ -57,7 +31,6 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_int", - type=self.make_expected_type("int"), summary="10", children=[ValueCheck(name="pointer")], ) @@ -65,7 +38,6 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_int_ref", - type=self.make_expected_type("int", qualifiers="&"), summary="10", children=[ValueCheck(name="pointer")], ) @@ -73,7 +45,6 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_int_ref_ref", - type=self.make_expected_type("int", qualifiers="&&"), summary="10", children=[ValueCheck(name="pointer")], ) @@ -81,12 +52,11 @@ def test_unique_ptr_variables(self): valobj = self.expect_var_path( "up_str", - type=self.make_expected_basic_string_ptr(), summary='"hello"', children=[ValueCheck(name="pointer", summary='"hello"')], ) - valobj = self.expect_var_path("up_user", type=self.make_expected_type("User")) + valobj = self.expect_var_path("up_user") self.assertRegex(valobj.summary, "^User @ 0x0*[1-9a-f][0-9a-f]+$") self.assertNotEqual(valobj.child[0].unsigned, 0) @@ -121,3 +91,67 @@ def test_unique_ptr_variables(self): self.expect_var_path("ptr_node->next->value", value="2") self.expect_var_path("(*ptr_node).value", value="1") self.expect_var_path("(*(*ptr_node).next).value", value="2") + + @add_test_categories(["libstdcxx"]) + def test_libstdcxx(self): + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test() + + @add_test_categories(["libc++"]) + def test_libcxx(self): + self.build(dictionary={"USE_LIBCPP": 1}) + self.do_test() + + def do_test_recursive_unique_ptr(self): + # Tests that LLDB can handle when we have a loop in the unique_ptr + # reference chain and that it correctly handles the diff erent options + # for the frame variable command in this case. + self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp(self, "Set break point at this line.") + self.runCmd("run", RUN_SUCCEEDED) + self.expect( + "thread list", + STOPPED_DUE_TO_BREAKPOINT, + substrs=["stopped", "stop reason = breakpoint"], + ) + + self.expect("frame variable f1->next", substrs=["next = NodeU @"]) + self.expect( + "frame variable --ptr-depth=1 f1->next", + substrs=["next = NodeU @", "value = 2"], + ) + self.expect( + "frame variable --ptr-depth=2 f1->next", + substrs=["next = NodeU @", "value = 1", "value = 2"], + ) + + frame = self.frame() + self.assertTrue(frame.IsValid()) + self.assertEqual( + 2, + frame.GetValueForVariablePath("f1->next.object.value").GetValueAsUnsigned(), + ) + self.assertEqual( + 2, frame.GetValueForVariablePath("f1->next->value").GetValueAsUnsigned() + ) + self.assertEqual( + 1, + frame.GetValueForVariablePath( + "f1->next.object.next.obj.value" + ).GetValueAsUnsigned(), + ) + self.assertEqual( + 1, + frame.GetValueForVariablePath("f1->next->next->value").GetValueAsUnsigned(), + ) + + @add_test_categories(["libstdcxx"]) + def test_recursive_unique_ptr_libstdcxx(self): + self.build(dictionary={"USE_LIBSTDCPP": 1}) + self.do_test_recursive_unique_ptr() + + @add_test_categories(["libc++"]) + def test_recursive_unique_ptr_libcxx(self): + self.build(dictionary={"USE_LIBCPP": 1}) + self.do_test_recursive_unique_ptr() diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/main.cpp similarity index 74% rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/main.cpp index afdddf0bbaf16..15e9f70dbd6aa 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/main.cpp @@ -15,11 +15,20 @@ struct NodeU { // representation when the type of the second element is an empty class. So // we need a deleter class with a dummy data member to trigger the other path. struct NonEmptyIntDeleter { - void operator()(int* ptr) { delete ptr; } + void operator()(int *ptr) { delete ptr; } int dummy_ = 9999; }; +static void recursive() { + // Set up a structure where we have a loop in the unique_ptr chain. + NodeU *f1 = new NodeU{nullptr, 1}; + NodeU *f2 = new NodeU{nullptr, 2}; + f1->next.reset(f2); + f2->next.reset(f1); + std::puts("Set break point at this line."); +} + int main() { std::unique_ptr<int> up_empty; std::unique_ptr<int> up_int = std::make_unique<int>(10); @@ -33,5 +42,9 @@ int main() { std::unique_ptr<NodeU>(new NodeU{nullptr, 2}); ptr_node = std::unique_ptr<NodeU>(new NodeU{std::move(ptr_node), 1}); - return 0; // break here + std::puts("// break here"); + + recursive(); + + return 0; } diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/Makefile deleted file mode 100644 index c1c8b4a2a0a53..0000000000000 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -CXX_SOURCES := main.cpp - -USE_LIBCPP := 1 - -# We need debug info tuning for lldb in order to emit the preferred name for -# std::string. See https://reviews.llvm.org/D145803. -CXXFLAGS_EXTRAS := -std=c++14 -glldb -include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py deleted file mode 100644 index 8f57dc88f3187..0000000000000 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/TestDataFormatterStdUniquePtr.py +++ /dev/null @@ -1,134 +0,0 @@ -""" -Test lldb data formatter subsystem. -""" - -import lldb -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -from lldbsuite.test import lldbutil - - -class StdUniquePtrDataFormatterTestCase(TestBase): - @add_test_categories(["libstdcxx"]) - @expectedFailureAll(bugnumber="llvm.org/pr50861", compiler="gcc") - @skipIf(oslist=["linux"], archs=["arm$", "aarch64"]) - def test_with_run_command(self): - self.build() - self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) - - lldbutil.run_break_set_by_source_regexp(self, "Set break point at this line.") - self.runCmd("run", RUN_SUCCEEDED) - - # The stop reason of the thread should be breakpoint. - self.expect( - "thread list", - STOPPED_DUE_TO_BREAKPOINT, - substrs=["stopped", "stop reason = breakpoint"], - ) - - frame = self.frame() - self.assertTrue(frame.IsValid()) - - self.expect("frame variable nup", substrs=["nup = nullptr"]) - self.expect("frame variable iup", substrs=["iup = 123"]) - self.expect("frame variable sup", substrs=['sup = "foobar"']) - - self.expect("frame variable ndp", substrs=["ndp = nullptr"]) - self.expect( - "frame variable idp", substrs=["idp = 456", "deleter = ", "a = 1", "b = 2"] - ) - self.expect( - "frame variable sdp", - substrs=['sdp = "baz"', "deleter = ", "a = 3", "b = 4"], - ) - - self.assertEqual( - 123, frame.GetValueForVariablePath("iup.object").GetValueAsUnsigned() - ) - self.assertEqual( - 123, frame.GetValueForVariablePath("*iup").GetValueAsUnsigned() - ) - self.assertFalse(frame.GetValueForVariablePath("iup.deleter").IsValid()) - - self.assertEqual( - '"foobar"', frame.GetValueForVariablePath("sup.object").GetSummary() - ) - self.assertEqual('"foobar"', frame.GetValueForVariablePath("*sup").GetSummary()) - self.assertFalse(frame.GetValueForVariablePath("sup.deleter").IsValid()) - - self.assertEqual( - 456, frame.GetValueForVariablePath("idp.object").GetValueAsUnsigned() - ) - self.assertEqual( - 456, frame.GetValueForVariablePath("*idp").GetValueAsUnsigned() - ) - self.assertEqual( - '"baz"', frame.GetValueForVariablePath("sdp.object").GetSummary() - ) - self.assertEqual('"baz"', frame.GetValueForVariablePath("*sdp").GetSummary()) - - idp_deleter = frame.GetValueForVariablePath("idp.deleter") - self.assertTrue(idp_deleter.IsValid()) - self.assertEqual( - 1, idp_deleter.GetChildMemberWithName("a").GetValueAsUnsigned() - ) - self.assertEqual( - 2, idp_deleter.GetChildMemberWithName("b").GetValueAsUnsigned() - ) - - sdp_deleter = frame.GetValueForVariablePath("sdp.deleter") - self.assertTrue(sdp_deleter.IsValid()) - self.assertEqual( - 3, sdp_deleter.GetChildMemberWithName("a").GetValueAsUnsigned() - ) - self.assertEqual( - 4, sdp_deleter.GetChildMemberWithName("b").GetValueAsUnsigned() - ) - - @skipIfFreeBSD - @skipIfWindows # libstdcpp not ported to Windows - @skipIfDarwin # doesn't compile on Darwin - @skipIfwatchOS # libstdcpp not ported to watchos - @skipIf(oslist=["linux"], archs=["arm$", "aarch64"]) - @add_test_categories(["libstdcxx"]) - def test_recursive_unique_ptr(self): - # Tests that LLDB can handle when we have a loop in the unique_ptr - # reference chain and that it correctly handles the diff erent options - # for the frame variable command in this case. - self.build() - self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) - - lldbutil.run_break_set_by_source_regexp(self, "Set break point at this line.") - self.runCmd("run", RUN_SUCCEEDED) - self.expect( - "thread list", - STOPPED_DUE_TO_BREAKPOINT, - substrs=["stopped", "stop reason = breakpoint"], - ) - - self.expect("frame variable f1->fp", substrs=["fp = Foo @ 0x"]) - self.expect( - "frame variable --ptr-depth=1 f1->fp", substrs=["data = 2", "fp = Foo @ 0x"] - ) - self.expect( - "frame variable --ptr-depth=2 f1->fp", - substrs=["data = 2", "fp = Foo @ 0x", "data = 1"], - ) - - frame = self.frame() - self.assertTrue(frame.IsValid()) - self.assertEqual( - 2, frame.GetValueForVariablePath("f1->fp.object.data").GetValueAsUnsigned() - ) - self.assertEqual( - 2, frame.GetValueForVariablePath("f1->fp->data").GetValueAsUnsigned() - ) - self.assertEqual( - 1, - frame.GetValueForVariablePath( - "f1->fp.object.fp.object.data" - ).GetValueAsUnsigned(), - ) - self.assertEqual( - 1, frame.GetValueForVariablePath("f1->fp->fp->data").GetValueAsUnsigned() - ) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/invalid/TestDataFormatterInvalidStdUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/invalid/TestDataFormatterInvalidStdUniquePtr.py deleted file mode 100644 index 9cacfce989769..0000000000000 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/invalid/TestDataFormatterInvalidStdUniquePtr.py +++ /dev/null @@ -1,4 +0,0 @@ -import lldbsuite.test.lldbinline as lldbinline -from lldbsuite.test.decorators import * - -lldbinline.MakeInlineTest(__file__, globals(), [no_debug_info_test]) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/invalid/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/invalid/main.cpp deleted file mode 100644 index b12cab231695b..0000000000000 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/invalid/main.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// Test that we don't crash when trying to pretty-print structures that don't -// have the layout our data formatters expect. -namespace std { -template<typename T, typename Deleter = void> -class unique_ptr {}; -} - -int main() { - std::unique_ptr<int> U; - return 0; //% self.expect("frame variable U", substrs=["unique_ptr", "{}"]) -} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp deleted file mode 100644 index dd0072764d4e6..0000000000000 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/unique_ptr/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <memory> -#include <string> - -struct Deleter { - void operator()(void *) {} - - int a; - int b; -}; - -struct Foo { - int data; - std::unique_ptr<Foo> fp; -}; - -int main() { - std::unique_ptr<char> nup; - std::unique_ptr<int> iup(new int{123}); - std::unique_ptr<std::string> sup(new std::string("foobar")); - - std::unique_ptr<char, Deleter> ndp; - std::unique_ptr<int, Deleter> idp(new int{456}, Deleter{1, 2}); - std::unique_ptr<std::string, Deleter> sdp(new std::string("baz"), - Deleter{3, 4}); - - std::unique_ptr<Foo> fp(new Foo{3}); - - // Set up a structure where we have a loop in the unique_ptr chain. - Foo* f1 = new Foo{1}; - Foo* f2 = new Foo{2}; - f1->fp.reset(f2); - f2->fp.reset(f1); - - return 0; // Set break point at this line. -} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits