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:
Jason Molenda 2014-02-21 05:20:25 +00:00
parent d57124455b
commit 31d7ad4ecf
3 changed files with 121 additions and 54 deletions

View File

@ -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

View File

@ -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

View File

@ -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",