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 @@ -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(); } diff --git a/lldb/include/lldb/Target/RegisterContext.h b/lldb/include/lldb/Target/RegisterContext.h --- a/lldb/include/lldb/Target/RegisterContext.h +++ b/lldb/include/lldb/Target/RegisterContext.h @@ -148,6 +148,31 @@ uint64_t GetPC(uint64_t fail_value = LLDB_INVALID_ADDRESS); + /// Get an address suitable for symbolication. + /// When symbolicating -- computing line, block, function -- + /// for a function in the middle of the stack, using the return + /// address can lead to unexpected results for the user. + /// A function that ends in a tail-call may have another function + /// as the "return" address, but it will never actually return. + /// Or a noreturn call in the middle of a function is the end of + /// a block of instructions, and a DWARF location list entry for + /// the return address may be a very different code path with + /// incorrect results when printing variables for this frame. + /// + /// At a source line view, the user expects the current-line indictation + /// to point to the function call they're under, not the next source line. + /// + /// The return address (GetPC()) should always be shown to the user, + /// but when computing context, keeping within the bounds of the + /// call instruction is what the user expects to see. + /// + /// \param [out] address + /// An Address object that will be filled in, if a PC can be retrieved. + /// + /// \return + /// Returns true if the Address param was filled in. + bool GetPCForSymbolication(Address &address); + bool SetPC(uint64_t pc); bool SetPC(Address addr); @@ -196,6 +221,19 @@ void SetStopID(uint32_t stop_id) { m_stop_id = stop_id; } protected: + /// 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; + } + // Classes that inherit from RegisterContext can see and modify these Thread &m_thread; // The thread that this register context belongs to. uint32_t m_concrete_frame_idx; // The concrete frame index for this register diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/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 diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -134,6 +134,24 @@ /// The Address object set to the current PC value. const Address &GetFrameCodeAddress(); + /// Get the current code Address suitable for symbolication, + /// may not be the same as GetFrameCodeAddress(). + /// + /// For a frame in the middle of the stack, the return-pc is the + /// current code address, but for symbolication purposes the + /// return address after a noreturn call may point to the next + /// function, a DWARF location list entry that is a completely + /// different code path, or the wrong source line. + /// + /// The address returned should be used for symbolication (source line, + /// block, function, DWARF location entry selection) but should NOT + /// be shown to the user. It may not point to an actual instruction + /// boundary. + /// + /// \return + /// The Address object set to the current PC value. + Address GetFrameCodeAddressForSymbolication(); + /// Change the pc value for a given thread. /// /// Change the current pc value for the frame on this thread. 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,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; } diff --git a/lldb/source/Target/RegisterContext.cpp b/lldb/source/Target/RegisterContext.cpp --- a/lldb/source/Target/RegisterContext.cpp +++ b/lldb/source/Target/RegisterContext.cpp @@ -150,6 +150,22 @@ return success; } +bool RegisterContext::GetPCForSymbolication(Address &address) { + addr_t pc = GetPC(LLDB_INVALID_ADDRESS); + if (pc == LLDB_INVALID_ADDRESS) + return false; + TargetSP target_sp = m_thread.CalculateTarget(); + if (!target_sp.get()) + return false; + + if (!BehavesLikeZerothFrame()) + pc--; + const bool allow_section_end = true; + address.SetOpcodeLoadAddress(pc, target_sp.get(), AddressClass::eCode, + allow_section_end); + return true; +} + bool RegisterContext::SetPC(Address addr) { TargetSP target_sp = m_thread.CalculateTarget(); Target *target = target_sp.get(); 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 @@ -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,9 @@ "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; + 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 +812,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 +940,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)) { diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -213,6 +213,31 @@ return m_frame_code_addr; } +Address StackFrame::GetFrameCodeAddressForSymbolication() { + Address lookup_addr(GetFrameCodeAddress()); + if (!lookup_addr.IsValid()) + return lookup_addr; + if (m_behaves_like_zeroth_frame) + return lookup_addr; + + addr_t offset = lookup_addr.GetOffset(); + if (offset > 0) { + lookup_addr.SetOffset(offset - 1); + } else { + // lookup_addr is the start of a section. We need do the math on the + // actual load address and re-compute the section. We're working with + // a 'noreturn' function at the end of a section. + TargetSP target_sp = CalculateTarget(); + if (target_sp) { + addr_t addr_minus_one = lookup_addr.GetOpcodeLoadAddress( + target_sp.get(), AddressClass::eCode) - + 1; + lookup_addr.SetOpcodeLoadAddress(addr_minus_one, target_sp.get()); + } + } + return lookup_addr; +} + bool StackFrame::ChangePC(addr_t pc) { std::lock_guard guard(m_mutex); // We can't change the pc value of a history stack frame - it is immutable. @@ -288,30 +313,7 @@ // If this is not frame zero, then we need to subtract 1 from the PC value // when doing address lookups since the PC will be on the instruction // following the function call instruction... - - Address lookup_addr(GetFrameCodeAddress()); - if (!m_behaves_like_zeroth_frame && lookup_addr.IsValid()) { - addr_t offset = lookup_addr.GetOffset(); - if (offset > 0) { - lookup_addr.SetOffset(offset - 1); - - } else { - // lookup_addr is the start of a section. We need do the math on the - // actual load address and re-compute the section. We're working with - // a 'noreturn' function at the end of a section. - ThreadSP thread_sp(GetThread()); - if (thread_sp) { - TargetSP target_sp(thread_sp->CalculateTarget()); - if (target_sp) { - addr_t addr_minus_one = - lookup_addr.GetLoadAddress(target_sp.get()) - 1; - lookup_addr.SetLoadAddress(addr_minus_one, target_sp.get()); - } else { - lookup_addr.SetOffset(offset - 1); - } - } - } - } + Address lookup_addr(GetFrameCodeAddressForSymbolication()); if (m_sc.module_sp) { // We have something in our stack frame symbol context, lets check if we diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -522,27 +522,10 @@ SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext( eSymbolContextBlock | eSymbolContextFunction); Block *unwind_block = unwind_sc.block; + TargetSP target_sp = m_thread.CalculateTarget(); if (unwind_block) { - Address curr_frame_address(unwind_frame_sp->GetFrameCodeAddress()); - TargetSP target_sp = m_thread.CalculateTarget(); - // Be sure to adjust the frame address to match the address that was - // 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 (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 - // case resolve the address again to the correct section plus - // offset form. - addr_t load_addr = curr_frame_address.GetOpcodeLoadAddress( - target_sp.get(), AddressClass::eCode); - curr_frame_address.SetOpcodeLoadAddress( - load_addr - 1, target_sp.get(), AddressClass::eCode); - } else { - curr_frame_address.Slide(-1); - } - } + Address curr_frame_address( + unwind_frame_sp->GetFrameCodeAddressForSymbolication()); SymbolContext next_frame_sc; Address next_frame_address; diff --git a/lldb/source/Target/UnwindLLDB.cpp b/lldb/source/Target/UnwindLLDB.cpp --- a/lldb/source/Target/UnwindLLDB.cpp +++ b/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; }