diff --git a/lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp b/lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp index f8231c8b9ee76..2eaf88d0c7738 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp +++ b/lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp @@ -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); @@ -806,6 +811,7 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { "address", "id", "enqueuePriority", + "parent", "children", // Children below this point are hidden. @@ -823,8 +829,9 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd { }; llvm::Expected 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 { @@ -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" + 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(&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 tasks = m_task_info.childTasks; @@ -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 {}; @@ -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(); } } @@ -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; }; @@ -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) { diff --git a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp index 42a707d71102a..91f146fd4a378 100644 --- a/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp +++ b/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp @@ -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); diff --git a/lldb/test/API/lang/swift/async/continuations/TestSwiftContinuationSynthetic.py b/lldb/test/API/lang/swift/async/continuations/TestSwiftContinuationSynthetic.py index 1adba03fd102d..c4247ac4de5a9 100644 --- a/lldb/test/API/lang/swift/async/continuations/TestSwiftContinuationSynthetic.py +++ b/lldb/test/API/lang/swift/async/continuations/TestSwiftContinuationSynthetic.py @@ -24,6 +24,7 @@ def test_unsafe_continuation_printing(self): address = 0x[0-9a-f]+ id = \1 enqueuePriority = 0 + parent = nil children = \{\} \} \} @@ -49,6 +50,7 @@ def test_checked_continuation_printing(self): address = 0x[0-9a-f]+ id = \1 enqueuePriority = 0 + parent = nil children = \{\} \} \} diff --git a/lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py b/lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py index cea29347686c2..9d50af2b473ad 100644 --- a/lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py +++ b/lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py @@ -25,6 +25,7 @@ def test_top_level_task(self): address = 0x[0-9a-f]+ id = \1 enqueuePriority = \.medium + parent = nil children = \{\} } """ @@ -49,6 +50,7 @@ def test_current_task(self): address = 0x[0-9a-f]+ id = \1 enqueuePriority = \.medium + parent = 0x[0-9a-f]+ \{\} children = \{\} \} """ diff --git a/lldb/test/API/lang/swift/async/formatters/task/children/TestSwiftSyntheticTaskChildren.py b/lldb/test/API/lang/swift/async/formatters/task/children/TestSwiftSyntheticTaskChildren.py index a8660c15692c0..f71f33c3e2705 100644 --- a/lldb/test/API/lang/swift/async/formatters/task/children/TestSwiftSyntheticTaskChildren.py +++ b/lldb/test/API/lang/swift/async/formatters/task/children/TestSwiftSyntheticTaskChildren.py @@ -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 = \{\} \} \} diff --git a/lldb/test/API/lang/swift/async/formatters/task/parent/Makefile b/lldb/test/API/lang/swift/async/formatters/task/parent/Makefile new file mode 100644 index 0000000000000..cca30b939e652 --- /dev/null +++ b/lldb/test/API/lang/swift/async/formatters/task/parent/Makefile @@ -0,0 +1,3 @@ +SWIFT_SOURCES := main.swift +SWIFTFLAGS_EXTRAS := -parse-as-library +include Makefile.rules diff --git a/lldb/test/API/lang/swift/async/formatters/task/parent/TestSwiftSyntheticTaskParent.py b/lldb/test/API/lang/swift/async/formatters/task/parent/TestSwiftSyntheticTaskParent.py new file mode 100644 index 0000000000000..fafa8ffc5cf07 --- /dev/null +++ b/lldb/test/API/lang/swift/async/formatters/task/parent/TestSwiftSyntheticTaskParent.py @@ -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 "" diff --git a/lldb/test/API/lang/swift/async/formatters/task/parent/main.swift b/lldb/test/API/lang/swift/async/formatters/task/parent/main.swift new file mode 100644 index 0000000000000..caf0f7af367a0 --- /dev/null +++ b/lldb/test/API/lang/swift/async/formatters/task/parent/main.swift @@ -0,0 +1,9 @@ +func breakHere() {} + +@main struct Main { + static func main() async { + await breakHere() + async let x = breakHere() + await x + } +} diff --git a/lldb/test/API/lang/swift/async/taskgroups/TestSwiftTaskGroupSynthetic.py b/lldb/test/API/lang/swift/async/taskgroups/TestSwiftTaskGroupSynthetic.py index 053aed29c5959..605c7ef6f1b78 100644 --- a/lldb/test/API/lang/swift/async/taskgroups/TestSwiftTaskGroupSynthetic.py +++ b/lldb/test/API/lang/swift/async/taskgroups/TestSwiftTaskGroupSynthetic.py @@ -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 = \{\} \} \}