[Lldb-commits] [lldb] r146723 - in /lldb/trunk/source/Plugins/Process/Utility: RegisterContextLLDB.cpp RegisterContextLLDB.h

Jason Molenda jmolenda at apple.com
Thu Dec 15 20:30:32 PST 2011


Author: jmolenda
Date: Thu Dec 15 22:30:31 2011
New Revision: 146723

URL: http://llvm.org/viewvc/llvm-project?rev=146723&view=rev
Log:
When we're unwinding out of frame 0 and we end up with a bogus frame
1 -- an address pointing off into non-executable memory -- don't
abort the unwind.  We'll use the ABI's default UnwindPlan to try
to get out of frame 1 and on many platforms with a standard frame
chain stack layout we can get back on track and get a valid frame
2.  This preserves the lldb behavior to-date; the change last week
to require the memory region to be executable broke it.

I'd like to mark this frame specially when displayed to the user;
I tried to override the places where the frame's pc value is returned
to change it to a sentinel value (e.g. LLDB_INVALID_ADDRESS) but
couldn't get that to work cleanly so I backed that part out for
now.  When this happens we'll often miss one of the user's actual
frames, the one that's of most interest to the user, so I'd like
to make this visually distinctive.

Note that a frame in non-executable memory region is only allowed
for frame 1.  After that we should be solid on the unwind and any
pc address in non-executable memory indicates a failure and we
should stop unwinding.


Modified:
    lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.cpp
    lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.h

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=146723&r1=146722&r2=146723&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.cpp Thu Dec 15 22:30:31 2011
@@ -203,6 +203,7 @@
 RegisterContextLLDB::InitializeNonZerothFrame()
 {
     LogSP log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_UNWIND));
+
     if (IsFrameZero ())
     {
         m_frame_type = eNotAValidFrame;
@@ -231,7 +232,8 @@
         m_frame_type = eNotAValidFrame;
         return;
     }
-    // A pc value of 0 up on the stack indicates we've hit the end of the stack
+
+    // A pc of 0x0 means it's the end of the stack crawl
     if (pc == 0)
     {
         m_frame_type = eNotAValidFrame;
@@ -239,13 +241,29 @@
     }
 
     // Test the pc value to see if we know it's in an unmapped/non-executable region of memory.
-    // If so, our unwind has made a mistake somewhere and we should stop.
     uint32_t permissions;
     if (m_thread.GetProcess().GetLoadAddressPermissions(pc, permissions)
         && (permissions & ePermissionsExecutable) == 0)
     {
-        m_frame_type = eNotAValidFrame;
-        return;
+        // If this is the second frame off the stack, we may have unwound the first frame
+        // incorrectly.  But using the architecture default unwind plan may get us back on
+        // track -- albeit possibly skipping a real frame.  Give this frame a clearly-invalid
+        // pc and see if we can get any further.
+        if (GetNextFrame().get() && GetNextFrame()->IsValid() && GetNextFrame()->IsFrameZero())
+        {
+            if (log)
+            {
+                log->Printf("%*sFrame %u had a pc of 0x%llx which is not in executable memory but on frame 1 -- allowing it once.",
+                            m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number, (uint64_t) pc);
+            }
+            m_frame_type = eSkipFrame;
+        }
+        else
+        {
+            // anywhere other than the second frame, a non-executable pc means we're off in the weeds -- stop now.
+            m_frame_type = eNotAValidFrame;
+            return;
+        }
     }
 
     m_thread.GetProcess().GetTarget().GetSectionLoadList().ResolveLoadAddress (pc, m_current_pc);
@@ -265,7 +283,10 @@
             m_fast_unwind_plan_sp.reset ();
             m_full_unwind_plan_sp.reset (new UnwindPlan (lldb::eRegisterKindGeneric));
             abi->CreateDefaultUnwindPlan(*m_full_unwind_plan_sp);
-            m_frame_type = eNormalFrame;
+            if (m_frame_type != eSkipFrame)  // don't override eSkipFrame
+            {
+                m_frame_type = eNormalFrame;
+            }
             m_all_registers_available = false;
             m_current_offset = -1;
             m_current_offset_backed_up_one = -1;
@@ -283,7 +304,10 @@
                         log->Printf("%*sFrame %u failed to get cfa value",
                                     m_frame_number < 100 ? m_frame_number : 100, "", m_frame_number);
                     }
-                    m_frame_type = eNormalFrame;
+                    if (m_frame_type != eSkipFrame)   // don't override eSkipFrame
+                    {
+                        m_frame_type = eNormalFrame;
+                    }
                     return;
                 }
                 m_cfa = cfa_regval + cfa_offset;
@@ -408,7 +432,10 @@
     else
     {
         // FIXME:  Detect eDebuggerFrame here.
-        m_frame_type = eNormalFrame;
+        if (m_frame_type != eSkipFrame) // don't override eSkipFrame
+        {
+            m_frame_type = eNormalFrame;
+        }
     }
 
     // We've set m_frame_type and m_sym_ctx before this call.
@@ -869,6 +896,18 @@
     return m_frame_type != eNotAValidFrame;
 }
 
+// A skip frame is a bogus frame on the stack -- but one where we're likely to find a real frame farther
+// up the stack if we keep looking.  It's always the second frame in an unwind (i.e. the first frame after
+// frame zero) where unwinding can be the trickiest.  Ideally we'll mark up this frame in some way so the
+// user knows we're displaying bad data and we may have skipped one frame of their real program in the 
+// process of getting back on track.
+
+bool
+RegisterContextLLDB::IsSkipFrame () const
+{
+    return m_frame_type == eSkipFrame;
+}
+
 // Answer the question: Where did THIS frame save the CALLER frame ("previous" frame)'s register value?
 
 bool
@@ -1312,6 +1351,13 @@
     return m_parent_unwind.GetRegisterContextForFrameNum (m_frame_number - 1);
 }
 
+RegisterContextLLDB::SharedPtr
+RegisterContextLLDB::GetPrevFrame () const
+{
+    RegisterContextLLDB::SharedPtr regctx;
+    return m_parent_unwind.GetRegisterContextForFrameNum (m_frame_number + 1);
+}
+
 // Retrieve the address of the start of the function of THIS frame
 
 bool
@@ -1319,6 +1365,7 @@
 {
     if (!IsValid())
         return false;
+
     if (!m_start_pc.IsValid())
     {
         return ReadPC (start_pc); 
@@ -1334,6 +1381,7 @@
 {
     if (!IsValid())
         return false;
+
     if (ReadGPRValue (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, pc))
     {
         // A pc value of 0 or 1 is impossible in the middle of the stack -- it indicates the end of a stack walk.
@@ -1356,4 +1404,3 @@
         return false;
     }
 }
-

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=146723&r1=146722&r2=146723&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.h (original)
+++ lldb/trunk/source/Plugins/Process/Utility/RegisterContextLLDB.h Thu Dec 15 22:30:31 2011
@@ -88,6 +88,7 @@
         eNormalFrame,
         eSigtrampFrame,
         eDebuggerFrame,  // a debugger inferior function call frame; we get caller's registers from debugger
+        eSkipFrame,      // The unwind resulted in a bogus frame but may get back on track so we don't want to give up yet
         eNotAValidFrame  // this frame is invalid for some reason - most likely it is past the top (end) of the stack
     };
 
@@ -108,6 +109,17 @@
     SharedPtr
     GetNextFrame () const;
 
+    SharedPtr
+    GetPrevFrame () const;
+
+    // A SkipFrame occurs when the unwind out of frame 0 didn't go right -- we've got one bogus frame at frame #1.  
+    // There is a good chance we'll get back on track if we follow the frame pointer chain (or whatever is appropriate
+    // on this ABI) so we allow one invalid frame to be in the stack.  Ideally we'll mark this frame specially at some
+    // point and indicate to the user that the unwinder had a hiccup.  Often when this happens we will miss a frame of
+    // the program's actual stack in the unwind and we want to flag that for the user somehow.
+    bool
+    IsSkipFrame () const;
+
     // Provide a location for where THIS function saved the CALLER's register value
     // Or a frame "below" this one saved it, i.e. a function called by this one, preserved a register that this
     // function didn't modify/use.





More information about the lldb-commits mailing list