Skip to content

Commit 01c80c9

Browse files
committed
[lldb] Introduce backtracing of Swift Tasks
1 parent 38c9bd4 commit 01c80c9

File tree

6 files changed

+162
-0
lines changed

6 files changed

+162
-0
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "Plugins/Language/Swift/SwiftStringIndex.h"
1515
#include "Plugins/LanguageRuntime/Swift/ReflectionContextInterface.h"
1616
#include "Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.h"
17+
#include "Plugins/LanguageRuntime/Swift/SwiftTask.h"
1718
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
1819
#include "lldb/DataFormatters/FormattersHelpers.h"
1920
#include "lldb/DataFormatters/StringPrinter.h"
@@ -23,6 +24,7 @@
2324
#include "lldb/Utility/LLDBLog.h"
2425
#include "lldb/Utility/Log.h"
2526
#include "lldb/Utility/Status.h"
27+
#include "lldb/Utility/StreamString.h"
2628
#include "lldb/Utility/Timer.h"
2729
#include "lldb/ValueObject/ValueObject.h"
2830
#include "lldb/lldb-enumerations.h"
@@ -818,6 +820,16 @@ class TaskSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
818820
"could not get info for async task {0:x}: {1}", task_ptr,
819821
fmt_consume(std::move(err)));
820822
} 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+
821833
m_task_info = *task_info;
822834
for (auto child :
823835
{m_is_child_task_sp, m_is_future_sp, m_is_group_child_task_sp,

lldb/source/Plugins/LanguageRuntime/Swift/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_lldb_library(lldbPluginSwiftLanguageRuntime PLUGIN
66
SwiftLanguageRuntimeNames.cpp
77
SwiftLanguageRuntimeRemoteAST.cpp
88
SwiftMetadataCache.cpp
9+
SwiftTask.cpp
910

1011
LINK_LIBS
1112
swiftAST

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.resumeAsyncContext = task_info.ResumeAsyncContext;
404405
return result;
405406
}
406407

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
#include <mutex>
1717

18+
#include "lldb/lldb-defines.h"
1819
#include "lldb/lldb-types.h"
1920
#include "swift/ABI/ObjectFile.h"
2021
#include "swift/Remote/RemoteAddress.h"
@@ -163,6 +164,7 @@ class ReflectionContextInterface {
163164
bool hasIsRunning = false;
164165
bool isRunning = false;
165166
bool isEnqueued = false;
167+
lldb::addr_t resumeAsyncContext = LLDB_INVALID_ADDRESS;
166168
};
167169
// The default limits are copied from swift-inspect.
168170
virtual llvm::Expected<AsyncTaskInfo>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include "SwiftTask.h"
2+
#include "SwiftLanguageRuntime.h"
3+
#include "lldb/Target/Process.h"
4+
5+
using namespace llvm;
6+
using namespace lldb;
7+
8+
lldb_private::ThreadTask::ThreadTask(tid_t tid, addr_t async_ctx,
9+
ExecutionContext &exe_ctx)
10+
: Thread(exe_ctx.GetProcessRef(), tid, true),
11+
m_concrete_reg_ctx_sp(exe_ctx.GetFrameSP()->GetRegisterContext()) {
12+
m_async_ctx = async_ctx;
13+
auto ptr_size = exe_ctx.GetTargetRef().GetArchitecture().GetAddressByteSize();
14+
// A simplified description of AsyncContext. See swift/Task/ABI.h
15+
// struct AsyncContext {
16+
// AsyncContext *Parent; // offset 0
17+
// TaskContinuationFunction *ResumeParent; // offset 8
18+
// };
19+
auto resume_offset = ptr_size; // offsetof(AsyncContext, ResumeParent)
20+
auto resume_ptr = async_ctx + resume_offset;
21+
Status status;
22+
m_pc = exe_ctx.GetProcessRef().ReadPointerFromMemory(resume_ptr, status);
23+
}
24+
25+
RegisterContextSP lldb_private::ThreadTask::GetRegisterContext() {
26+
if (!m_async_reg_ctx_sp)
27+
m_async_reg_ctx_sp = std::make_shared<RegisterContextTask>(
28+
*this, m_concrete_reg_ctx_sp, m_pc, m_async_ctx);
29+
return m_async_reg_ctx_sp;
30+
}
31+
32+
lldb_private::RegisterContextTask::RegisterContextTask(
33+
Thread &thread, RegisterContextSP reg_info_sp, addr_t pc, addr_t async_ctx)
34+
: RegisterContext(thread, 0), m_reg_info_sp(reg_info_sp),
35+
m_async_ctx(async_ctx), m_pc(pc) {
36+
auto &target = thread.GetProcess()->GetTarget();
37+
auto triple = target.GetArchitecture().GetTriple();
38+
if (auto regnums = GetAsyncUnwindRegisterNumbers(triple.getArch()))
39+
m_async_ctx_regnum = regnums->async_ctx_regnum;
40+
}
41+
42+
bool lldb_private::RegisterContextTask::ReadRegister(
43+
const RegisterInfo *reg_info, RegisterValue &reg_value) {
44+
if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC) {
45+
reg_value = m_pc;
46+
return true;
47+
}
48+
if (reg_info->kinds[eRegisterKindLLDB] == m_async_ctx_regnum) {
49+
reg_value = m_async_ctx;
50+
return true;
51+
}
52+
return false;
53+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
2+
#include "lldb/Target/RegisterContext.h"
3+
#include "lldb/Target/Thread.h"
4+
#include "lldb/Utility/RegisterValue.h"
5+
#include "lldb/lldb-forward.h"
6+
7+
namespace lldb_private {
8+
9+
using namespace lldb;
10+
11+
/// Provides a subset of Thread operations for Swift Tasks.
12+
///
13+
/// Currently, this supports backtraces of Tasks, and selecting frames in the
14+
/// backtrace. Async frames make available the variables that are stored in the
15+
/// Task's "async context" (instead of the stack).
16+
///
17+
/// See `Task<Success, Failure>` and `UnsafeCurrentTask`
18+
class ThreadTask : public Thread {
19+
public:
20+
ThreadTask(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx);
21+
22+
/// Returns a Task specific register context (RegisterContextTask).
23+
RegisterContextSP GetRegisterContext() override;
24+
25+
~ThreadTask() override { DestroyThread(); }
26+
27+
// No-op overrides.
28+
void RefreshStateAfterStop() override {}
29+
lldb::RegisterContextSP
30+
CreateRegisterContextForFrame(StackFrame *frame) override {
31+
return {};
32+
}
33+
bool CalculateStopInfo() override { return false; }
34+
35+
private:
36+
/// A register context that is the source of `RegisterInfo` data.
37+
RegisterContextSP m_concrete_reg_ctx_sp;
38+
/// Lazily initialized `RegisterContextTask`.
39+
RegisterContextSP m_async_reg_ctx_sp;
40+
/// The Task's async context.
41+
addr_t m_async_ctx = LLDB_INVALID_ADDRESS;
42+
/// The address of the async context's resume function.
43+
addr_t m_pc = LLDB_INVALID_ADDRESS;
44+
};
45+
46+
/// A Swift Task specific register context. Supporting class for `ThreadTask`,
47+
/// see its documentation for details.
48+
class RegisterContextTask : public RegisterContext {
49+
public:
50+
RegisterContextTask(Thread &thread, RegisterContextSP reg_info_sp, addr_t pc,
51+
addr_t async_ctx);
52+
53+
/// RegisterContextTask supports readonly from only two (necessary)
54+
/// registers. Namely, the pc and the async context registers.
55+
bool ReadRegister(const RegisterInfo *reg_info,
56+
RegisterValue &reg_value) override;
57+
58+
// Pass through overrides.
59+
size_t GetRegisterCount() override {
60+
return m_reg_info_sp->GetRegisterCount();
61+
}
62+
const RegisterInfo *GetRegisterInfoAtIndex(size_t idx) override {
63+
return m_reg_info_sp->GetRegisterInfoAtIndex(idx);
64+
}
65+
size_t GetRegisterSetCount() override {
66+
return m_reg_info_sp->GetRegisterSetCount();
67+
}
68+
const RegisterSet *GetRegisterSet(size_t reg_set) override {
69+
return m_reg_info_sp->GetRegisterSet(reg_set);
70+
}
71+
lldb::ByteOrder GetByteOrder() override {
72+
return m_reg_info_sp->GetByteOrder();
73+
}
74+
75+
// No-op overrides.
76+
void InvalidateAllRegisters() override {}
77+
bool WriteRegister(const RegisterInfo *reg_info,
78+
const RegisterValue &reg_value) override {
79+
return false;
80+
}
81+
82+
private:
83+
/// A register context that is the source of `RegisterInfo` data.
84+
RegisterContextSP m_reg_info_sp;
85+
/// The architecture specific regnum (LLDB) which holds the async context.
86+
uint32_t m_async_ctx_regnum = LLDB_INVALID_REGNUM;
87+
/// The Task's async context.
88+
RegisterValue m_async_ctx;
89+
/// The address of the async context's resume function.
90+
RegisterValue m_pc;
91+
};
92+
93+
} // namespace lldb_private

0 commit comments

Comments
 (0)