Skip to content

Commit e6ebf2a

Browse files
[lldb][swift] Remove ThreadPlanStepInAsync
This thread plan attempts to identify whether a funclet contains a single line in the line table. If it does, it considers such a funclet to contain no user code and then attempts to step through it, reaching the next funclet. This is problematic, as some funclets _can_ have a single line for legitimate reasons. Recent improvements are taking LLDB closer to being able to step from any funclet to any funclet. As such, if a `thread step-*` command takes us to one "single-line"-funclet, this should not be problematic. LLDB must know how to move from this funclet to wherever the next `thread step-*` requires. The other evidence this plan is not really needed is that no tests fail when it is removed. It seems that `API/lang/swift/async/stepping/step-in/task-switch` was originally intended to test this step-through behavior, but the async function of that test had a **single** funclet (the entry funclet). Stepping through that would have taken us back to caller, which would have been incorrect. With or without this patch, we are correctly stopping at the entry funclet. In fact, the test is even checking we do so: ``` self.assertEqual(frame.name, "a.f() async -> Swift.Int") ``` Note that the demangled name doesn't contain a number prior to `a.f()`, which is how non-entry funclets can be identified. With all that in mind, it doesn't seem like this test was exercising ThreadPlanStepInAsync. A follow up commit will use one idea from ThreadPlanStepInAsync in order to step through *swift_task_switch* (as opposed to through funclets) to the target funclet of swift_task_switch.
1 parent ef32f25 commit e6ebf2a

File tree

1 file changed

+1
-224
lines changed

1 file changed

+1
-224
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp

Lines changed: 1 addition & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,13 @@ enum class ThunkKind {
4646
ObjCAttribute,
4747
Reabstraction,
4848
ProtocolConformance,
49-
AsyncFunction,
5049
};
5150

5251
enum 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,220 +287,9 @@ 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 {
304-
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;
346-
}
347-
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-
}
359-
360-
void DidPush() override {
361-
if (m_step_in_plan_sp)
362-
PushPlan(m_step_in_plan_sp);
363-
}
364-
365-
bool ValidatePlan(Stream *error) override { return (bool)m_step_in_plan_sp; }
366-
367-
void GetDescription(Stream *s, lldb::DescriptionLevel level) override {
368-
// TODO: Implement completely.
369-
s->PutCString("ThreadPlanStepInAsync");
370-
}
371-
372-
bool DoPlanExplainsStop(Event *event) override {
373-
if (!HasTID())
374-
return false;
375-
376-
if (!m_async_breakpoint_sp)
377-
return false;
378-
379-
return GetBreakpointAsyncContext() == m_initial_async_ctx;
380-
}
381-
382-
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-
SetPlanComplete();
390-
return true;
391-
}
392-
393-
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;
414-
}
415-
416-
bool WillStop() override { return false; }
417-
418-
lldb::StateType GetPlanRunState() override { return eStateRunning; }
419-
420-
bool StopOthers() override { return false; }
421-
422-
void DidPop() override {
423-
if (m_async_breakpoint_sp)
424-
m_async_breakpoint_sp->GetTarget().RemoveBreakpointByID(
425-
m_async_breakpoint_sp->GetID());
426-
}
427-
428-
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-
452-
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);
473-
}
474-
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 {};
494-
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-
}
500-
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-
}
505-
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-
};
511-
512293
static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
513294
bool stop_others) {
514295
// Here are the trampolines we have at present.
@@ -546,10 +327,6 @@ static lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
546327
switch (thunk_action) {
547328
case ThunkAction::Unknown:
548329
return nullptr;
549-
case ThunkAction::AsyncStepIn:
550-
if (ThreadPlanStepInAsync::NeedsStep(sc))
551-
return std::make_shared<ThreadPlanStepInAsync>(thread, sc);
552-
return nullptr;
553330
case ThunkAction::GetThunkTarget: {
554331
swift::Demangle::Context demangle_ctx;
555332
std::string thunk_target = demangle_ctx.getThunkTarget(symbol_name);

0 commit comments

Comments
 (0)