Skip to content

Commit 6bdbcdc

Browse files
committed
Add backtrace command, and tests
1 parent ead37d6 commit 6bdbcdc

File tree

9 files changed

+159
-28
lines changed

9 files changed

+159
-28
lines changed

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -820,16 +820,6 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
820820
"could not get info for async task {0:x}: {1}", task_ptr,
821821
fmt_consume(std::move(err)));
822822
} else {
823-
824-
// Print a backtrace of the Task to stdout.
825-
ExecutionContext exe_ctx{m_backend.GetExecutionContextRef()};
826-
auto tt = std::make_shared<ThreadTask>(
827-
3000, task_info->resumeAsyncContext, exe_ctx);
828-
StreamString ss;
829-
tt->GetStatus(ss, 0, 100, 0, false, true);
830-
auto desc = ss.GetString();
831-
printf("%.*s\n", (int)desc.size(), desc.data());
832-
833823
m_task_info = *task_info;
834824
for (auto child :
835825
{m_is_child_task_sp, m_is_future_sp, m_is_group_child_task_sp,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ class TargetReflectionContext : public ReflectionContextInterface {
401401
result.hasIsRunning = task_info.HasIsRunning;
402402
result.isRunning = task_info.IsRunning;
403403
result.isEnqueued = task_info.IsEnqueued;
404+
result.id = task_info.Id;
404405
result.resumeAsyncContext = task_info.ResumeAsyncContext;
405406
return result;
406407
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ class ReflectionContextInterface {
164164
bool hasIsRunning = false;
165165
bool isRunning = false;
166166
bool isEnqueued = false;
167+
uint64_t id = 0;
167168
lldb::addr_t resumeAsyncContext = LLDB_INVALID_ADDRESS;
168169
};
169170
// The default limits are copied from swift-inspect.

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "SwiftMetadataCache.h"
1717

1818
#include "Plugins/ExpressionParser/Swift/SwiftPersistentExpressionState.h"
19+
#include "Plugins/LanguageRuntime/Swift/SwiftTask.h"
1920
#include "Plugins/Process/Utility/RegisterContext_x86.h"
2021
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
2122
#include "Plugins/TypeSystem/Swift/SwiftDemangle.h"
@@ -47,6 +48,7 @@
4748
#include "lldb/ValueObject/ValueObjectCast.h"
4849
#include "lldb/ValueObject/ValueObjectConstResult.h"
4950
#include "lldb/ValueObject/ValueObjectVariable.h"
51+
#include "llvm/Support/FormatAdapters.h"
5052

5153
#include "lldb/lldb-enumerations.h"
5254
#include "swift/AST/ASTMangler.h"
@@ -2083,6 +2085,84 @@ class CommandObjectSwift_RefCount : public CommandObjectRaw {
20832085
}
20842086
};
20852087

2088+
class CommandObjectLanguageSwiftTaskBacktrace final
2089+
: public CommandObjectParsed {
2090+
public:
2091+
CommandObjectLanguageSwiftTaskBacktrace(CommandInterpreter &interpreter)
2092+
: CommandObjectParsed(interpreter, "backtrace",
2093+
"Show the backtrace of Swift tasks. See `thread "
2094+
"backtrace` for customizing backtrace output.",
2095+
"language swift task backtrace <variable-name>") {
2096+
AddSimpleArgumentList(eArgTypeVarName);
2097+
}
2098+
2099+
private:
2100+
void DoExecute(Args &command, CommandReturnObject &result) override {
2101+
if (!m_exe_ctx.GetFramePtr()) {
2102+
result.AppendError("no active frame selected");
2103+
return;
2104+
}
2105+
2106+
if (command[0].ref().empty()) {
2107+
result.AppendError("no task variable");
2108+
return;
2109+
}
2110+
2111+
StackFrame &frame = m_exe_ctx.GetFrameRef();
2112+
uint32_t path_options =
2113+
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
2114+
VariableSP var_sp;
2115+
Status status;
2116+
ValueObjectSP valobj_sp = frame.GetValueForVariableExpressionPath(
2117+
command[0].c_str(), eDynamicDontRunTarget, path_options, var_sp,
2118+
status);
2119+
if (!valobj_sp)
2120+
return;
2121+
2122+
ValueObjectSP task_obj_sp = valobj_sp->GetChildMemberWithName("_task");
2123+
if (!task_obj_sp)
2124+
return;
2125+
uint64_t task_ptr = task_obj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
2126+
if (task_ptr == LLDB_INVALID_ADDRESS)
2127+
return;
2128+
auto *runtime = SwiftLanguageRuntime::Get(m_exe_ctx.GetProcessSP());
2129+
if (!runtime)
2130+
return;
2131+
ThreadSafeReflectionContext reflection_ctx =
2132+
runtime->GetReflectionContext();
2133+
llvm::Expected<ReflectionContextInterface::AsyncTaskInfo> task_info =
2134+
reflection_ctx->asyncTaskInfo(task_ptr);
2135+
if (auto err = task_info.takeError()) {
2136+
LLDB_LOG(GetLog(LLDBLog::DataFormatters | LLDBLog::Types),
2137+
"could not get info for async task {0:x}: {1}", task_ptr,
2138+
fmt_consume(std::move(err)));
2139+
return;
2140+
}
2141+
2142+
auto thread_task = ThreadTask::Create(
2143+
task_info->id, task_info->resumeAsyncContext, m_exe_ctx);
2144+
if (auto error = thread_task.takeError()) {
2145+
result.AppendError(toString(std::move(error)));
2146+
return;
2147+
}
2148+
thread_task.get()->GetStatus(result.GetOutputStream(), 0, UINT32_MAX, 0,
2149+
false, false);
2150+
result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
2151+
}
2152+
};
2153+
2154+
class CommandObjectLanguageSwiftTask final : public CommandObjectMultiword {
2155+
public:
2156+
CommandObjectLanguageSwiftTask(CommandInterpreter &interpreter)
2157+
: CommandObjectMultiword(
2158+
interpreter, "task", "Commands for inspecting Swift Tasks.",
2159+
"language swift task <subcommand> [<subcommand-options>]") {
2160+
LoadSubCommand("backtrace",
2161+
CommandObjectSP(new CommandObjectLanguageSwiftTaskBacktrace(
2162+
interpreter)));
2163+
}
2164+
};
2165+
20862166
class CommandObjectMultiwordSwift : public CommandObjectMultiword {
20872167
public:
20882168
CommandObjectMultiwordSwift(CommandInterpreter &interpreter)
@@ -2094,6 +2174,8 @@ class CommandObjectMultiwordSwift : public CommandObjectMultiword {
20942174
interpreter)));
20952175
LoadSubCommand("refcount", CommandObjectSP(new CommandObjectSwift_RefCount(
20962176
interpreter)));
2177+
LoadSubCommand("task", CommandObjectSP(new CommandObjectLanguageSwiftTask(
2178+
interpreter)));
20972179
}
20982180

20992181
virtual ~CommandObjectMultiwordSwift() {}

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

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
11
#include "SwiftTask.h"
22
#include "SwiftLanguageRuntime.h"
33
#include "lldb/Target/Process.h"
4+
#include "lldb/lldb-enumerations.h"
5+
#include "llvm/Support/Error.h"
46

57
using namespace llvm;
68
using namespace lldb;
79

810
namespace lldb_private {
911

10-
ThreadTask::ThreadTask(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx)
12+
ThreadTask::ThreadTask(tid_t tid, addr_t async_ctx, addr_t resume_fn,
13+
ExecutionContext &exe_ctx)
1114
: Thread(exe_ctx.GetProcessRef(), tid, true),
12-
m_reg_info_sp(exe_ctx.GetFrameSP()->GetRegisterContext()) {
13-
m_async_ctx = async_ctx;
14-
uint32_t ptr_size =
15-
exe_ctx.GetTargetRef().GetArchitecture().GetAddressByteSize();
15+
m_reg_info_sp(exe_ctx.GetFrameSP()->GetRegisterContext()),
16+
m_async_ctx(async_ctx), m_resume_fn(resume_fn) {}
17+
18+
RegisterContextSP lldb_private::ThreadTask::GetRegisterContext() {
19+
if (!m_async_reg_ctx_sp)
20+
m_async_reg_ctx_sp = std::make_shared<RegisterContextTask>(
21+
*this, m_reg_info_sp, m_resume_fn, m_async_ctx);
22+
return m_async_reg_ctx_sp;
23+
}
24+
25+
llvm::Expected<std::shared_ptr<ThreadTask>>
26+
ThreadTask::Create(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx) {
27+
auto &process = exe_ctx.GetProcessRef();
28+
auto &target = exe_ctx.GetTargetRef();
29+
1630
// A simplified description of AsyncContext. See swift/Task/ABI.h
1731
// struct AsyncContext {
1832
// AsyncContext *Parent; // offset 0
1933
// TaskContinuationFunction *ResumeParent; // offset 8
2034
// };
21-
uint32_t resume_offset = ptr_size; // offsetof(AsyncContext, ResumeParent)
22-
uint32_t resume_ptr = async_ctx + resume_offset;
35+
// The resume function is stored at `offsetof(AsyncContext, ResumeParent)`,
36+
// which is the async context's base address plus the size of a pointer.
37+
uint32_t ptr_size = target.GetArchitecture().GetAddressByteSize();
38+
addr_t resume_ptr = async_ctx + ptr_size;
2339
Status status;
24-
m_resume_fn =
25-
exe_ctx.GetProcessRef().ReadPointerFromMemory(resume_ptr, status);
26-
}
40+
addr_t resume_fn = process.ReadPointerFromMemory(resume_ptr, status);
41+
if (status.Fail() || resume_fn == LLDB_INVALID_ADDRESS)
42+
return createStringError("failed to read task's resume function");
2743

28-
RegisterContextSP lldb_private::ThreadTask::GetRegisterContext() {
29-
if (!m_async_reg_ctx_sp)
30-
m_async_reg_ctx_sp = std::make_shared<RegisterContextTask>(
31-
*this, m_reg_info_sp, m_resume_fn, m_async_ctx);
32-
return m_async_reg_ctx_sp;
44+
return std::make_shared<ThreadTask>(tid, async_ctx, resume_fn, exe_ctx);
3345
}
3446

3547
RegisterContextTask::RegisterContextTask(Thread &thread,

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@ using namespace lldb;
1515
/// Task's "async context" (instead of the stack).
1616
///
1717
/// See `Task<Success, Failure>` and `UnsafeCurrentTask`
18-
class ThreadTask : public Thread {
18+
class ThreadTask final : public Thread {
1919
public:
20-
ThreadTask(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx);
20+
ThreadTask(tid_t tid, addr_t async_ctx, addr_t resume_fn,
21+
ExecutionContext &exe_ctx);
22+
23+
static llvm::Expected<std::shared_ptr<ThreadTask>>
24+
Create(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx);
2125

2226
/// Returns a Task specific register context (RegisterContextTask).
2327
RegisterContextSP GetRegisterContext() override;
@@ -45,7 +49,7 @@ class ThreadTask : public Thread {
4549

4650
/// A Swift Task specific register context. Supporting class for `ThreadTask`,
4751
/// see its documentation for details.
48-
class RegisterContextTask : public RegisterContext {
52+
class RegisterContextTask final : public RegisterContext {
4953
public:
5054
RegisterContextTask(Thread &thread, RegisterContextSP reg_info_sp,
5155
addr_t resume_fn, addr_t async_ctx);
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: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import TestBase
4+
import lldbsuite.test.lldbutil as lldbutil
5+
6+
7+
class TestCase(TestBase):
8+
def test(self):
9+
self.build()
10+
lldbutil.run_to_source_breakpoint(
11+
self, "break here", lldb.SBFileSpec("main.swift")
12+
)
13+
self.expect(
14+
"language swift task backtrace task",
15+
substrs=[
16+
".sleep(",
17+
"`second() at main.swift:6",
18+
"`first() at main.swift:2",
19+
"`closure #1 in static Main.main() at main.swift:12",
20+
],
21+
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
func first() async {
2+
await second()
3+
}
4+
5+
func second() async {
6+
try? await Task.sleep(for: .seconds(10))
7+
}
8+
9+
@main struct Main {
10+
static func main() async {
11+
let task = Task {
12+
await first()
13+
}
14+
try? await Task.sleep(for: .seconds(0.01))
15+
print("break here")
16+
}
17+
}

0 commit comments

Comments
 (0)