@@ -46,15 +46,13 @@ enum class ThunkKind {
46
46
ObjCAttribute,
47
47
Reabstraction,
48
48
ProtocolConformance,
49
- AsyncFunction,
50
49
};
51
50
52
51
enum class ThunkAction {
53
52
Unknown = 0 ,
54
53
GetThunkTarget,
55
54
StepIntoConformance,
56
55
StepThrough,
57
- AsyncStepIn,
58
56
};
59
57
60
58
} // namespace
@@ -232,12 +230,8 @@ static ThunkKind GetThunkKind(Symbol *symbol) {
232
230
if (nodes->getNumChildren () == 0 )
233
231
return ThunkKind::Unknown;
234
232
235
- if (!demangle_ctx.isThunkSymbol (symbol_name)) {
236
- if (IsSwiftAsyncFunctionSymbol (nodes)) {
237
- return ThunkKind::AsyncFunction;
238
- }
233
+ if (!demangle_ctx.isThunkSymbol (symbol_name))
239
234
return ThunkKind::Unknown;
240
- }
241
235
242
236
NodePointer main_node = nodes->getFirstChild ();
243
237
switch (main_node->getKind ()) {
@@ -276,8 +270,6 @@ static const char *GetThunkKindName(ThunkKind kind) {
276
270
return " GetThunkTarget" ;
277
271
case ThunkKind::ProtocolConformance:
278
272
return " StepIntoConformance" ;
279
- case ThunkKind::AsyncFunction:
280
- return " AsyncStepIn" ;
281
273
}
282
274
}
283
275
@@ -295,219 +287,115 @@ static ThunkAction GetThunkAction(ThunkKind kind) {
295
287
return ThunkAction::StepThrough;
296
288
case ThunkKind::ProtocolConformance:
297
289
return ThunkAction::StepIntoConformance;
298
- case ThunkKind::AsyncFunction:
299
- return ThunkAction::AsyncStepIn;
300
290
}
301
291
}
302
292
303
- class ThreadPlanStepInAsync : public ThreadPlan {
293
+ // / A thread plan to run to a specific address on a specific async context.
294
+ class ThreadPlanRunToAddressOnAsyncCtx : public ThreadPlan {
304
295
public:
305
- static bool NeedsStep (SymbolContext &sc) {
306
- if (sc.line_entry .IsValid () && sc.line_entry .line == 0 )
307
- // Compiler generated function, need to step in.
308
- return true ;
309
-
310
- // TEMPORARY HACK WORKAROUND
311
- if (!sc.symbol || !sc.comp_unit )
312
- return false ;
313
- auto fn_start = sc.symbol ->GetFileAddress ();
314
- auto fn_end = sc.symbol ->GetFileAddress () + sc.symbol ->GetByteSize ();
315
- llvm::SmallSet<uint32_t , 2 > unique_debug_lines;
316
- if (auto *line_table = sc.comp_unit ->GetLineTable ()) {
317
- for (uint32_t i = 0 ; i < line_table->GetSize (); ++i) {
318
- LineEntry line_entry;
319
- if (line_table->GetLineEntryAtIndex (i, line_entry)) {
320
- if (!line_entry.IsValid () || line_entry.line == 0 )
321
- continue ;
322
-
323
- auto line_start = line_entry.range .GetBaseAddress ().GetFileAddress ();
324
- if (fn_start <= line_start && line_start < fn_end) {
325
- unique_debug_lines.insert (line_entry.line );
326
- // This logic is to distinguish between async functions that only
327
- // call `swift_task_switch` (which, from the perspective of the
328
- // user, has no meaningful function body), vs async functions that
329
- // do have a function body. In the first case, lldb should step
330
- // further to find the function body, in the second case lldb has
331
- // found a body and should stop.
332
- //
333
- // Currently, async functions that go through `swift_task_switch`
334
- // are generated with a reference to a single line. If this function
335
- // has more than one unique debug line, then it is a function that
336
- // has a body, and execution can stop here.
337
- if (unique_debug_lines.size () >= 2 )
338
- // No step into `swift_task_switch` required.
339
- return false ;
340
- }
341
- }
342
- }
343
- }
344
-
345
- return true ;
296
+ // / Creates a thread plan to run to destination_addr of an async function
297
+ // / whose context is async_ctx.
298
+ ThreadPlanRunToAddressOnAsyncCtx (Thread &thread, addr_t destination_addr,
299
+ addr_t async_ctx)
300
+ : ThreadPlan(eKindGeneric, " run-to-funclet" , thread, eVoteNoOpinion,
301
+ eVoteNoOpinion),
302
+ m_destination_addr (destination_addr), m_expected_async_ctx(async_ctx) {
303
+ auto &target = thread.GetProcess ()->GetTarget ();
304
+ m_funclet_bp = target.CreateBreakpoint (destination_addr, true , false );
305
+ m_funclet_bp->SetBreakpointKind (" async-run-to-funclet" );
346
306
}
347
307
348
- ThreadPlanStepInAsync (Thread &thread, SymbolContext &sc)
349
- : ThreadPlan(eKindGeneric, " step-in-async" , thread, eVoteNoOpinion,
350
- eVoteNoOpinion) {
351
- assert (sc.function );
352
- if (!sc.function )
353
- return ;
354
-
355
- m_step_in_plan_sp = std::make_shared<ThreadPlanStepInRange>(
356
- thread, sc.function ->GetAddressRange (), sc, " swift_task_switch" ,
357
- RunMode::eAllThreads, eLazyBoolNo, eLazyBoolNo);
358
- }
308
+ bool ValidatePlan (Stream *error) override {
309
+ if (m_funclet_bp->HasResolvedLocations ())
310
+ return true ;
359
311
360
- void DidPush () override {
361
- if (m_step_in_plan_sp)
362
- PushPlan (m_step_in_plan_sp) ;
312
+ // If we failed to resolve any locations, this plan is invalid.
313
+ m_funclet_bp-> GetTarget (). RemoveBreakpointByID (m_funclet_bp-> GetID ());
314
+ return false ;
363
315
}
364
316
365
- bool ValidatePlan (Stream *error) override { return (bool )m_step_in_plan_sp; }
366
-
367
317
void GetDescription (Stream *s, lldb::DescriptionLevel level) override {
368
- // TODO: Implement completely.
369
- s->PutCString (" ThreadPlanStepInAsync" );
318
+ s->PutCString (" ThreadPlanRunToAddressOnAsyncCtx to address = " );
319
+ s->PutHex64 (m_destination_addr);
320
+ s->PutCString (" with async ctx = " );
321
+ s->PutHex64 (m_expected_async_ctx);
370
322
}
371
323
324
+ // / This plan explains the stop if the current async context is the async
325
+ // / context this plan was created with.
372
326
bool DoPlanExplainsStop (Event *event) override {
373
327
if (!HasTID ())
374
328
return false ;
375
-
376
- if (!m_async_breakpoint_sp)
377
- return false ;
378
-
379
- return GetBreakpointAsyncContext () == m_initial_async_ctx;
329
+ return GetCurrentAsyncContext () == m_expected_async_ctx;
380
330
}
381
331
332
+ // / If this plan explained the stop, it always stops: its sole purpose is to
333
+ // / run to the breakpoint it set on the right async function invocation.
382
334
bool ShouldStop (Event *event) override {
383
- if (!m_async_breakpoint_sp)
384
- return false ;
385
-
386
- if (GetBreakpointAsyncContext () != m_initial_async_ctx)
387
- return false ;
388
-
389
335
SetPlanComplete ();
390
336
return true ;
391
337
}
392
338
339
+ // / If this plan said ShouldStop, then its job is complete.
393
340
bool MischiefManaged () override {
394
- if (IsPlanComplete ())
395
- return true ;
396
-
397
- if (!m_step_in_plan_sp->IsPlanComplete ())
398
- return false ;
399
-
400
- if (!m_step_in_plan_sp->PlanSucceeded ()) {
401
- // If the step in fails, then this plan fails.
402
- SetPlanComplete (false );
403
- return true ;
404
- }
405
-
406
- if (!m_async_breakpoint_sp) {
407
- auto &thread = GetThread ();
408
- m_async_breakpoint_sp = CreateAsyncBreakpoint (thread);
409
- m_initial_async_ctx = GetAsyncContext (thread.GetStackFrameAtIndex (1 ));
410
- ClearTID ();
411
- }
412
-
413
- return false ;
341
+ return IsPlanComplete ();
414
342
}
415
343
416
344
bool WillStop () override { return false ; }
417
-
418
345
lldb::StateType GetPlanRunState () override { return eStateRunning; }
419
-
420
346
bool StopOthers () override { return false ; }
421
-
422
347
void DidPop () override {
423
- if (m_async_breakpoint_sp)
424
- m_async_breakpoint_sp->GetTarget ().RemoveBreakpointByID (
425
- m_async_breakpoint_sp->GetID ());
348
+ m_funclet_bp->GetTarget ().RemoveBreakpointByID (m_funclet_bp->GetID ());
426
349
}
427
350
428
351
private:
429
- bool IsAtAsyncBreakpoint () {
430
- auto stop_info_sp = GetPrivateStopInfo ();
431
- if (!stop_info_sp)
432
- return false ;
433
-
434
- if (stop_info_sp->GetStopReason () != eStopReasonBreakpoint)
435
- return false ;
436
-
437
- auto &site_list = m_process.GetBreakpointSiteList ();
438
- auto site_sp = site_list.FindByID (stop_info_sp->GetValue ());
439
- if (!site_sp)
440
- return false ;
441
-
442
- return site_sp->IsBreakpointAtThisSite (m_async_breakpoint_sp->GetID ());
443
- }
444
-
445
- std::optional<lldb::addr_t > GetBreakpointAsyncContext () {
446
- if (m_breakpoint_async_ctx)
447
- return m_breakpoint_async_ctx;
448
-
449
- if (!IsAtAsyncBreakpoint ())
450
- return {};
451
-
352
+ addr_t GetCurrentAsyncContext () {
452
353
auto frame_sp = GetThread ().GetStackFrameAtIndex (0 );
453
- auto async_ctx = GetAsyncContext (frame_sp);
454
-
455
- if (!IsIndirectContext (frame_sp)) {
456
- m_breakpoint_async_ctx = async_ctx;
457
- return m_breakpoint_async_ctx;
458
- }
459
-
460
- // Dereference the indirect async context.
461
- auto process_sp = GetThread ().GetProcess ();
462
- Status error;
463
- m_breakpoint_async_ctx =
464
- process_sp->ReadPointerFromMemory (async_ctx, error);
465
- return m_breakpoint_async_ctx;
466
- }
467
-
468
- bool IsIndirectContext (lldb::StackFrameSP frame_sp) {
469
- auto sc = frame_sp->GetSymbolContext (eSymbolContextSymbol);
470
- auto mangled_name = sc.symbol ->GetMangled ().GetMangledName ().GetStringRef ();
471
- return SwiftLanguageRuntime::IsSwiftAsyncAwaitResumePartialFunctionSymbol (
472
- mangled_name);
354
+ return frame_sp->GetStackID ().GetCallFrameAddress ();
473
355
}
474
356
475
- BreakpointSP CreateAsyncBreakpoint (Thread &thread) {
476
- // The signature for `swift_task_switch` is as follows:
477
- // SWIFT_CC(swiftasync)
478
- // void swift_task_switch(
479
- // SWIFT_ASYNC_CONTEXT AsyncContext *resumeContext,
480
- // TaskContinuationFunction *resumeFunction,
481
- // ExecutorRef newExecutor);
482
- //
483
- // The async context given as the first argument is not passed using the
484
- // calling convention's first register, it's passed in the platform's async
485
- // context register. This means the `resumeFunction` parameter uses the
486
- // first ABI register (ex: x86-64: rdi, arm64: x0).
487
- auto reg_ctx = thread.GetStackFrameAtIndex (0 )->GetRegisterContext ();
488
- constexpr auto resume_fn_regnum = LLDB_REGNUM_GENERIC_ARG1;
489
- auto resume_fn_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (
490
- RegisterKind::eRegisterKindGeneric, resume_fn_regnum);
491
- auto resume_fn_ptr = reg_ctx->ReadRegisterAsUnsigned (resume_fn_reg, 0 );
492
- if (!resume_fn_ptr)
493
- return {};
357
+ addr_t m_destination_addr;
358
+ addr_t m_expected_async_ctx;
359
+ BreakpointSP m_funclet_bp;
360
+ };
494
361
495
- auto &target = thread.GetProcess ()->GetTarget ();
496
- auto breakpoint_sp = target.CreateBreakpoint (resume_fn_ptr, true , false );
497
- breakpoint_sp->SetBreakpointKind (" async-step" );
498
- return breakpoint_sp;
499
- }
362
+ // / Given a thread that is stopped at the start of swift_task_switch, create a
363
+ // / thread plan that runs to the address of the resume function.
364
+ static ThreadPlanSP CreateRunThroughTaskSwitchThreadPlan (Thread &thread) {
365
+ // The signature for `swift_task_switch` is as follows:
366
+ // SWIFT_CC(swiftasync)
367
+ // void swift_task_switch(
368
+ // SWIFT_ASYNC_CONTEXT AsyncContext *resumeContext,
369
+ // TaskContinuationFunction *resumeFunction,
370
+ // ExecutorRef newExecutor);
371
+ //
372
+ // The async context given as the first argument is not passed using the
373
+ // calling convention's first register, it's passed in the platform's async
374
+ // context register. This means the `resumeFunction` parameter uses the
375
+ // first ABI register (ex: x86-64: rdi, arm64: x0).
376
+ RegisterContextSP reg_ctx =
377
+ thread.GetStackFrameAtIndex (0 )->GetRegisterContext ();
378
+ constexpr unsigned resume_fn_regnum = LLDB_REGNUM_GENERIC_ARG1;
379
+ unsigned resume_fn_reg = reg_ctx->ConvertRegisterKindToRegisterNumber (
380
+ RegisterKind::eRegisterKindGeneric, resume_fn_regnum);
381
+ uint64_t resume_fn_ptr = reg_ctx->ReadRegisterAsUnsigned (resume_fn_reg, 0 );
382
+ if (!resume_fn_ptr)
383
+ return {};
500
384
501
- static lldb::addr_t GetAsyncContext (lldb::StackFrameSP frame_sp) {
502
- auto reg_ctx_sp = frame_sp->GetRegisterContext ();
503
- return SwiftLanguageRuntime::GetAsyncContext (reg_ctx_sp.get ());
504
- }
385
+ auto arch = reg_ctx->CalculateTarget ()->GetArchitecture ();
386
+ std::optional<AsyncUnwindRegisterNumbers> async_regs =
387
+ GetAsyncUnwindRegisterNumbers (arch.GetMachine ());
388
+ if (!async_regs)
389
+ return {};
390
+ unsigned async_reg_number = reg_ctx->ConvertRegisterKindToRegisterNumber (
391
+ async_regs->GetRegisterKind (), async_regs->async_ctx_regnum );
392
+ uint64_t async_ctx = reg_ctx->ReadRegisterAsUnsigned (async_reg_number, 0 );
393
+ if (!async_ctx)
394
+ return {};
505
395
506
- ThreadPlanSP m_step_in_plan_sp;
507
- BreakpointSP m_async_breakpoint_sp;
508
- std::optional<lldb::addr_t > m_initial_async_ctx;
509
- std::optional<lldb::addr_t > m_breakpoint_async_ctx;
510
- };
396
+ return std::make_shared<ThreadPlanRunToAddressOnAsyncCtx>(
397
+ thread, resume_fn_ptr, async_ctx);
398
+ }
511
399
512
400
static lldb::ThreadPlanSP GetStepThroughTrampolinePlan (Thread &thread,
513
401
bool stop_others) {
@@ -538,18 +426,18 @@ static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
538
426
if (symbol_addr != cur_addr)
539
427
return nullptr ;
540
428
541
- const char *symbol_name = symbol->GetMangled ().GetMangledName ().AsCString ();
429
+ Mangled &mangled_symbol_name = symbol->GetMangled ();
430
+ const char *symbol_name = mangled_symbol_name.GetMangledName ().AsCString ();
431
+
432
+ if (mangled_symbol_name.GetDemangledName () == " swift_task_switch" )
433
+ return CreateRunThroughTaskSwitchThreadPlan (thread);
542
434
543
435
ThunkKind thunk_kind = GetThunkKind (symbol);
544
436
ThunkAction thunk_action = GetThunkAction (thunk_kind);
545
437
546
438
switch (thunk_action) {
547
439
case ThunkAction::Unknown:
548
440
return nullptr ;
549
- case ThunkAction::AsyncStepIn:
550
- if (ThreadPlanStepInAsync::NeedsStep (sc))
551
- return std::make_shared<ThreadPlanStepInAsync>(thread, sc);
552
- return nullptr ;
553
441
case ThunkAction::GetThunkTarget: {
554
442
swift::Demangle::Context demangle_ctx;
555
443
std::string thunk_target = demangle_ctx.getThunkTarget (symbol_name);
0 commit comments