Skip to content

Commit 7845ee2

Browse files
committed
[lldb][Formatter] Consolidate libstdc++ and libc++ unique_ptr formatter tests into generic test (llvm#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 llvm#146740 (cherry picked from commit 074ccde)
1 parent d54d47c commit 7845ee2

File tree

10 files changed

+98
-240
lines changed

10 files changed

+98
-240
lines changed

lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ size_t lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::
417417
return 0;
418418
if (name == "deleter")
419419
return 1;
420-
if (name == "$$dereference$$")
420+
if (name == "obj" || name == "object" || name == "$$dereference$$")
421421
return 2;
422422
return UINT32_MAX;
423423
}

lldb/source/Plugins/Language/CPlusPlus/LibStdcppUniquePointer.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ class LibStdcppUniquePtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
4444
// objects are only destroyed when every shared pointer to any of them
4545
// is destroyed, so we must not store a shared pointer to any ValueObject
4646
// derived from our backend ValueObject (since we're in the same cluster).
47-
ValueObject* m_ptr_obj = nullptr;
48-
ValueObject* m_obj_obj = nullptr;
47+
ValueObject *m_ptr_obj = nullptr;
4948
ValueObject* m_del_obj = nullptr;
5049

5150
ValueObjectSP GetTuple();
@@ -109,7 +108,6 @@ lldb::ChildCacheState LibStdcppUniquePtrSyntheticFrontEnd::Update() {
109108
if (del_obj)
110109
m_del_obj = del_obj->Clone(ConstString("deleter")).get();
111110
}
112-
m_obj_obj = nullptr;
113111

114112
return lldb::ChildCacheState::eRefetch;
115113
}
@@ -123,15 +121,13 @@ LibStdcppUniquePtrSyntheticFrontEnd::GetChildAtIndex(uint32_t idx) {
123121
if (idx == 1 && m_del_obj)
124122
return m_del_obj->GetSP();
125123
if (idx == 2) {
126-
if (m_ptr_obj && !m_obj_obj) {
127-
Status error;
128-
ValueObjectSP obj_obj = m_ptr_obj->Dereference(error);
129-
if (error.Success()) {
130-
m_obj_obj = obj_obj->Clone(ConstString("object")).get();
124+
if (m_ptr_obj) {
125+
Status status;
126+
auto value_sp = m_ptr_obj->Dereference(status);
127+
if (status.Success()) {
128+
return value_sp;
131129
}
132130
}
133-
if (m_obj_obj)
134-
return m_obj_obj->GetSP();
135131
}
136132
return lldb::ValueObjectSP();
137133
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
CXX_SOURCES := main.cpp
2-
3-
USE_LIBSTDCPP := 1
2+
CXXFLAGS_EXTRAS := -std=c++14
43

54
include Makefile.rules
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,23 @@
11
"""
2-
Test lldb data formatter for libc++ std::unique_ptr.
2+
Test lldb data formatter for std::unique_ptr.
33
"""
44

5-
65
import lldb
76
from lldbsuite.test.decorators import *
87
from lldbsuite.test.lldbtest import *
98
from lldbsuite.test import lldbutil
109

1110

1211
class TestCase(TestBase):
13-
def make_expected_type(self, pointee_type: str, qualifiers: str = "") -> str:
14-
if qualifiers:
15-
qualifiers = " " + qualifiers
16-
17-
if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion(
18-
[">", "16.0"]
19-
):
20-
return f"std::unique_ptr<{pointee_type}>{qualifiers}"
21-
else:
22-
return f"std::unique_ptr<{pointee_type}, std::default_delete<{pointee_type}> >{qualifiers}"
23-
24-
def make_expected_basic_string_ptr(self) -> str:
25-
if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion(
26-
[">", "16.0"]
27-
):
28-
return f"std::unique_ptr<std::string>"
29-
else:
30-
return (
31-
"std::unique_ptr<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, "
32-
"std::default_delete<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >"
33-
)
34-
35-
@add_test_categories(["libc++"])
36-
def test_unique_ptr_variables(self):
12+
def do_test(self):
3713
"""Test `frame variable` output for `std::unique_ptr` types."""
38-
self.build()
3914

4015
lldbutil.run_to_source_breakpoint(
4116
self, "// break here", lldb.SBFileSpec("main.cpp")
4217
)
4318

4419
valobj = self.expect_var_path(
4520
"up_empty",
46-
type=self.make_expected_type("int"),
4721
summary="nullptr",
4822
children=[ValueCheck(name="pointer")],
4923
)
@@ -57,36 +31,32 @@ def test_unique_ptr_variables(self):
5731

5832
valobj = self.expect_var_path(
5933
"up_int",
60-
type=self.make_expected_type("int"),
6134
summary="10",
6235
children=[ValueCheck(name="pointer")],
6336
)
6437
self.assertNotEqual(valobj.child[0].unsigned, 0)
6538

6639
valobj = self.expect_var_path(
6740
"up_int_ref",
68-
type=self.make_expected_type("int", qualifiers="&"),
6941
summary="10",
7042
children=[ValueCheck(name="pointer")],
7143
)
7244
self.assertNotEqual(valobj.child[0].unsigned, 0)
7345

7446
valobj = self.expect_var_path(
7547
"up_int_ref_ref",
76-
type=self.make_expected_type("int", qualifiers="&&"),
7748
summary="10",
7849
children=[ValueCheck(name="pointer")],
7950
)
8051
self.assertNotEqual(valobj.child[0].unsigned, 0)
8152

8253
valobj = self.expect_var_path(
8354
"up_str",
84-
type=self.make_expected_basic_string_ptr(),
8555
summary='"hello"',
8656
children=[ValueCheck(name="pointer", summary='"hello"')],
8757
)
8858

89-
valobj = self.expect_var_path("up_user", type=self.make_expected_type("User"))
59+
valobj = self.expect_var_path("up_user")
9060
self.assertRegex(valobj.summary, "^User @ 0x0*[1-9a-f][0-9a-f]+$")
9161
self.assertNotEqual(valobj.child[0].unsigned, 0)
9262

@@ -115,3 +85,67 @@ def test_unique_ptr_variables(self):
11585

11686
self.expect_var_path("up_user->id", type="int", value="30")
11787
self.expect_var_path("up_user->name", type="std::string", summary='"steph"')
88+
89+
@add_test_categories(["libstdcxx"])
90+
def test_libstdcxx(self):
91+
self.build(dictionary={"USE_LIBSTDCPP": 1})
92+
self.do_test()
93+
94+
@add_test_categories(["libc++"])
95+
def test_libcxx(self):
96+
self.build(dictionary={"USE_LIBCPP": 1})
97+
self.do_test()
98+
99+
def do_test_recursive_unique_ptr(self):
100+
# Tests that LLDB can handle when we have a loop in the unique_ptr
101+
# reference chain and that it correctly handles the different options
102+
# for the frame variable command in this case.
103+
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
104+
105+
lldbutil.run_break_set_by_source_regexp(self, "Set break point at this line.")
106+
self.runCmd("run", RUN_SUCCEEDED)
107+
self.expect(
108+
"thread list",
109+
STOPPED_DUE_TO_BREAKPOINT,
110+
substrs=["stopped", "stop reason = breakpoint"],
111+
)
112+
113+
self.expect("frame variable f1->next", substrs=["next = NodeU @"])
114+
self.expect(
115+
"frame variable --ptr-depth=1 f1->next",
116+
substrs=["next = NodeU @", "value = 2"],
117+
)
118+
self.expect(
119+
"frame variable --ptr-depth=2 f1->next",
120+
substrs=["next = NodeU @", "value = 1", "value = 2"],
121+
)
122+
123+
frame = self.frame()
124+
self.assertTrue(frame.IsValid())
125+
self.assertEqual(
126+
2,
127+
frame.GetValueForVariablePath("f1->next.object.value").GetValueAsUnsigned(),
128+
)
129+
self.assertEqual(
130+
2, frame.GetValueForVariablePath("f1->next->value").GetValueAsUnsigned()
131+
)
132+
self.assertEqual(
133+
1,
134+
frame.GetValueForVariablePath(
135+
"f1->next.object.next.obj.value"
136+
).GetValueAsUnsigned(),
137+
)
138+
self.assertEqual(
139+
1,
140+
frame.GetValueForVariablePath("f1->next->next->value").GetValueAsUnsigned(),
141+
)
142+
143+
@add_test_categories(["libstdcxx"])
144+
def test_recursive_unique_ptr_libstdcxx(self):
145+
self.build(dictionary={"USE_LIBSTDCPP": 1})
146+
self.do_test_recursive_unique_ptr()
147+
148+
@add_test_categories(["libc++"])
149+
def test_recursive_unique_ptr_libcxx(self):
150+
self.build(dictionary={"USE_LIBCPP": 1})
151+
self.do_test_recursive_unique_ptr()

lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/main.cpp renamed to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unique_ptr/main.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,29 @@ struct User {
66
std::string name = "steph";
77
};
88

9+
struct NodeU {
10+
std::unique_ptr<NodeU> next;
11+
int value;
12+
};
13+
914
// libc++ stores unique_ptr data in a compressed pair, which has a specialized
1015
// representation when the type of the second element is an empty class. So
1116
// we need a deleter class with a dummy data member to trigger the other path.
1217
struct NonEmptyIntDeleter {
13-
void operator()(int* ptr) { delete ptr; }
18+
void operator()(int *ptr) { delete ptr; }
1419

1520
int dummy_ = 9999;
1621
};
1722

23+
static void recursive() {
24+
// Set up a structure where we have a loop in the unique_ptr chain.
25+
NodeU *f1 = new NodeU{nullptr, 1};
26+
NodeU *f2 = new NodeU{nullptr, 2};
27+
f1->next.reset(f2);
28+
f2->next.reset(f1);
29+
std::puts("Set break point at this line.");
30+
}
31+
1832
int main() {
1933
std::unique_ptr<int> up_empty;
2034
std::unique_ptr<int> up_int = std::make_unique<int>(10);
@@ -24,6 +38,13 @@ int main() {
2438
std::unique_ptr<User> up_user = std::make_unique<User>();
2539
auto up_non_empty_deleter =
2640
std::unique_ptr<int, NonEmptyIntDeleter>(new int(1234));
41+
std::unique_ptr<NodeU> ptr_node =
42+
std::unique_ptr<NodeU>(new NodeU{nullptr, 2});
43+
ptr_node = std::unique_ptr<NodeU>(new NodeU{std::move(ptr_node), 1});
44+
45+
std::puts("// break here");
46+
47+
recursive();
2748

28-
return 0; // break here
49+
return 0;
2950
}

lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/Makefile

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)