Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand All @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
CXX_SOURCES := main.cpp

USE_LIBSTDCPP := 1

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -1,49 +1,23 @@
"""
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 *
from lldbsuite.test import lldbutil


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")
)

valobj = self.expect_var_path(
"up_empty",
type=self.make_expected_type("int"),
summary="nullptr",
children=[ValueCheck(name="pointer")],
)
Expand All @@ -57,36 +31,32 @@ 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")],
)
self.assertNotEqual(valobj.child[0].unsigned, 0)

valobj = self.expect_var_path(
"up_int_ref",
type=self.make_expected_type("int", qualifiers="&"),
summary="10",
children=[ValueCheck(name="pointer")],
)
self.assertNotEqual(valobj.child[0].unsigned, 0)

valobj = self.expect_var_path(
"up_int_ref_ref",
type=self.make_expected_type("int", qualifiers="&&"),
summary="10",
children=[ValueCheck(name="pointer")],
)
self.assertNotEqual(valobj.child[0].unsigned, 0)

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)

Expand Down Expand Up @@ -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 different 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()
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading