[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