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 @@ -173,7 +173,30 @@ 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 + /// asynchronous calls they made, but are part of a logical backtrace that + /// we want to show the developer because that's how they think of the + /// program flow. + static lldb::UnwindPlanSP + GetRuntimeUnwindPlan(lldb_private::Thread &thread, + lldb_private::RegisterContext *regctx); + protected: + // The static GetRuntimeUnwindPlan method above is only implemented in the + // base class; subclasses may override this protected member if they can + // provide one of these UnwindPlans. + virtual lldb::UnwindPlanSP + GetRuntimeUnwindPlan(lldb::ProcessSP process_sp, + 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::GetRuntimeUnwindPlan(Thread &thread, + RegisterContext *regctx) { + ProcessSP process_sp = thread.GetProcess(); + if (!process_sp.get()) + return UnwindPlanSP(); + for (const lldb::LanguageType lang_type : Language::GetSupportedLanguages()) { + if (LanguageRuntime *runtime = process_sp->GetLanguageRuntime(lang_type)) { + UnwindPlanSP plan_sp = runtime->GetRuntimeUnwindPlan(process_sp, 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,22 @@ 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 LanguageRuntime may provide an UnwindPlan that is used in this + // stack trace base on the RegisterContext contents, intsead + // of the normal UnwindPlans we would use for the return-pc. + UnwindPlanSP lang_runtime_plan_sp = + LanguageRuntime::GetRuntimeUnwindPlan(m_thread, this); + if (lang_runtime_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 +307,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 +537,43 @@ } } - // 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 LanguageRuntime UnwindPlan for this unwind, 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 (lang_runtime_plan_sp.get()) { + active_row = + lang_runtime_plan_sp->GetRowForFunctionOffset(m_current_offset); + row_register_kind = lang_runtime_plan_sp->GetRegisterKind(); + if (!ReadFrameAddress(row_register_kind, active_row->GetCFAValue(), + m_cfa)) { + UnwindLogMsg("Cannot set cfa"); + } else { + m_full_unwind_plan_sp = lang_runtime_plan_sp; + if (log) { + StreamString active_row_strm; + active_row->Dump(active_row_strm, lang_runtime_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.)