@@ -46,15 +46,13 @@ enum class ThunkKind {
4646 ObjCAttribute,
4747 Reabstraction,
4848 ProtocolConformance,
49- AsyncFunction,
5049};
5150
5251enum class ThunkAction {
5352 Unknown = 0 ,
5453 GetThunkTarget,
5554 StepIntoConformance,
5655 StepThrough,
57- AsyncStepIn,
5856};
5957
6058} // namespace
@@ -232,12 +230,8 @@ static ThunkKind GetThunkKind(Symbol *symbol) {
232230 if (nodes->getNumChildren () == 0 )
233231 return ThunkKind::Unknown;
234232
235- if (!demangle_ctx.isThunkSymbol (symbol_name)) {
236- if (IsSwiftAsyncFunctionSymbol (nodes)) {
237- return ThunkKind::AsyncFunction;
238- }
233+ if (!demangle_ctx.isThunkSymbol (symbol_name))
239234 return ThunkKind::Unknown;
240- }
241235
242236 NodePointer main_node = nodes->getFirstChild ();
243237 switch (main_node->getKind ()) {
@@ -276,8 +270,6 @@ static const char *GetThunkKindName(ThunkKind kind) {
276270 return " GetThunkTarget" ;
277271 case ThunkKind::ProtocolConformance:
278272 return " StepIntoConformance" ;
279- case ThunkKind::AsyncFunction:
280- return " AsyncStepIn" ;
281273 }
282274}
283275
@@ -295,219 +287,115 @@ static ThunkAction GetThunkAction(ThunkKind kind) {
295287 return ThunkAction::StepThrough;
296288 case ThunkKind::ProtocolConformance:
297289 return ThunkAction::StepIntoConformance;
298- case ThunkKind::AsyncFunction:
299- return ThunkAction::AsyncStepIn;
300290 }
301291}
302292
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 {
304295public:
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" );
346306 }
347307
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 ;
359311
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 ;
363315 }
364316
365- bool ValidatePlan (Stream *error) override { return (bool )m_step_in_plan_sp; }
366-
367317 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);
370322 }
371323
324+ // / This plan explains the stop if the current async context is the async
325+ // / context this plan was created with.
372326 bool DoPlanExplainsStop (Event *event) override {
373327 if (!HasTID ())
374328 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;
380330 }
381331
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.
382334 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-
389335 SetPlanComplete ();
390336 return true ;
391337 }
392338
339+ // / If this plan said ShouldStop, then its job is complete.
393340 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 ();
414342 }
415343
416344 bool WillStop () override { return false ; }
417-
418345 lldb::StateType GetPlanRunState () override { return eStateRunning; }
419-
420346 bool StopOthers () override { return false ; }
421-
422347 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 ());
426349 }
427350
428351private:
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 () {
452353 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 ();
473355 }
474356
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+ };
494361
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 {};
500384
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 {};
505395
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+ }
511399
512400static lldb::ThreadPlanSP GetStepThroughTrampolinePlan (Thread &thread,
513401 bool stop_others) {
@@ -538,18 +426,18 @@ static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
538426 if (symbol_addr != cur_addr)
539427 return nullptr ;
540428
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);
542434
543435 ThunkKind thunk_kind = GetThunkKind (symbol);
544436 ThunkAction thunk_action = GetThunkAction (thunk_kind);
545437
546438 switch (thunk_action) {
547439 case ThunkAction::Unknown:
548440 return nullptr ;
549- case ThunkAction::AsyncStepIn:
550- if (ThreadPlanStepInAsync::NeedsStep (sc))
551- return std::make_shared<ThreadPlanStepInAsync>(thread, sc);
552- return nullptr ;
553441 case ThunkAction::GetThunkTarget: {
554442 swift::Demangle::Context demangle_ctx;
555443 std::string thunk_target = demangle_ctx.getThunkTarget (symbol_name);
0 commit comments