diff --git a/lldb/include/lldb/Target/LanguageRuntime.h b/lldb/include/lldb/Target/LanguageRuntime.h --- a/lldb/include/lldb/Target/LanguageRuntime.h +++ b/lldb/include/lldb/Target/LanguageRuntime.h @@ -18,6 +18,7 @@ #include "lldb/Expression/LLVMUserExpression.h" #include "lldb/Symbol/DeclVendor.h" #include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/RegisterContext.h" #include "lldb/Target/Runtime.h" #include "lldb/lldb-private.h" #include "lldb/lldb-public.h" @@ -173,7 +174,26 @@ virtual bool isA(const void *ClassID) const { return ClassID == &ID; } static char ID; + // A language runtime may be able to provide a special UnwindPlan for + // the frame represented by the register contents \a regctx when that + // frame is not following the normal ABI conventions. + // Instead of using the normal UnwindPlan for the function, we will use + // this special UnwindPlan for this one backtrace. + // One example of this would be a language that has asynchronous functions, + // functions that may not be currently-executing, while waiting on other + // asynchornous calls they made, but are part of a logical backtrace how + // the programmer views the call stack. + static lldb::UnwindPlanSP + GetAsyncedFrameUnwindPlan(lldb_private::Thread &thread, + lldb_private::RegisterContext *regctx); + protected: + virtual lldb::UnwindPlanSP + GetAsyncedFrameForThisLanguage(lldb_private::Thread &thread, + lldb_private::RegisterContext *regctx) { + return lldb::UnwindPlanSP(); + } + LanguageRuntime(Process *process); }; diff --git a/lldb/source/Target/LanguageRuntime.cpp b/lldb/source/Target/LanguageRuntime.cpp --- a/lldb/source/Target/LanguageRuntime.cpp +++ b/lldb/source/Target/LanguageRuntime.cpp @@ -259,6 +259,21 @@ return exc_breakpt_sp; } +UnwindPlanSP +LanguageRuntime::GetAsyncedFrameUnwindPlan(Thread &thread, + RegisterContext *regctx) { + for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) { + if (LanguageRuntime *runtime = + thread.GetProcess()->GetLanguageRuntime(lang_type)) { + UnwindPlanSP plan_sp = + runtime->GetAsyncedFrameForThisLanguage(thread, regctx); + if (plan_sp.get()) + return plan_sp; + } + } + return UnwindPlanSP(); +} + void LanguageRuntime::InitializeCommands(CommandObject *parent) { if (!parent) return; diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -24,6 +24,7 @@ #include "lldb/Target/ABI.h" #include "lldb/Target/DynamicLoader.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" @@ -283,6 +284,24 @@ return; } + ExecutionContext exe_ctx(m_thread.shared_from_this()); + Process *process = exe_ctx.GetProcessPtr(); + + // Some languages may have a logical parent stack frame which is + // not a real stack frame, but the programmer would consider it to + // be the caller of the frame, e.g. Swift asynchronous frames. + // + // A Swift async function will have a normal UnwindPlan when it is + // executing, but it may be unscheduled (or on a different thread) + // when the function it calls asynchronously, and we need to use an + // UnwindPlan from the LanguageRuntime to present this frame to the + // user in the backtrace. + UnwindPlanSP async_plan_sp = + LanguageRuntime::GetAsyncedFrameUnwindPlan(m_thread, this); + if (async_plan_sp.get()) { + UnwindLogMsg("This is an async frame"); + } + addr_t pc; if (!ReadGPRValue(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc)) { UnwindLogMsg("could not get pc value"); @@ -290,8 +309,6 @@ return; } - ExecutionContext exe_ctx(m_thread.shared_from_this()); - Process *process = exe_ctx.GetProcessPtr(); // Let ABIs fixup code addresses to make sure they are valid. In ARM ABIs // this will strip bit zero in case we read a PC from memory or from the LR. ABI *abi = process->GetABI().get(); @@ -522,12 +539,42 @@ } } - // We've set m_frame_type and m_sym_ctx before this call. - m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame(); - UnwindPlan::RowSP active_row; RegisterKind row_register_kind = eRegisterKindGeneric; + // If we have an asynchronous unwind plan from the LanguageRuntime, + // use those rules to find the caller frame instead of the function's + // normal UnwindPlans. The full unwind plan for this frame will be + // the LanguageRuntime-provided unwind plan, and there will not be a + // fast unwind plan. + if (async_plan_sp.get()) { + active_row = async_plan_sp->GetRowForFunctionOffset(m_current_offset); + row_register_kind = async_plan_sp->GetRegisterKind(); + if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), + m_cfa)) { + UnwindLogMsg("Cannot set cfa"); + } else { + m_full_unwind_plan_sp = async_plan_sp; + if (active_row.get() && log) { + StreamString active_row_strm; + active_row->Dump(active_row_strm, async_plan_sp.get(), &m_thread, + m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr())); + UnwindLogMsg("async active row: %s", active_row_strm.GetData()); + } + UnwindLogMsg("m_cfa = 0x%" PRIx64 " m_afa = 0x%" PRIx64, m_cfa, m_afa); + UnwindLogMsg( + "initialized async frame current pc is 0x%" PRIx64 + " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64, + (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), + (uint64_t)m_cfa, (uint64_t)m_afa); + + return; + } + } + + // We've set m_frame_type and m_sym_ctx before this call. + m_fast_unwind_plan_sp = GetFastUnwindPlanForFrame(); + // Try to get by with just the fast UnwindPlan if possible - the full // UnwindPlan may be expensive to get (e.g. if we have to parse the entire // eh_frame section of an ObjectFile for the first time.)