Skip to content

Commit 0e7ec03

Browse files
authored
[lldb][formatter] Add children of Swift Tasks (#10057)
When displaying a Swift `Task`, include its child tasks. This adds a `children` child to the `Task` synthetic provider. The type of `children` is a dynamically constructed tuple of N `UnsafeCurrentTask`, where N is the number of child tasks. This allows users to see structured concurrency child tasks, in particular `async let` child tasks, and `TaskGroup` child tasks.
1 parent 9643fc4 commit 0e7ec03

File tree

8 files changed

+143
-9
lines changed

8 files changed

+143
-9
lines changed

lldb/source/Plugins/Language/Swift/SwiftFormatters.cpp

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h"
1616
#include "Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h"
1717
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
18+
#include "Plugins/TypeSystem/Swift/SwiftDemangle.h"
1819
#include "lldb/DataFormatters/FormattersHelpers.h"
1920
#include "lldb/DataFormatters/StringPrinter.h"
2021
#include "lldb/Symbol/CompilerType.h"
@@ -28,6 +29,7 @@
2829
#include "lldb/ValueObject/ValueObject.h"
2930
#include "lldb/lldb-enumerations.h"
3031
#include "swift/AST/Types.h"
32+
#include "swift/Demangling/Demangle.h"
3133
#include "swift/Demangling/ManglingMacros.h"
3234
#include "llvm/ADT/STLExtras.h"
3335
#include "llvm/ADT/StringRef.h"
@@ -761,10 +763,10 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
761763
auto ts_or_err =
762764
target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeSwift);
763765
if (auto err = ts_or_err.takeError()) {
764-
LLDB_LOG(
765-
GetLog(LLDBLog::DataFormatters | LLDBLog::Types),
766-
"could not get Swift type system for Task synthetic provider: {0}",
767-
fmt_consume(std::move(err)));
766+
LLDB_LOG_ERROR(GetLog(LLDBLog::DataFormatters | LLDBLog::Types),
767+
std::move(err),
768+
"could not get Swift type system for Task synthetic "
769+
"provider: {0}");
768770
return;
769771
}
770772
m_ts = llvm::dyn_cast_or_null<TypeSystemSwiftTypeRef>(ts_or_err->get());
@@ -782,6 +784,7 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
782784
"isStatusRecordLocked",
783785
"isEscalated",
784786
"isEnqueued",
787+
"children",
785788
"isRunning",
786789
};
787790

@@ -837,7 +840,22 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
837840
RETURN_CHILD(m_is_escalated_sp, isEscalated, bool_type);
838841
case 10:
839842
RETURN_CHILD(m_is_enqueued_sp, isEnqueued, bool_type);
840-
case 11:
843+
case 11: {
844+
if (!m_child_tasks_sp) {
845+
const auto &tasks = m_task_info.childTasks;
846+
std::string mangled_typename =
847+
mangledTypenameForTasksTuple(tasks.size());
848+
CompilerType tasks_tuple_type =
849+
m_ts->GetTypeFromMangledTypename(ConstString(mangled_typename));
850+
DataExtractor data{tasks.data(), tasks.size() * sizeof(tasks[0]),
851+
endian::InlHostByteOrder(), sizeof(void *)};
852+
m_child_tasks_sp = ValueObject::CreateValueObjectFromData(
853+
"children", data, m_backend.GetExecutionContextRef(),
854+
tasks_tuple_type);
855+
}
856+
return m_child_tasks_sp;
857+
}
858+
case 12:
841859
RETURN_CHILD(m_is_running_sp, isRunning, bool_type);
842860
default:
843861
return {};
@@ -858,17 +876,17 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
858876
llvm::Expected<ReflectionContextInterface::AsyncTaskInfo> task_info =
859877
reflection_ctx->asyncTaskInfo(task_ptr);
860878
if (auto err = task_info.takeError()) {
861-
LLDB_LOG(GetLog(LLDBLog::DataFormatters | LLDBLog::Types),
862-
"could not get info for async task {0:x}: {1}", task_ptr,
863-
fmt_consume(std::move(err)));
879+
LLDB_LOG_ERROR(
880+
GetLog(LLDBLog::DataFormatters | LLDBLog::Types), std::move(err),
881+
"could not get info for async task {0:x}: {1}", task_ptr);
864882
} else {
865883
m_task_info = *task_info;
866884
for (auto child :
867885
{m_id_sp, m_kind_sp, m_enqueue_priority_sp, m_is_child_task_sp,
868886
m_is_future_sp, m_is_group_child_task_sp,
869887
m_is_async_let_task_sp, m_is_cancelled_sp,
870888
m_is_status_record_locked_sp, m_is_escalated_sp,
871-
m_is_enqueued_sp, m_is_running_sp})
889+
m_is_enqueued_sp, m_child_tasks_sp, m_is_running_sp})
872890
child.reset();
873891
}
874892
}
@@ -886,6 +904,35 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
886904
return std::distance(children.begin(), it);
887905
}
888906

907+
private:
908+
std::string mangledTypenameForTasksTuple(size_t count) {
909+
/*
910+
Global > TypeMangling > Type > Tuple
911+
TupleElement > Type > Structure
912+
Module, text="Swift"
913+
Identifier, text="UnsafeCurrentTask"
914+
*/
915+
using namespace ::swift::Demangle;
916+
using Kind = Node::Kind;
917+
NodeFactory factory;
918+
auto [root, tuple] = swift_demangle::MakeNodeChain(
919+
{Kind::TypeMangling, Kind::Type, Kind::Tuple}, factory);
920+
921+
// Make a TupleElement subtree N times, where N is the number of subtasks.
922+
for (size_t i = 0; i < count; ++i) {
923+
auto *structure = swift_demangle::MakeNodeChain(
924+
tuple, {Kind::TupleElement, Kind::Type, Kind::Structure}, factory);
925+
if (structure) {
926+
structure->addChild(
927+
factory.createNode(Kind::Module, ::swift::STDLIB_NAME), factory);
928+
structure->addChild(
929+
factory.createNode(Kind::Identifier, "UnsafeCurrentTask"), factory);
930+
}
931+
}
932+
933+
return mangleNode(root).result();
934+
}
935+
889936
private:
890937
TypeSystemSwiftTypeRef *m_ts = nullptr;
891938
ReflectionContextInterface::AsyncTaskInfo m_task_info;
@@ -900,6 +947,7 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
900947
ValueObjectSP m_is_status_record_locked_sp;
901948
ValueObjectSP m_is_escalated_sp;
902949
ValueObjectSP m_is_enqueued_sp;
950+
ValueObjectSP m_child_tasks_sp;
903951
ValueObjectSP m_is_running_sp;
904952
};
905953
}

lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContext.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ class TargetReflectionContext : public ReflectionContextInterface {
407407
result.kind = task_info.Kind;
408408
result.enqueuePriority = task_info.EnqueuePriority;
409409
result.resumeAsyncContext = task_info.ResumeAsyncContext;
410+
for (auto child : task_info.ChildTasks)
411+
result.childTasks.push_back(child);
410412
return result;
411413
}
412414

lldb/source/Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ class ReflectionContextInterface {
170170
uint32_t kind = 0;
171171
uint32_t enqueuePriority = 0;
172172
lldb::addr_t resumeAsyncContext = LLDB_INVALID_ADDRESS;
173+
std::vector<lldb::addr_t> childTasks;
173174
};
174175
// The default limits are copied from swift-inspect.
175176
virtual llvm::Expected<AsyncTaskInfo>

lldb/source/Plugins/TypeSystem/Swift/SwiftDemangle.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/Demangling/Demangler.h"
1818
#include "swift/Demangling/ManglingFlavor.h"
1919
#include "llvm/ADT/ArrayRef.h"
20+
#include <initializer_list>
2021

2122
namespace lldb_private {
2223
namespace swift_demangle {
@@ -116,6 +117,41 @@ GetDemangledTypeMangling(swift::Demangle::Demangler &dem,
116117
return GetTypeMangling(dem.demangleSymbol(name));
117118
}
118119

120+
/// Create a parent-child chain of demangle nodes for the specified node kinds.
121+
///
122+
/// \param initial[in] The starting node to construct the chain from.
123+
/// \param kinds[in] The node kind for each created node.
124+
/// \param factory[in] The factory to create nodes.
125+
/// \return The tail leaf node created, whose kind is the last kind in \c kinds.
126+
inline NodePointer
127+
MakeNodeChain(NodePointer initial,
128+
std::initializer_list<swift::Demangle::Node::Kind> kinds,
129+
NodeFactory &factory) {
130+
auto *current = initial;
131+
for (auto kind : kinds) {
132+
if (!current)
133+
return nullptr;
134+
auto *child = factory.createNode(kind);
135+
current->addChild(child, factory);
136+
current = child;
137+
}
138+
return current;
139+
}
140+
141+
/// Create a parent-child chain of demangle nodes for the specified node kinds.
142+
///
143+
/// \param initial[in] The starting node to construct the chain from.
144+
/// \param kinds[in] The node kind for each created node.
145+
/// \param factory[in] The factory to create nodes.
146+
/// \return A pair of nodes: the head and tail of the constructed nodes.
147+
inline std::pair<NodePointer, NodePointer>
148+
MakeNodeChain(std::initializer_list<swift::Demangle::Node::Kind> kinds,
149+
NodeFactory &factory) {
150+
auto *root = factory.createNode(Node::Kind::Global);
151+
auto *leaf = MakeNodeChain(root, kinds, factory);
152+
return {root, leaf};
153+
}
154+
119155
/// Wrap node in Global/TypeMangling/Type.
120156
inline swift::Demangle::NodePointer
121157
MangleType(swift::Demangle::Demangler &dem,

lldb/test/API/lang/swift/async/formatters/task/TestSwiftTaskSyntheticProvider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def test_top_level_task(self):
2828
"isAsyncLetTask = false",
2929
"isCancelled = false",
3030
"isEnqueued = ",
31+
"children = {}",
3132
],
3233
)
3334

@@ -49,5 +50,6 @@ def test_current_task(self):
4950
"isAsyncLetTask = true",
5051
"isCancelled = false",
5152
"isEnqueued = false",
53+
"children = {}",
5254
],
5355
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SWIFT_SOURCES := main.swift
2+
SWIFTFLAGS_EXTRAS := -parse-as-library
3+
include Makefile.rules
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
6+
7+
class TestCase(TestBase):
8+
9+
@skipUnlessDarwin
10+
@swiftTest
11+
def test(self):
12+
""""""
13+
self.build()
14+
lldbutil.run_to_source_breakpoint(
15+
self, "break here", lldb.SBFileSpec("main.swift")
16+
)
17+
self.expect(
18+
"language swift task info",
19+
substrs=[
20+
"(UnsafeCurrentTask) current_task = {",
21+
"id = 1",
22+
"isChildTask = false",
23+
"isAsyncLetTask = false",
24+
"children = {",
25+
"0 = {",
26+
"id = 2",
27+
"isChildTask = true",
28+
"isAsyncLetTask = true",
29+
"children = {}",
30+
],
31+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
func f() async -> Int {
2+
try? await Task.sleep(for: .seconds(300))
3+
return 30
4+
}
5+
6+
@main struct Main {
7+
static func main() async {
8+
async let number = f()
9+
await print("break here \(number)")
10+
}
11+
}

0 commit comments

Comments
 (0)