Skip to content

[lldb][DataFormatter] Format libstdc++ unique_ptr like we do libc++ #146909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 4, 2025
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
5 changes: 5 additions & 0 deletions lldb/include/lldb/DataFormatters/FormattersHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ void AddFilter(TypeCategoryImpl::SharedPointer category_sp,

std::optional<size_t> ExtractIndexFromString(const char *item_name);

/// Prints the summary for the pointer value of a C++
/// std::unique_ptr/std::shared_ptr/std::weak_ptr.
void DumpCxxSmartPtrPointerSummary(Stream &stream, ValueObject &ptr,
const TypeSummaryOptions &options);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return value is a bit awkward. Happy to consider alternatives.

Copy link
Collaborator

@labath labath Jul 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it actually needed though? In my experiments, a normally constructed empty shared pointer will have the __ctrl_ field as null anyway.

I was able to construct a nullptr shared_ptr with a non-empty control field using the subobject constructor (std::shared_ptr<int> si(new int(47)); std::shared_ptr<int> sie(si, nullptr);), but in that case, maybe we actually should show the weak and shared counts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, don't think the early return is necessary. Let me add that subobject constructor test too

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied your suggestion (and refactored the weak/strong count logic in the latest commit). Happy to split that up into a separate PR if this is becoming too noisy.


Address GetArrayAddressOrPointerValue(ValueObject &valobj);

time_t GetOSXEpoch();
Expand Down
19 changes: 19 additions & 0 deletions lldb/source/DataFormatters/FormattersHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,22 @@ lldb_private::formatters::GetArrayAddressOrPointerValue(ValueObject &valobj) {

return data_addr.address;
}

void lldb_private::formatters::DumpCxxSmartPtrPointerSummary(
Stream &stream, ValueObject &ptr, const TypeSummaryOptions &options) {
if (ptr.GetValueAsUnsigned(0) == 0) {
stream.Printf("nullptr");
return;
}

Status error;
ValueObjectSP pointee_sp = ptr.Dereference(error);
if (!pointee_sp || !error.Success())
return;

if (!pointee_sp->DumpPrintableRepresentation(
stream, ValueObject::eValueObjectRepresentationStyleSummary,
lldb::eFormatInvalid,
ValueObject::PrintableRepresentationSpecialCases::eDisable, false))
stream.Printf("ptr = 0x%" PRIx64, ptr.GetValueAsUnsigned(0));
}
70 changes: 26 additions & 44 deletions lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,39 +157,38 @@ bool lldb_private::formatters::LibcxxSmartPointerSummaryProvider(
ValueObjectSP valobj_sp(valobj.GetNonSyntheticValue());
if (!valobj_sp)
return false;
ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_"));
ValueObjectSP count_sp(
valobj_sp->GetChildAtNamePath({"__cntrl_", "__shared_owners_"}));
ValueObjectSP weakcount_sp(
valobj_sp->GetChildAtNamePath({"__cntrl_", "__shared_weak_owners_"}));

if (!ptr_sp)
ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_"));
ValueObjectSP ctrl_sp(valobj_sp->GetChildMemberWithName("__cntrl_"));
if (!ctrl_sp || !ptr_sp)
return false;

if (ptr_sp->GetValueAsUnsigned(0) == 0) {
stream.Printf("nullptr");
DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);

bool success;
uint64_t ctrl_addr = ctrl_sp->GetValueAsUnsigned(0, &success);
// Empty control field. We're done.
if (!success || ctrl_addr == 0)
return true;
} else {
bool print_pointee = false;
Status error;
ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
if (pointee_sp && error.Success()) {
if (pointee_sp->DumpPrintableRepresentation(
stream, ValueObject::eValueObjectRepresentationStyleSummary,
lldb::eFormatInvalid,
ValueObject::PrintableRepresentationSpecialCases::eDisable,
false))
print_pointee = true;
}
if (!print_pointee)
stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));

if (auto count_sp = ctrl_sp->GetChildMemberWithName("__shared_owners_")) {
bool success;
uint64_t count = count_sp->GetValueAsUnsigned(0, &success);
if (!success)
return false;

stream.Printf(" strong=%" PRIu64, count + 1);
}

if (count_sp)
stream.Printf(" strong=%" PRIu64, 1 + count_sp->GetValueAsUnsigned(0));
if (auto weak_count_sp =
ctrl_sp->GetChildMemberWithName("__shared_weak_owners_")) {
bool success;
uint64_t count = weak_count_sp->GetValueAsUnsigned(0, &success);
if (!success)
return false;

if (weakcount_sp)
stream.Printf(" weak=%" PRIu64, 1 + weakcount_sp->GetValueAsUnsigned(0));
stream.Printf(" weak=%" PRIu64, count + 1);
}

return true;
}
Expand All @@ -210,24 +209,7 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
if (!ptr_sp)
return false;

if (ptr_sp->GetValueAsUnsigned(0) == 0) {
stream.Printf("nullptr");
return true;
} else {
bool print_pointee = false;
Status error;
ValueObjectSP pointee_sp = ptr_sp->Dereference(error);
if (pointee_sp && error.Success()) {
if (pointee_sp->DumpPrintableRepresentation(
stream, ValueObject::eValueObjectRepresentationStyleSummary,
lldb::eFormatInvalid,
ValueObject::PrintableRepresentationSpecialCases::eDisable,
false))
print_pointee = true;
}
if (!print_pointee)
stream.Printf("ptr = 0x%" PRIx64, ptr_sp->GetValueAsUnsigned(0));
}
DumpCxxSmartPtrPointerSummary(stream, *ptr_sp, options);

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,8 @@ bool LibStdcppUniquePtrSyntheticFrontEnd::GetSummary(
if (!m_ptr_obj)
return false;

bool success;
uint64_t ptr_value = m_ptr_obj->GetValueAsUnsigned(0, &success);
if (!success)
return false;
if (ptr_value == 0)
stream.Printf("nullptr");
else
stream.Printf("0x%" PRIx64, ptr_value);
DumpCxxSmartPtrPointerSummary(stream, *m_ptr_obj, options);

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Test lldb data formatter for libc++ std::shared_ptr.
"""


import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
Expand Down Expand Up @@ -91,6 +90,14 @@ def test_shared_ptr_variables(self):
self.expect_var_path("sp_user->id", type="int", value="30")
self.expect_var_path("sp_user->name", type="std::string", summary='"steph"')

valobj = self.expect_var_path(
"si", type="std::shared_ptr<int>", summary="47 strong=2 weak=1"
)

valobj = self.expect_var_path(
"sie", type="std::shared_ptr<int>", summary="nullptr strong=2 weak=1"
)

self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("ptr_node->value", value="1")
self.expect_var_path("ptr_node->next->value", value="2")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,9 @@ int main() {
std::shared_ptr<NodeS>(new NodeS{nullptr, 2});
ptr_node = std::shared_ptr<NodeS>(new NodeS{std::move(ptr_node), 1});

// Construct empty shared_ptr with non-null control field.
std::shared_ptr<int> si(new int(47));
std::shared_ptr<int> sie(si, nullptr);

return 0; // break here
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Test lldb data formatter subsystem.
"""


import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
Expand Down Expand Up @@ -31,15 +30,16 @@ def test_with_run_command(self):
self.assertTrue(frame.IsValid())

self.expect("frame variable nup", substrs=["nup = nullptr"])
self.expect("frame variable iup", substrs=["iup = 0x"])
self.expect("frame variable sup", substrs=["sup = 0x"])
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 = 0x", "deleter = ", "a = 1", "b = 2"]
"frame variable idp", substrs=["idp = 456", "deleter = ", "a = 1", "b = 2"]
)
self.expect(
"frame variable sdp", substrs=["sdp = 0x", "deleter = ", "a = 3", "b = 4"]
"frame variable sdp",
substrs=['sdp = "baz"', "deleter = ", "a = 3", "b = 4"],
)

self.assertEqual(
Expand Down Expand Up @@ -106,13 +106,13 @@ def test_recursive_unique_ptr(self):
substrs=["stopped", "stop reason = breakpoint"],
)

self.expect("frame variable f1->fp", substrs=["fp = 0x"])
self.expect("frame variable f1->fp", substrs=["fp = Foo @ 0x"])
self.expect(
"frame variable --ptr-depth=1 f1->fp", substrs=["data = 2", "fp = 0x"]
"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 = 0x", "data = 1"],
substrs=["data = 2", "fp = Foo @ 0x", "data = 1"],
)

frame = self.frame()
Expand Down
Loading