forked from llvm/llvm-project
-
Notifications
You must be signed in to change notification settings - Fork 333
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9787 from swiftlang/dl/lldb-Introduce-backtracing…
…-of-Swift-Tasks [lldb] Introduce backtracing of Swift Tasks
- Loading branch information
Showing
10 changed files
with
306 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#include "SwiftTask.h" | ||
#include "SwiftLanguageRuntime.h" | ||
#include "lldb/Target/Process.h" | ||
#include "lldb/lldb-enumerations.h" | ||
#include "llvm/Support/Error.h" | ||
|
||
using namespace llvm; | ||
using namespace lldb; | ||
|
||
namespace lldb_private { | ||
|
||
ThreadTask::ThreadTask(tid_t tid, addr_t async_ctx, addr_t resume_fn, | ||
ExecutionContext &exe_ctx) | ||
: Thread(exe_ctx.GetProcessRef(), tid, true), | ||
m_reg_info_sp(exe_ctx.GetFrameSP()->GetRegisterContext()), | ||
m_async_ctx(async_ctx), m_resume_fn(resume_fn) {} | ||
|
||
RegisterContextSP lldb_private::ThreadTask::GetRegisterContext() { | ||
if (!m_async_reg_ctx_sp) | ||
m_async_reg_ctx_sp = std::make_shared<RegisterContextTask>( | ||
*this, m_reg_info_sp, m_resume_fn, m_async_ctx); | ||
return m_async_reg_ctx_sp; | ||
} | ||
|
||
llvm::Expected<std::shared_ptr<ThreadTask>> | ||
ThreadTask::Create(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx) { | ||
auto &process = exe_ctx.GetProcessRef(); | ||
auto &target = exe_ctx.GetTargetRef(); | ||
|
||
// A simplified description of AsyncContext. See swift/Task/ABI.h | ||
// struct AsyncContext { | ||
// AsyncContext *Parent; // offset 0 | ||
// TaskContinuationFunction *ResumeParent; // offset 8 | ||
// }; | ||
// The resume function is stored at `offsetof(AsyncContext, ResumeParent)`, | ||
// which is the async context's base address plus the size of a pointer. | ||
uint32_t ptr_size = target.GetArchitecture().GetAddressByteSize(); | ||
addr_t resume_ptr = async_ctx + ptr_size; | ||
Status status; | ||
addr_t resume_fn = process.ReadPointerFromMemory(resume_ptr, status); | ||
if (status.Fail() || resume_fn == LLDB_INVALID_ADDRESS) | ||
return createStringError("failed to read task's resume function"); | ||
|
||
return std::make_shared<ThreadTask>(tid, async_ctx, resume_fn, exe_ctx); | ||
} | ||
|
||
RegisterContextTask::RegisterContextTask(Thread &thread, | ||
RegisterContextSP reg_info_sp, | ||
addr_t resume_fn, addr_t async_ctx) | ||
: RegisterContext(thread, 0), m_reg_info_sp(reg_info_sp), | ||
m_async_ctx(async_ctx), m_resume_fn(resume_fn) { | ||
auto &target = thread.GetProcess()->GetTarget(); | ||
auto triple = target.GetArchitecture().GetTriple(); | ||
if (auto regnums = GetAsyncUnwindRegisterNumbers(triple.getArch())) | ||
m_async_ctx_regnum = regnums->async_ctx_regnum; | ||
} | ||
|
||
bool RegisterContextTask::ReadRegister(const RegisterInfo *reg_info, | ||
RegisterValue ®_value) { | ||
if (!reg_info) | ||
return false; | ||
|
||
if (reg_info->kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC) { | ||
reg_value = m_resume_fn; | ||
return true; | ||
} | ||
if (reg_info->kinds[eRegisterKindLLDB] == m_async_ctx_regnum) { | ||
reg_value = m_async_ctx; | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
} // namespace lldb_private |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
|
||
#include "lldb/Target/RegisterContext.h" | ||
#include "lldb/Target/Thread.h" | ||
#include "lldb/Utility/RegisterValue.h" | ||
#include "lldb/lldb-forward.h" | ||
|
||
namespace lldb_private { | ||
|
||
using namespace lldb; | ||
|
||
/// Provides a subset of Thread operations for Swift Tasks. | ||
/// | ||
/// Currently, this supports backtraces of Tasks, and selecting frames in the | ||
/// backtrace. Async frames make available the variables that are stored in the | ||
/// Task's "async context" (instead of the stack). | ||
/// | ||
/// See `Task<Success, Failure>` and `UnsafeCurrentTask` | ||
class ThreadTask final : public Thread { | ||
public: | ||
ThreadTask(tid_t tid, addr_t async_ctx, addr_t resume_fn, | ||
ExecutionContext &exe_ctx); | ||
|
||
static llvm::Expected<std::shared_ptr<ThreadTask>> | ||
Create(tid_t tid, addr_t async_ctx, ExecutionContext &exe_ctx); | ||
|
||
/// Returns a Task specific register context (RegisterContextTask). | ||
RegisterContextSP GetRegisterContext() override; | ||
|
||
~ThreadTask() override { DestroyThread(); } | ||
|
||
// No-op overrides. | ||
void RefreshStateAfterStop() override {} | ||
lldb::RegisterContextSP | ||
CreateRegisterContextForFrame(StackFrame *frame) override { | ||
return {}; | ||
} | ||
bool CalculateStopInfo() override { return false; } | ||
|
||
private: | ||
/// A register context that is the source of `RegisterInfo` data. | ||
RegisterContextSP m_reg_info_sp; | ||
/// Lazily initialized `RegisterContextTask`. | ||
RegisterContextSP m_async_reg_ctx_sp; | ||
/// The Task's async context. | ||
addr_t m_async_ctx = LLDB_INVALID_ADDRESS; | ||
/// The address of the async context's resume function. | ||
addr_t m_resume_fn = LLDB_INVALID_ADDRESS; | ||
}; | ||
|
||
/// A Swift Task specific register context. Supporting class for `ThreadTask`, | ||
/// see its documentation for details. | ||
class RegisterContextTask final : public RegisterContext { | ||
public: | ||
RegisterContextTask(Thread &thread, RegisterContextSP reg_info_sp, | ||
addr_t resume_fn, addr_t async_ctx); | ||
|
||
/// RegisterContextTask supports readonly from only two (necessary) | ||
/// registers. Namely, the pc and the async context registers. | ||
bool ReadRegister(const RegisterInfo *reg_info, | ||
RegisterValue ®_value) override; | ||
|
||
// Pass through overrides. | ||
size_t GetRegisterCount() override { | ||
return m_reg_info_sp->GetRegisterCount(); | ||
} | ||
const RegisterInfo *GetRegisterInfoAtIndex(size_t idx) override { | ||
return m_reg_info_sp->GetRegisterInfoAtIndex(idx); | ||
} | ||
size_t GetRegisterSetCount() override { | ||
return m_reg_info_sp->GetRegisterSetCount(); | ||
} | ||
const RegisterSet *GetRegisterSet(size_t reg_set) override { | ||
return m_reg_info_sp->GetRegisterSet(reg_set); | ||
} | ||
lldb::ByteOrder GetByteOrder() override { | ||
return m_reg_info_sp->GetByteOrder(); | ||
} | ||
|
||
// No-op overrides. | ||
void InvalidateAllRegisters() override {} | ||
bool WriteRegister(const RegisterInfo *reg_info, | ||
const RegisterValue ®_value) override { | ||
return false; | ||
} | ||
|
||
private: | ||
/// A register context that is the source of `RegisterInfo` data. | ||
RegisterContextSP m_reg_info_sp; | ||
/// The architecture specific regnum (LLDB) which holds the async context. | ||
uint32_t m_async_ctx_regnum = LLDB_INVALID_REGNUM; | ||
/// The Task's async context. | ||
RegisterValue m_async_ctx; | ||
/// The address of the async context's resume function. | ||
RegisterValue m_resume_fn; | ||
}; | ||
|
||
} // namespace lldb_private |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
21 changes: 21 additions & 0 deletions
21
lldb/test/API/lang/swift/async/tasks/TestSwiftTaskBacktrace.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import lldb | ||
from lldbsuite.test.decorators import * | ||
from lldbsuite.test.lldbtest import TestBase | ||
import lldbsuite.test.lldbutil as lldbutil | ||
|
||
|
||
class TestCase(TestBase): | ||
def test(self): | ||
self.build() | ||
lldbutil.run_to_source_breakpoint( | ||
self, "break here", lldb.SBFileSpec("main.swift") | ||
) | ||
self.expect( | ||
"language swift task backtrace task", | ||
substrs=[ | ||
".sleep(", | ||
"`second() at main.swift:6", | ||
"`first() at main.swift:2", | ||
"`closure #1 in static Main.main() at main.swift:12", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
func first() async { | ||
await second() | ||
} | ||
|
||
func second() async { | ||
try? await Task.sleep(for: .seconds(10)) | ||
} | ||
|
||
@main struct Main { | ||
static func main() async { | ||
let task = Task { | ||
await first() | ||
} | ||
try? await Task.sleep(for: .seconds(0.01)) | ||
print("break here") | ||
} | ||
} |