Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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,4 @@
CXX_SOURCES := main.cpp

USE_LIBSTDCPP := 1
CXXFLAGS_EXTRAS := -std=c++14

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