Skip to content

[lldb] Cherry-pick lost commits during the 6.1 branch #9654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -361,23 +361,13 @@ class ThreadPlanRunToAddressOnAsyncCtx : public ThreadPlan {

/// Given a thread that is stopped at the start of swift_task_switch, create a
/// thread plan that runs to the address of the resume function.
static ThreadPlanSP CreateRunThroughTaskSwitchThreadPlan(Thread &thread) {
// The signature for `swift_task_switch` is as follows:
// SWIFT_CC(swiftasync)
// void swift_task_switch(
// SWIFT_ASYNC_CONTEXT AsyncContext *resumeContext,
// TaskContinuationFunction *resumeFunction,
// ExecutorRef newExecutor);
//
// The async context given as the first argument is not passed using the
// calling convention's first register, it's passed in the platform's async
// context register. This means the `resumeFunction` parameter uses the
// first ABI register (ex: x86-64: rdi, arm64: x0).
static ThreadPlanSP
CreateRunThroughTaskSwitchThreadPlan(Thread &thread,
unsigned resume_fn_generic_regnum) {
RegisterContextSP reg_ctx =
thread.GetStackFrameAtIndex(0)->GetRegisterContext();
constexpr unsigned resume_fn_regnum = LLDB_REGNUM_GENERIC_ARG1;
unsigned resume_fn_reg = reg_ctx->ConvertRegisterKindToRegisterNumber(
RegisterKind::eRegisterKindGeneric, resume_fn_regnum);
RegisterKind::eRegisterKindGeneric, resume_fn_generic_regnum);
uint64_t resume_fn_ptr = reg_ctx->ReadRegisterAsUnsigned(resume_fn_reg, 0);
if (!resume_fn_ptr)
return {};
Expand All @@ -397,6 +387,41 @@ static ThreadPlanSP CreateRunThroughTaskSwitchThreadPlan(Thread &thread) {
thread, resume_fn_ptr, async_ctx);
}

/// Creates a thread plan to step over swift runtime functions that can trigger
/// a task switch, like `async_task_switch` or `swift_asyncLet_get`.
static ThreadPlanSP
CreateRunThroughTaskSwitchingTrampolines(Thread &thread,
StringRef trampoline_name) {
// The signature for `swift_task_switch` is as follows:
// SWIFT_CC(swiftasync)
// void swift_task_switch(
// SWIFT_ASYNC_CONTEXT AsyncContext *resumeContext,
// TaskContinuationFunction *resumeFunction,
// ExecutorRef newExecutor);
//
// The async context given as the first argument is not passed using the
// calling convention's first register, it's passed in the platform's async
// context register. This means the `resumeFunction` parameter uses the
// first ABI register (ex: x86-64: rdi, arm64: x0).
if (trampoline_name == "swift_task_switch")
return CreateRunThroughTaskSwitchThreadPlan(thread,
LLDB_REGNUM_GENERIC_ARG1);
// The signature for `swift_asyncLet_get` and `swift_asyncLet_finish` are the
// same. Like `task_switch`, the async context (first argument) uses the async
// context register, and not the arg1 register; as such, the continuation
// funclet can be found in arg3.
//
// swift_asyncLet_get(SWIFT_ASYNC_CONTEXT AsyncContext *,
// AsyncLet *,
// void *,
// TaskContinuationFunction *,
if (trampoline_name == "swift_asyncLet_get" ||
trampoline_name == "swift_asyncLet_finish")
return CreateRunThroughTaskSwitchThreadPlan(thread,
LLDB_REGNUM_GENERIC_ARG3);
return nullptr;
}

static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
bool stop_others) {
// Here are the trampolines we have at present.
Expand Down Expand Up @@ -429,8 +454,9 @@ static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
Mangled &mangled_symbol_name = symbol->GetMangled();
const char *symbol_name = mangled_symbol_name.GetMangledName().AsCString();

if (mangled_symbol_name.GetDemangledName() == "swift_task_switch")
return CreateRunThroughTaskSwitchThreadPlan(thread);
if (ThreadPlanSP thread_plan = CreateRunThroughTaskSwitchingTrampolines(
thread, mangled_symbol_name.GetDemangledName()))
return thread_plan;

ThunkKind thunk_kind = GetThunkKind(symbol);
ThunkAction thunk_action = GetThunkAction(thunk_kind);
Expand Down
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import lldb
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbtest as lldbtest
import lldbsuite.test.lldbutil as lldbutil


@skipIfAsan # rdar://138777205
class TestCase(lldbtest.TestBase):

def check_is_in_line(self, thread, linenum):
frame = thread.frames[0]
line_entry = frame.GetLineEntry()
self.assertEqual(linenum, line_entry.GetLine())

@swiftTest
@skipIf(oslist=["windows", "linux"])
def test(self):
"""Test conditions for async step-over."""
self.build()

source_file = lldb.SBFileSpec("main.swift")
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
self, "BREAK HERE", source_file
)

# Step over should reach every line in the interval [10, 20]
expected_line_nums = [10, 11, 12, 13, 14, 15]
# FIXME: for some reason we loop back to the start of the do block after the last statement.
# rdar://140159600
expected_line_nums += [8]
expected_line_nums += [17, 18, 19, 20]
for expected_line_num in expected_line_nums:
thread.StepOver()
stop_reason = thread.GetStopReason()
self.assertStopReason(stop_reason, lldb.eStopReasonPlanComplete)
self.check_is_in_line(thread, expected_line_num)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
func getTimestamp(x: Int) async -> Int {
return 40 + x
}

func work() {}

func foo() async {
do {
work() // BREAK HERE
async let timestamp1 = getTimestamp(x:1)
work()
async let timestamp2 = getTimestamp(x:2)
work()
let timestamps = await [timestamp1, timestamp2]
print(timestamps)
}
async let timestamp3 = getTimestamp(x:3)
work()
let actual_timestamp3 = await timestamp3
print(actual_timestamp3)
}

@main enum entry {
static func main() async {
await foo()
}
}