jasonmolenda created this revision. jasonmolenda added a reviewer: clayborg. jasonmolenda added a project: LLDB. Herald added a subscriber: JDevlieghere. jasonmolenda requested review of this revision.
This patch allows a RegisterContext to record if it behaves like a 0th frame -- where its pc value should not be decremented before symbolication. It adds a call out to the LanguageRuntime to get a special UnwindPlan when unwinding from the 0th (first) frame, not just further unwinds up the stack. The majority of the patch is the mechanical addition of the new ivar/method to expose this, and the LanguageRuntime hook in InitializeZerothFrame(). This is a follow-on patch to the previous https://reviews.llvm.org/D96839 For the Swift async unwinds, the saved pc values we have are not normal ABI style calls, and backing up the resume pc results in bad symbolication. I've been meaning to add a capability like this to RegisterContext for a while for DWARFExpression::Evaluate where we get a frame's pc value to look up a location list entry too (and do not decr the pc resulting in bogus values being displayed for noreturn calls) - I'll fix that in a separate patch. I have API tests when the Swift language runtime is present, but in generic lldb we don't have an environment quite like this. In the followup patch to DWARFExpression::Evaluate I'll be able to test RegisterContext::BehavesLikeZerothFrame so there will be some test coverage then. rdar://problem/70398009 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D97644 Files: lldb/include/lldb/Target/LanguageRuntime.h lldb/include/lldb/Target/RegisterContext.h lldb/include/lldb/Target/RegisterContextUnwind.h lldb/source/Target/LanguageRuntime.cpp lldb/source/Target/RegisterContextUnwind.cpp lldb/source/Target/StackFrameList.cpp lldb/source/Target/UnwindLLDB.cpp
Index: lldb/source/Target/UnwindLLDB.cpp =================================================================== --- lldb/source/Target/UnwindLLDB.cpp +++ lldb/source/Target/UnwindLLDB.cpp @@ -420,6 +420,8 @@ // too behaves like the zeroth frame (i.e. the pc might not // be pointing just past a call in it) behaves_like_zeroth_frame = true; + } else if (m_frames[idx]->reg_ctx_lldb_sp->BehavesLikeZerothFrame()) { + behaves_like_zeroth_frame = true; } else { behaves_like_zeroth_frame = false; } Index: lldb/source/Target/StackFrameList.cpp =================================================================== --- lldb/source/Target/StackFrameList.cpp +++ lldb/source/Target/StackFrameList.cpp @@ -529,7 +529,7 @@ // used to lookup the symbol context above. If we are in the first // concrete frame, then we lookup using the current address, else we // decrement the address by one to get the correct location. - if (idx > 0) { + if (idx > 0 && !behaves_like_zeroth_frame) { if (curr_frame_address.GetOffset() == 0) { // If curr_frame_address points to the first address in a section // then after adjustment it will point to an other section. In that Index: lldb/source/Target/RegisterContextUnwind.cpp =================================================================== --- lldb/source/Target/RegisterContextUnwind.cpp +++ lldb/source/Target/RegisterContextUnwind.cpp @@ -58,10 +58,11 @@ m_fast_unwind_plan_sp(), m_full_unwind_plan_sp(), m_fallback_unwind_plan_sp(), m_all_registers_available(false), m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS), - m_afa(LLDB_INVALID_ADDRESS), m_start_pc(), - m_current_pc(), m_current_offset(0), m_current_offset_backed_up_one(0), - m_sym_ctx(sym_ctx), m_sym_ctx_valid(false), m_frame_number(frame_number), - m_registers(), m_parent_unwind(unwind_lldb) { + m_afa(LLDB_INVALID_ADDRESS), m_start_pc(), m_current_pc(), + m_current_offset(0), m_current_offset_backed_up_one(0), + m_behaves_like_zeroth_frame(false), m_sym_ctx(sym_ctx), + m_sym_ctx_valid(false), m_frame_number(frame_number), m_registers(), + m_parent_unwind(unwind_lldb) { m_sym_ctx.Clear(false); m_sym_ctx_valid = false; @@ -140,6 +141,12 @@ if (abi) current_pc = abi->FixCodeAddress(current_pc); + UnwindPlanSP lang_runtime_plan_sp = LanguageRuntime::GetRuntimeUnwindPlan( + m_thread, this, m_behaves_like_zeroth_frame); + if (lang_runtime_plan_sp.get()) { + UnwindLogMsg("This is an async frame"); + } + // Initialize m_current_pc, an Address object, based on current_pc, an // addr_t. m_current_pc.SetLoadAddress(current_pc, &process->GetTarget()); @@ -203,6 +210,38 @@ UnwindPlan::RowSP active_row; lldb::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; + } + } + if (m_full_unwind_plan_sp && m_full_unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { active_row = @@ -294,8 +333,8 @@ // 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); + UnwindPlanSP lang_runtime_plan_sp = LanguageRuntime::GetRuntimeUnwindPlan( + m_thread, this, m_behaves_like_zeroth_frame); if (lang_runtime_plan_sp.get()) { UnwindLogMsg("This is an async frame"); } @@ -483,6 +522,8 @@ // so do not decrement and recompute if the symbol we already found is a trap // handler. decr_pc_and_recompute_addr_range = false; + } else if (m_behaves_like_zeroth_frame) { + decr_pc_and_recompute_addr_range = false; } else { // Decrement to find the function containing the call. decr_pc_and_recompute_addr_range = true; @@ -675,6 +716,14 @@ bool RegisterContextUnwind::IsFrameZero() const { return m_frame_number == 0; } +bool RegisterContextUnwind::BehavesLikeZerothFrame() const { + if (m_frame_number == 0) + return true; + if (m_behaves_like_zeroth_frame) + return true; + return false; +} + // Find a fast unwind plan for this frame, if possible. // // On entry to this method, @@ -745,10 +794,10 @@ "unable to get architectural default UnwindPlan from ABI plugin"); } - bool behaves_like_zeroth_frame = false; if (IsFrameZero() || GetNextFrame()->m_frame_type == eTrapHandlerFrame || - GetNextFrame()->m_frame_type == eDebuggerFrame) { - behaves_like_zeroth_frame = true; + GetNextFrame()->m_frame_type == eDebuggerFrame || + m_behaves_like_zeroth_frame) { + m_behaves_like_zeroth_frame = true; // If this frame behaves like a 0th frame (currently executing or // interrupted asynchronously), all registers can be retrieved. m_all_registers_available = true; @@ -764,7 +813,7 @@ if ((!m_sym_ctx_valid || (m_sym_ctx.function == nullptr && m_sym_ctx.symbol == nullptr)) && - behaves_like_zeroth_frame && m_current_pc.IsValid()) { + m_behaves_like_zeroth_frame && m_current_pc.IsValid()) { uint32_t permissions; addr_t current_pc_addr = m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()); @@ -892,7 +941,7 @@ // Typically the NonCallSite UnwindPlan is the unwind created by inspecting // the assembly language instructions - if (behaves_like_zeroth_frame && process) { + if (m_behaves_like_zeroth_frame && process) { unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite( process->GetTarget(), m_thread); if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress(m_current_pc)) { Index: lldb/source/Target/LanguageRuntime.cpp =================================================================== --- lldb/source/Target/LanguageRuntime.cpp +++ lldb/source/Target/LanguageRuntime.cpp @@ -259,14 +259,16 @@ return exc_breakpt_sp; } -UnwindPlanSP LanguageRuntime::GetRuntimeUnwindPlan(Thread &thread, - RegisterContext *regctx) { +UnwindPlanSP +LanguageRuntime::GetRuntimeUnwindPlan(Thread &thread, RegisterContext *regctx, + bool &behaves_like_zeroth_frame) { 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); + UnwindPlanSP plan_sp = runtime->GetRuntimeUnwindPlan( + process_sp, regctx, behaves_like_zeroth_frame); if (plan_sp.get()) return plan_sp; } Index: lldb/include/lldb/Target/RegisterContextUnwind.h =================================================================== --- lldb/include/lldb/Target/RegisterContextUnwind.h +++ lldb/include/lldb/Target/RegisterContextUnwind.h @@ -67,6 +67,11 @@ bool ReadPC(lldb::addr_t &start_pc); + // Indicates whether this frame *behaves* like frame zero -- the currently + // executing frame -- or not. This can be true in the middle of the stack + // above asynchronous trap handlers (sigtramp) for instance. + bool BehavesLikeZerothFrame() const override; + private: enum FrameType { eNormalFrame, @@ -228,14 +233,16 @@ // unknown // 0 if no instructions have been executed yet. - int m_current_offset_backed_up_one; // how far into the function we've - // executed; -1 if unknown // 0 if no instructions have been executed yet. // On architectures where the return address on the stack points // to the instruction after the CALL, this value will have 1 // subtracted from it. Else a function that ends in a CALL will // have an offset pointing into the next function's address range. // m_current_pc has the actual address of the "current" pc. + int m_current_offset_backed_up_one; // how far into the function we've + // executed; -1 if unknown + + bool m_behaves_like_zeroth_frame; // this frame behaves like frame zero lldb_private::SymbolContext &m_sym_ctx; bool m_sym_ctx_valid; // if ResolveSymbolContextForAddress fails, don't try to Index: lldb/include/lldb/Target/RegisterContext.h =================================================================== --- lldb/include/lldb/Target/RegisterContext.h +++ lldb/include/lldb/Target/RegisterContext.h @@ -180,6 +180,19 @@ lldb::RegisterKind target_rk, uint32_t &target_regnum); + /// Indicates that this frame is currently executing code, + /// that the PC value is not a return-pc but an actual executing + /// instruction. Some places in lldb will treat a return-pc + /// value differently than the currently-executing-pc value, + /// and this method can indicate if that should be done. + /// The base class implementation only uses the frame index, + /// but subclasses may have additional information that they + /// can use to detect frames in this state, for instance a + /// frame above a trap handler (sigtramp etc). + virtual bool BehavesLikeZerothFrame() const { + return m_concrete_frame_idx == 0; + } + // lldb::ExecutionContextScope pure virtual functions lldb::TargetSP CalculateTarget() override; Index: lldb/include/lldb/Target/LanguageRuntime.h =================================================================== --- lldb/include/lldb/Target/LanguageRuntime.h +++ lldb/include/lldb/Target/LanguageRuntime.h @@ -183,9 +183,29 @@ /// 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. + /// + /// \param[in] thread + /// The thread that the unwind is happening on. + /// + /// \param[in] regctx + /// The RegisterContext for the frame we need to create an UnwindPlan. + /// We don't yet have a StackFrame when we're selecting the UnwindPlan. + /// + /// \param[out] behaves_like_zeroth_frame + /// With normal ABI calls, all stack frames except the zeroth frame need + /// to have the return-pc value backed up by 1 for symbolication purposes. + /// For these LanguageRuntime unwind plans, they may not follow normal ABI + /// calling conventions and the return pc may need to be symbolicated + /// as-is. + /// + /// \return + /// Returns an UnwindPlan to find the caller frame if it should be used, + /// instead of the UnwindPlan that would normally be used for this + /// function. static lldb::UnwindPlanSP GetRuntimeUnwindPlan(lldb_private::Thread &thread, - lldb_private::RegisterContext *regctx); + lldb_private::RegisterContext *regctx, + bool &behaves_like_zeroth_frame); protected: // The static GetRuntimeUnwindPlan method above is only implemented in the @@ -193,7 +213,8 @@ // provide one of these UnwindPlans. virtual lldb::UnwindPlanSP GetRuntimeUnwindPlan(lldb::ProcessSP process_sp, - lldb_private::RegisterContext *regctx) { + lldb_private::RegisterContext *regctx, + bool &behaves_like_zeroth_frame) { return lldb::UnwindPlanSP(); }
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits