[Lldb-commits] [lldb] r201839 - Add a new idea of a "fallback" UnwindPlan to the RegisterContextLLDB
Jason Molenda
jmolenda at apple.com
Thu Feb 20 21:20:25 PST 2014
Author: jmolenda
Date: Thu Feb 20 23:20:25 2014
New Revision: 201839
URL: http://llvm.org/viewvc/llvm-project?rev=201839&view=rev
Log:
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>
Modified:
lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.h
lldb/trunk/source/Plugins/Process/Utility/UnwindLLDB.cpp
Modified: lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.cpp?rev=201839&r1=201838&r2=201839&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.cpp Thu Feb 20 23:20:25 2014
@@ -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::GetFullUnwindPlanFo
{
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::GetFullUnwindPlanFo
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::GetFullUnwindPlanFo
// 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::SavedLocationForReg
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::SavedLocationForReg
}
// 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)
+ if (m_fallback_unwind_plan_sp.get() == NULL)
+ return false;
+
+ 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)
{
- 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)
+ FuncUnwindersSP func_unwinders_sp;
+ if (m_sym_ctx_valid && m_current_pc.IsValid() && m_current_pc.GetModule())
{
- UnwindPlan::RowSP active_row = arch_default_unwind_plan_sp->GetRowForFunctionOffset (m_current_offset);
-
- if (active_row && active_row->GetCFARegister() != LLDB_INVALID_REGNUM)
+ func_unwinders_sp = m_current_pc.GetModule()->GetObjectFile()->GetUnwindTable().GetFuncUnwindersContainingAddress (m_current_pc, m_sym_ctx);
+ if (func_unwinders_sp)
{
- 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 ();
- }
-
- 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());
+ 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
Modified: lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.h?rev=201839&r1=201838&r2=201839&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.h (original)
+++ lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.h Thu Feb 20 23:20:25 2014
@@ -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
Modified: lldb/trunk/source/Plugins/Process/Utility/UnwindLLDB.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Utility/UnwindLLDB.cpp?rev=201839&r1=201838&r2=201839&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Utility/UnwindLLDB.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Utility/UnwindLLDB.cpp Thu Feb 20 23:20:25 2014
@@ -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",
More information about the lldb-commits
mailing list