mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-11 02:16:50 +00:00
Add a new idea of a "fallback" UnwindPlan to the RegisterContextLLDB
class. If we try to unwind a stack frame to find a caller stack frame, and we fail to get a valid-looking frame, AND if the UnwindPlan we used is an assembly-inspection based UnwindPlan, then we should throw away the assembly-inspection UnwindPlan and try unwinding with the architectural default UnwindPlan. This code path won't be taken if eh_frame unwind instructions are available - lldb will always prefer those once it's off the zeroth frame. The problem I'm trying to fix here is the class of unwind failures that happen when we have hand-written assembly on the stack, with no eh_frame, and lldb's assembly parser fails to understand the assembly. People usually write their hand-written assembly to follow the frame-pointer-preserving conventions of the platform so the architectural default UnwindPlan will often work. We won't have the spill location for most of the non-volatile registers if we fall back to this, but it's better than stopping the unwind prematurely. This is a bit of a tricky change that I believe is correct, but if we get unwinds that go of into the weeds / unwind bogus frames at the end of the stack, I'll need to revisit it. <rdar://problem/16099440> llvm-svn: 201839
This commit is contained in:
parent
d57124455b
commit
31d7ad4ecf
@ -50,6 +50,7 @@ RegisterContextLLDB::RegisterContextLLDB
|
||||
m_thread(thread),
|
||||
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),
|
||||
@ -764,8 +765,10 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame ()
|
||||
{
|
||||
m_fast_unwind_plan_sp.reset();
|
||||
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtCallSite (m_current_offset_backed_up_one);
|
||||
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
||||
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc) && unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolYes)
|
||||
{
|
||||
return unwind_plan_sp;
|
||||
}
|
||||
}
|
||||
|
||||
// Ask the DynamicLoader if the eh_frame CFI should be trusted in this frame even when it's frame zero
|
||||
@ -791,6 +794,15 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame ()
|
||||
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread);
|
||||
if (unwind_plan_sp && unwind_plan_sp->PlanValidAtAddress (m_current_pc))
|
||||
{
|
||||
if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo)
|
||||
{
|
||||
// We probably have an UnwindPlan created by inspecting assembly instructions, and we probably
|
||||
// don't have any eh_frame instructions available.
|
||||
// The assembly profilers work really well with compiler-generated functions but hand-written
|
||||
// assembly can be problematic. We'll set the architecture default UnwindPlan as our fallback
|
||||
// UnwindPlan in case this doesn't work out when we try to unwind.
|
||||
m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
|
||||
}
|
||||
UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString());
|
||||
return unwind_plan_sp;
|
||||
}
|
||||
@ -808,6 +820,16 @@ RegisterContextLLDB::GetFullUnwindPlanForFrame ()
|
||||
// We'd prefer to use an UnwindPlan intended for call sites when we're at a call site but if we've
|
||||
// struck out on that, fall back to using the non-call-site assembly inspection UnwindPlan if possible.
|
||||
unwind_plan_sp = func_unwinders_sp->GetUnwindPlanAtNonCallSite (m_thread);
|
||||
if (unwind_plan_sp->GetSourcedFromCompiler() == eLazyBoolNo)
|
||||
{
|
||||
// We probably have an UnwindPlan created by inspecting assembly instructions, and we probably
|
||||
// don't have any eh_frame instructions available.
|
||||
// The assembly profilers work really well with compiler-generated functions but hand-written
|
||||
// assembly can be problematic. We'll set the architecture default UnwindPlan as our fallback
|
||||
// UnwindPlan in case this doesn't work out when we try to unwind.
|
||||
m_fallback_unwind_plan_sp = arch_default_unwind_plan_sp;
|
||||
}
|
||||
|
||||
if (IsUnwindPlanValidForCurrentPC(unwind_plan_sp, valid_offset))
|
||||
{
|
||||
UnwindLogMsgVerbose ("frame uses %s for full UnwindPlan", unwind_plan_sp->GetSourceName().GetCString());
|
||||
@ -1176,21 +1198,22 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat
|
||||
m_full_unwind_plan_sp->GetSourceName().GetCString());
|
||||
|
||||
// Throw away the full unwindplan; install the arch default unwindplan
|
||||
InvalidateFullUnwindPlan();
|
||||
|
||||
// Now re-fetch the pc value we're searching for
|
||||
uint32_t arch_default_pc_reg = LLDB_INVALID_REGNUM;
|
||||
UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
||||
if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, m_full_unwind_plan_sp->GetRegisterKind(), arch_default_pc_reg)
|
||||
&& arch_default_pc_reg != LLDB_INVALID_REGNUM
|
||||
&& active_row
|
||||
&& active_row->GetRegisterInfo (arch_default_pc_reg, unwindplan_regloc))
|
||||
if (TryFallbackUnwindPlan())
|
||||
{
|
||||
have_unwindplan_regloc = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
have_unwindplan_regloc = false;
|
||||
// Now re-fetch the pc value we're searching for
|
||||
uint32_t arch_default_pc_reg = LLDB_INVALID_REGNUM;
|
||||
UnwindPlan::RowSP active_row = m_full_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
||||
if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, m_full_unwind_plan_sp->GetRegisterKind(), arch_default_pc_reg)
|
||||
&& arch_default_pc_reg != LLDB_INVALID_REGNUM
|
||||
&& active_row
|
||||
&& active_row->GetRegisterInfo (arch_default_pc_reg, unwindplan_regloc))
|
||||
{
|
||||
have_unwindplan_regloc = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
have_unwindplan_regloc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1333,54 +1356,48 @@ RegisterContextLLDB::SavedLocationForRegister (uint32_t lldb_regnum, lldb_privat
|
||||
}
|
||||
|
||||
// If the Full unwindplan has been determined to be incorrect, this method will
|
||||
// replace it with the architecture's default unwindplna, if one is defined.
|
||||
// replace it with the architecture's default unwindplan, if one is defined.
|
||||
// It will also find the FuncUnwinders object for this function and replace the
|
||||
// Full unwind method for the function there so we don't use the errant Full unwindplan
|
||||
// again in the future of this debug session.
|
||||
// We're most likely doing this because the Full unwindplan was generated by assembly
|
||||
// instruction profiling and the profiler got something wrong.
|
||||
|
||||
void
|
||||
RegisterContextLLDB::InvalidateFullUnwindPlan ()
|
||||
bool
|
||||
RegisterContextLLDB::TryFallbackUnwindPlan ()
|
||||
{
|
||||
UnwindPlan::Row::RegisterLocation unwindplan_regloc;
|
||||
ExecutionContext exe_ctx (m_thread.shared_from_this());
|
||||
Process *process = exe_ctx.GetProcessPtr();
|
||||
ABI *abi = process ? process->GetABI().get() : NULL;
|
||||
if (abi)
|
||||
{
|
||||
UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp;
|
||||
UnwindPlanSP arch_default_unwind_plan_sp;
|
||||
arch_default_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
|
||||
abi->CreateDefaultUnwindPlan(*arch_default_unwind_plan_sp);
|
||||
if (arch_default_unwind_plan_sp)
|
||||
{
|
||||
UnwindPlan::RowSP active_row = arch_default_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
||||
|
||||
if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM)
|
||||
{
|
||||
FuncUnwindersSP func_unwinders_sp;
|
||||
if (m_sym_ctx_valid && m_current_pc.IsValid() && m_current_pc.GetModule())
|
||||
{
|
||||
func_unwinders_sp = m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
|
||||
if (func_unwinders_sp)
|
||||
{
|
||||
func_unwinders_sp->InvalidateNonCallSiteUnwindPlan (m_thread);
|
||||
}
|
||||
}
|
||||
m_registers.clear();
|
||||
m_full_unwind_plan_sp = arch_default_unwind_plan_sp;
|
||||
addr_t cfa_regval = LLDB_INVALID_ADDRESS;
|
||||
if (ReadGPRValue (arch_default_unwind_plan_sp->GetRegisterKind(), active_row->GetCFARegister(), cfa_regval))
|
||||
{
|
||||
m_cfa = cfa_regval + active_row->GetCFAOffset ();
|
||||
}
|
||||
if (m_fallback_unwind_plan_sp.get() == NULL)
|
||||
return false;
|
||||
|
||||
UnwindLogMsg ("full unwind plan '%s' has been replaced by architecture default unwind plan '%s' for this function from now on.",
|
||||
original_full_unwind_plan_sp->GetSourceName().GetCString(), arch_default_unwind_plan_sp->GetSourceName().GetCString());
|
||||
UnwindPlanSP original_full_unwind_plan_sp = m_full_unwind_plan_sp;
|
||||
UnwindPlan::RowSP active_row = m_fallback_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
|
||||
|
||||
if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM)
|
||||
{
|
||||
FuncUnwindersSP func_unwinders_sp;
|
||||
if (m_sym_ctx_valid && m_current_pc.IsValid() && m_current_pc.GetModule())
|
||||
{
|
||||
func_unwinders_sp = m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
|
||||
if (func_unwinders_sp)
|
||||
{
|
||||
func_unwinders_sp->InvalidateNonCallSiteUnwindPlan (m_thread);
|
||||
}
|
||||
}
|
||||
m_registers.clear();
|
||||
m_full_unwind_plan_sp = m_fallback_unwind_plan_sp;
|
||||
addr_t cfa_regval = LLDB_INVALID_ADDRESS;
|
||||
if (ReadGPRValue (m_fallback_unwind_plan_sp->GetRegisterKind(), active_row->GetCFARegister(), cfa_regval))
|
||||
{
|
||||
m_cfa = cfa_regval + active_row->GetCFAOffset ();
|
||||
}
|
||||
|
||||
UnwindLogMsg ("full unwind plan '%s' has been replaced by architecture default unwind plan '%s' for this function from now on.",
|
||||
original_full_unwind_plan_sp->GetSourceName().GetCString(), m_fallback_unwind_plan_sp->GetSourceName().GetCString());
|
||||
m_fallback_unwind_plan_sp.reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retrieve a general purpose register value for THIS frame, as saved by the NEXT frame, i.e. the frame that
|
||||
|
@ -160,8 +160,20 @@ private:
|
||||
const lldb_private::RegisterInfo *reg_info,
|
||||
const lldb_private::RegisterValue &value);
|
||||
|
||||
void
|
||||
InvalidateFullUnwindPlan ();
|
||||
//------------------------------------------------------------------
|
||||
/// If the unwind has to the caller frame has failed, try something else
|
||||
///
|
||||
/// If lldb is using an assembly language based UnwindPlan for a frame and
|
||||
/// the unwind to the caller frame fails, try falling back to a generic
|
||||
/// UnwindPlan (architecture default unwindplan) to see if that might work
|
||||
/// better. This is mostly helping to work around problems where the
|
||||
/// assembly language inspection fails on hand-written assembly code.
|
||||
///
|
||||
/// @return
|
||||
/// Returns true if a fallback unwindplan was found & was installed.
|
||||
//------------------------------------------------------------------
|
||||
bool
|
||||
TryFallbackUnwindPlan ();
|
||||
|
||||
// Get the contents of a general purpose (address-size) register for this frame
|
||||
// (usually retrieved from the next frame)
|
||||
@ -191,8 +203,10 @@ private:
|
||||
// i.e. where THIS frame saved them
|
||||
///
|
||||
|
||||
lldb::UnwindPlanSP m_fast_unwind_plan_sp; // may be NULL
|
||||
lldb::UnwindPlanSP m_fast_unwind_plan_sp; // may be NULL
|
||||
lldb::UnwindPlanSP m_full_unwind_plan_sp;
|
||||
lldb::UnwindPlanSP m_fallback_unwind_plan_sp; // may be NULL
|
||||
|
||||
bool m_all_registers_available; // Can we retrieve all regs or just nonvolatile regs?
|
||||
int m_frame_type; // enum FrameType
|
||||
|
||||
|
@ -158,6 +158,12 @@ UnwindLLDB::AddOneMoreFrame (ABI *abi)
|
||||
|
||||
if (reg_ctx_sp.get() == NULL)
|
||||
{
|
||||
// If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to that and return
|
||||
// true. Subsequent calls to TryFallbackUnwindPlan() will return false.
|
||||
if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan())
|
||||
{
|
||||
return AddOneMoreFrame (abi);
|
||||
}
|
||||
if (log)
|
||||
log->Printf ("%*sFrame %d did not get a RegisterContext, stopping.",
|
||||
cur_idx < 100 ? cur_idx : 100, "", cur_idx);
|
||||
@ -166,6 +172,12 @@ UnwindLLDB::AddOneMoreFrame (ABI *abi)
|
||||
|
||||
if (!reg_ctx_sp->IsValid())
|
||||
{
|
||||
// If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to that and return
|
||||
// true. Subsequent calls to TryFallbackUnwindPlan() will return false.
|
||||
if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan())
|
||||
{
|
||||
return AddOneMoreFrame (abi);
|
||||
}
|
||||
if (log)
|
||||
{
|
||||
log->Printf("%*sFrame %d invalid RegisterContext for this frame, stopping stack walk",
|
||||
@ -175,6 +187,12 @@ UnwindLLDB::AddOneMoreFrame (ABI *abi)
|
||||
}
|
||||
if (!reg_ctx_sp->GetCFA (cursor_sp->cfa))
|
||||
{
|
||||
// If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to that and return
|
||||
// true. Subsequent calls to TryFallbackUnwindPlan() will return false.
|
||||
if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan())
|
||||
{
|
||||
return AddOneMoreFrame (abi);
|
||||
}
|
||||
if (log)
|
||||
{
|
||||
log->Printf("%*sFrame %d did not get CFA for this frame, stopping stack walk",
|
||||
@ -189,6 +207,12 @@ UnwindLLDB::AddOneMoreFrame (ABI *abi)
|
||||
// these.
|
||||
if (reg_ctx_sp->IsTrapHandlerFrame() == false)
|
||||
{
|
||||
// If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to that and return
|
||||
// true. Subsequent calls to TryFallbackUnwindPlan() will return false.
|
||||
if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan())
|
||||
{
|
||||
return AddOneMoreFrame (abi);
|
||||
}
|
||||
if (log)
|
||||
{
|
||||
log->Printf("%*sFrame %d did not get a valid CFA for this frame, stopping stack walk",
|
||||
@ -199,6 +223,12 @@ UnwindLLDB::AddOneMoreFrame (ABI *abi)
|
||||
}
|
||||
if (!reg_ctx_sp->ReadPC (cursor_sp->start_pc))
|
||||
{
|
||||
// If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to that and return
|
||||
// true. Subsequent calls to TryFallbackUnwindPlan() will return false.
|
||||
if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan())
|
||||
{
|
||||
return AddOneMoreFrame (abi);
|
||||
}
|
||||
if (log)
|
||||
{
|
||||
log->Printf("%*sFrame %d did not get PC for this frame, stopping stack walk",
|
||||
@ -208,6 +238,12 @@ UnwindLLDB::AddOneMoreFrame (ABI *abi)
|
||||
}
|
||||
if (abi && !abi->CodeAddressIsValid (cursor_sp->start_pc))
|
||||
{
|
||||
// If the RegisterContextLLDB has a fallback UnwindPlan, it will switch to that and return
|
||||
// true. Subsequent calls to TryFallbackUnwindPlan() will return false.
|
||||
if (m_frames[cur_idx - 1]->reg_ctx_lldb_sp->TryFallbackUnwindPlan())
|
||||
{
|
||||
return AddOneMoreFrame (abi);
|
||||
}
|
||||
if (log)
|
||||
{
|
||||
log->Printf("%*sFrame %d did not get a valid PC, stopping stack walk",
|
||||
|
Loading…
x
Reference in New Issue
Block a user