Skip to content

[lldb] Add parent address to Task synthetic provider #10936

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
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
68 changes: 52 additions & 16 deletions lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,11 @@ namespace lldb_private {
namespace formatters {
namespace swift {

/// The size of Swift Tasks. Fragments are tail allocated.
static constexpr size_t AsyncTaskSize = sizeof(::swift::AsyncTask);
/// The offset of ChildFragment, which is the first fragment of an AsyncTask.
static constexpr offset_t ChildFragmentOffset = AsyncTaskSize;

class EnumSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
EnumSyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
Expand Down Expand Up @@ -806,6 +811,7 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
"address",
"id",
"enqueuePriority",
"parent",
"children",

// Children below this point are hidden.
Expand All @@ -823,8 +829,9 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
};

llvm::Expected<uint32_t> CalculateNumChildren() override {
// Show only the first four children address/id/enqueuePriority/children.
return 4;
// Show only the first five children
// address/id/enqueuePriority/parent/children.
return 5;
}

lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {
Expand Down Expand Up @@ -873,6 +880,36 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
RETURN_CHILD(m_enqueue_priority_sp, enqueuePriority, priority_type);
}
case 3: {
if (!m_parent_task_sp) {
auto process_sp = m_backend.GetProcessSP();
if (!process_sp)
return {};

// TypeMangling for "Swift.Optional<Swift.UnsafeRawPointer>"
CompilerType raw_pointer_type =
m_ts->GetTypeFromMangledTypename(ConstString("$sSVSgD"));

addr_t parent_addr = 0;
if (m_task_info.isChildTask) {
// Read ChildFragment::Parent, the first field of the ChildFragment.
Status status;
parent_addr = process_sp->ReadPointerFromMemory(
m_task_ptr + ChildFragmentOffset, status);
if (status.Fail() || parent_addr == LLDB_INVALID_ADDRESS)
parent_addr = 0;
}

addr_t value = parent_addr;
DataExtractor data{reinterpret_cast<const void *>(&value),
sizeof(value), endian::InlHostByteOrder(),
sizeof(void *)};
m_parent_task_sp = ValueObject::CreateValueObjectFromData(
"parent", data, m_backend.GetExecutionContextRef(),
raw_pointer_type);
}
return m_parent_task_sp;
}
case 4: {
if (!m_child_tasks_sp) {
using task_type = decltype(m_task_info.childTasks)::value_type;
std::vector<task_type> tasks = m_task_info.childTasks;
Expand Down Expand Up @@ -901,26 +938,26 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
}
return m_child_tasks_sp;
}
case 4:
RETURN_CHILD(m_is_child_task_sp, isChildTask, bool_type);
case 5:
RETURN_CHILD(m_is_future_sp, isFuture, bool_type);
RETURN_CHILD(m_is_child_task_sp, isChildTask, bool_type);
case 6:
RETURN_CHILD(m_is_group_child_task_sp, isGroupChildTask, bool_type);
RETURN_CHILD(m_is_future_sp, isFuture, bool_type);
case 7:
RETURN_CHILD(m_is_async_let_task_sp, isAsyncLetTask, bool_type);
RETURN_CHILD(m_is_group_child_task_sp, isGroupChildTask, bool_type);
case 8:
RETURN_CHILD(m_is_cancelled_sp, isCancelled, bool_type);
RETURN_CHILD(m_is_async_let_task_sp, isAsyncLetTask, bool_type);
case 9:
RETURN_CHILD(m_is_cancelled_sp, isCancelled, bool_type);
case 10:
RETURN_CHILD(m_is_status_record_locked_sp, isStatusRecordLocked,
bool_type);
case 10:
RETURN_CHILD(m_is_escalated_sp, isEscalated, bool_type);
case 11:
RETURN_CHILD(m_is_enqueued_sp, isEnqueued, bool_type);
RETURN_CHILD(m_is_escalated_sp, isEscalated, bool_type);
case 12:
RETURN_CHILD(m_is_enqueued_sp, isEnqueued, bool_type);
case 13:
RETURN_CHILD(m_is_complete_sp, isComplete, bool_type);
case 13: {
case 14: {
if (m_task_info.hasIsRunning)
RETURN_CHILD(m_is_running_sp, isRunning, bool_type);
return {};
Expand Down Expand Up @@ -953,8 +990,8 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
m_is_child_task_sp, m_is_future_sp, m_is_group_child_task_sp,
m_is_async_let_task_sp, m_is_cancelled_sp,
m_is_status_record_locked_sp, m_is_escalated_sp,
m_is_enqueued_sp, m_is_complete_sp, m_child_tasks_sp,
m_is_running_sp})
m_is_enqueued_sp, m_is_complete_sp, m_parent_task_sp,
m_child_tasks_sp, m_is_running_sp})
child.reset();
}
}
Expand Down Expand Up @@ -990,6 +1027,7 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
ValueObjectSP m_is_escalated_sp;
ValueObjectSP m_is_enqueued_sp;
ValueObjectSP m_is_complete_sp;
ValueObjectSP m_parent_task_sp;
ValueObjectSP m_child_tasks_sp;
ValueObjectSP m_is_running_sp;
};
Expand Down Expand Up @@ -1280,8 +1318,6 @@ class TaskGroupSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
bool operator==(const Task &other) const { return addr == other.addr; }
bool operator!=(const Task &other) const { return !(*this == other); }

static constexpr offset_t AsyncTaskSize = sizeof(::swift::AsyncTask);
static constexpr offset_t ChildFragmentOffset = AsyncTaskSize;
static constexpr offset_t NextChildOffset = ChildFragmentOffset + 0x8;

Task getNextChild(Status &status) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2857,6 +2857,13 @@ TypeSystemSwiftTypeRef::RemangleAsType(swift::Demangle::Demangler &dem,
if (!node)
return {};

// Guard against an empty opaque type. This can happen when demangling an
// OpaqueTypeRef (ex `$sBpD`). An empty opaque will assert when mangled.
if (auto *opaque_type =
swift_demangle::ChildAtPath(node, {Node::Kind::OpaqueType}))
if (!opaque_type->hasChildren())
return {};

using namespace swift::Demangle;
if (node->getKind() != Node::Kind::Global) {
auto global = dem.createNode(Node::Kind::Global);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def test_unsafe_continuation_printing(self):
address = 0x[0-9a-f]+
id = \1
enqueuePriority = 0
parent = nil
children = \{\}
\}
\}
Expand All @@ -49,6 +50,7 @@ def test_checked_continuation_printing(self):
address = 0x[0-9a-f]+
id = \1
enqueuePriority = 0
parent = nil
children = \{\}
\}
\}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def test_top_level_task(self):
address = 0x[0-9a-f]+
id = \1
enqueuePriority = \.medium
parent = nil
children = \{\}
}
"""
Expand All @@ -49,6 +50,7 @@ def test_current_task(self):
address = 0x[0-9a-f]+
id = \1
enqueuePriority = \.medium
parent = 0x[0-9a-f]+ \{\}
children = \{\}
\}
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ def test(self):
textwrap.dedent(
r"""
\(UnsafeCurrentTask\) current_task = id:1 flags:(?:running|enqueued) \{
address = 0x[0-9a-f]+
address = (0x[0-9a-f]+)
id = 1
enqueuePriority = 0
parent = nil
children = \{
0 = id:2 flags:(?:running\|)?(?:enqueued\|)?asyncLetTask \{
address = 0x[0-9a-f]+
id = 2
enqueuePriority = \.medium
parent = \1 \{\}
children = \{\}
\}
\}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SWIFT_SOURCES := main.swift
SWIFTFLAGS_EXTRAS := -parse-as-library
include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import re
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


ADDR_PATTERN = "(0x[0-9a-f]{6,})"

class TestCase(TestBase):

@skipUnlessDarwin
@swiftTest
def test(self):
self.build()
_, process, _, _ = lldbutil.run_to_name_breakpoint(self, "breakHere")

# First breakpoint hit occurrs in a root task, with no parent.
self.expect("task info", substrs=["parent = nil"])
root_task = self._extract("task info", f"address = {ADDR_PATTERN}")

# Continue to the next hit of the same breakpoint, which is called from
# an async let child task.
process.Continue()
parent_of_child_task = self._extract("task info", f"parent = {ADDR_PATTERN}")

# Ensure the parent of the child is the same as the root task.
self.assertEqual(root_task, parent_of_child_task)

def _extract(self, command: str, pattern: str) -> str:
ret = lldb.SBCommandReturnObject()
self.ci.HandleCommand(command, ret)
match = re.search(pattern, ret.GetOutput(), flags=re.I)
self.assertTrue(match)
return match.group(1) if match else ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
func breakHere() {}

@main struct Main {
static func main() async {
await breakHere()
async let x = breakHere()
await x
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,21 @@ def do_test_print(self):
address = 0x[0-9a-f]+
id = \1
enqueuePriority = \.medium
parent = (.+)
children = \{\}
\}
\[1\] = id:([1-9]\d*) flags:(?:running\|)?(?:enqueued\|)?groupChildTask \{
address = 0x[0-9a-f]+
id = \2
id = \3
enqueuePriority = \.medium
parent = \2
children = \{\}
\}
\[2\] = id:([1-9]\d*) flags:(?:running\|)?(?:enqueued\|)?groupChildTask \{
address = 0x[0-9a-f]+
id = \3
id = \4
enqueuePriority = \.medium
parent = \2
children = \{\}
\}
\}
Expand Down