[Lldb-commits] [lldb] r155236 - in /lldb/trunk: include/lldb/Breakpoint/BreakpointOptions.h include/lldb/Target/StopInfo.h source/Target/StopInfo.cpp source/Target/Thread.cpp source/Target/ThreadPlanStepRange.cpp test/functionalities/load_unload/TestLoadUnload.py

Jim Ingham jingham at apple.com
Fri Apr 20 14:16:56 PDT 2012


Author: jingham
Date: Fri Apr 20 16:16:56 2012
New Revision: 155236

URL: http://llvm.org/viewvc/llvm-project?rev=155236&view=rev
Log:
Make sure the "synchronous breakpoint callbacks" get called before the thread plan logic gets invoked, and if they
ask to continue that should short-circuit the thread plans for that thread.  Also add a bit more explanation for
how this machinery is supposed to work.  
Also pass eExecutionPolicyOnlyWhenNeeded, not eExecutionPolicyAlways when evaluating the expression for breakpoint
conditions.

Modified:
    lldb/trunk/include/lldb/Breakpoint/BreakpointOptions.h
    lldb/trunk/include/lldb/Target/StopInfo.h
    lldb/trunk/source/Target/StopInfo.cpp
    lldb/trunk/source/Target/Thread.cpp
    lldb/trunk/source/Target/ThreadPlanStepRange.cpp
    lldb/trunk/test/functionalities/load_unload/TestLoadUnload.py

Modified: lldb/trunk/include/lldb/Breakpoint/BreakpointOptions.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Breakpoint/BreakpointOptions.h?rev=155236&r1=155235&r2=155236&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Breakpoint/BreakpointOptions.h (original)
+++ lldb/trunk/include/lldb/Breakpoint/BreakpointOptions.h Fri Apr 20 16:16:56 2012
@@ -79,15 +79,91 @@
 
     //------------------------------------------------------------------
     // Callbacks
+    //
+    // Breakpoint callbacks come in two forms, synchronous and asynchronous.  Synchronous callbacks will get
+    // run before any of the thread plans are consulted, and if they return false the target will continue
+    // "under the radar" of the thread plans.  There are a couple of restrictions to synchronous callbacks:
+    // 1) They should NOT resume the target themselves.  Just return false if you want the target to restart.
+    // 2) Breakpoints with synchronous callbacks can't have conditions (or rather, they can have them, but they
+    //    won't do anything.  Ditto with ignore counts, etc...  You are supposed to control that all through the
+    //    callback.
+    // Asynchronous callbacks get run as part of the "ShouldStop" logic in the thread plan.  The logic there is:
+    //   a) If the breakpoint is thread specific and not for this thread, continue w/o running the callback.
+    //   b) If the ignore count says we shouldn't stop, then ditto.
+    //   c) If the condition says we shouldn't stop, then ditto.
+    //   d) Otherwise, the callback will get run, and if it returns true we will stop, and if false we won't.
+    //  The asynchronous callback can run the target itself, but at present that should be the last action the
+    //  callback does.  We will relax this condition at some point, but it will take a bit of plumbing to get
+    //  that to work.
+    // 
+    //------------------------------------------------------------------
+    
+    //------------------------------------------------------------------
+    /// Adds a callback to the breakpoint option set.
+    ///
+    /// @param[in] callback
+    ///    The function to be called when the breakpoint gets hit.
+    ///
+    /// @param[in] baton_sp
+    ///    A baton which will get passed back to the callback when it is invoked.
+    ///
+    /// @param[in] synchronous
+    ///    Whether this is a synchronous or asynchronous callback.  See discussion above.
     //------------------------------------------------------------------
     void SetCallback (BreakpointHitCallback callback, const lldb::BatonSP &baton_sp, bool synchronous = false);
+    
+    
+    //------------------------------------------------------------------
+    /// Remove the callback from this option set.
+    //------------------------------------------------------------------
+    void ClearCallback ();
+
+    // The rest of these functions are meant to be used only within the breakpoint handling mechanism.
+    
+    //------------------------------------------------------------------
+    /// Use this function to invoke the callback for a specific stop.
+    ///
+    /// @param[in] context
+    ///    The context in which the callback is to be invoked.  This includes the stop event, the
+    ///    execution context of the stop (since you might hit the same breakpoint on multiple threads) and
+    ///    whether we are currently executing synchronous or asynchronous callbacks.
+    /// 
+    /// @param[in] break_id
+    ///    The breakpoint ID that owns this option set.
+    ///
+    /// @param[in] break_loc_id
+    ///    The breakpoint location ID that owns this option set.
+    ///
+    /// @return
+    ///     The callback return value.
+    //------------------------------------------------------------------
     bool InvokeCallback (StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
+    
+    //------------------------------------------------------------------
+    /// Used in InvokeCallback to tell whether it is the right time to run this kind of callback.
+    ///
+    /// @param[in] condition
+    ///    The condition expression to evaluate when the breakpoint is hit.
+    //------------------------------------------------------------------
     bool IsCallbackSynchronous () {
         return m_callback_is_synchronous;
     }
+    
+    //------------------------------------------------------------------
+    /// Fetch the baton from the callback.
+    ///
+    /// @return
+    ///     The baton.
+    //------------------------------------------------------------------
     Baton *GetBaton ();
+    
+    //------------------------------------------------------------------
+    /// Fetch  a const version of the baton from the callback.
+    ///
+    /// @return
+    ///     The baton.
+    //------------------------------------------------------------------
     const Baton *GetBaton () const;
-    void ClearCallback ();
     
     //------------------------------------------------------------------
     // Condition

Modified: lldb/trunk/include/lldb/Target/StopInfo.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/StopInfo.h?rev=155236&r1=155235&r2=155236&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/StopInfo.h (original)
+++ lldb/trunk/include/lldb/Target/StopInfo.h Fri Apr 20 16:16:56 2012
@@ -74,12 +74,25 @@
     }
 
     // Stop the thread by default. Subclasses can override this to allow
-    // the thread to continue if desired.
+    // the thread to continue if desired.  The ShouldStop method should not do anything
+    // that might run code.  If you need to run code when deciding whether to stop
+    // at this StopInfo, that must be done in the PerformAction.  The PerformAction will
+    // always get called before the ShouldStop.
     virtual bool
     ShouldStop (Event *event_ptr)
     {
         return true;
     }
+    
+    // ShouldStopSynchronous will get called before any thread plans are consulted, and if it says we should
+    // resume the target, then we will just immediately resume.  This should not run any code in or resume the
+    // target.
+    
+    virtual bool
+    ShouldStopSynchronous (Event *event_ptr)
+    {
+        return true;
+    }
 
     // If should stop returns false, check if we should notify of this event
     virtual bool

Modified: lldb/trunk/source/Target/StopInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/StopInfo.cpp?rev=155236&r1=155235&r2=155236&view=diff
==============================================================================
--- lldb/trunk/source/Target/StopInfo.cpp (original)
+++ lldb/trunk/source/Target/StopInfo.cpp Fri Apr 20 16:16:56 2012
@@ -134,7 +134,7 @@
     }
 
     virtual bool
-    ShouldStop (Event *event_ptr)
+    ShouldStopSynchronous (Event *event_ptr)
     {
         if (!m_should_stop_is_valid)
         {
@@ -160,6 +160,15 @@
         return m_should_stop;
     }
     
+    bool
+    ShouldStop (Event *event_ptr)
+    {
+        // This just reports the work done by PerformAction or the synchronous stop.  It should
+        // only ever get called after they have had a chance to run.
+        assert (m_should_stop_is_valid);
+        return m_should_stop;
+    }
+    
     virtual void
     PerformAction (Event *event_ptr)
     {
@@ -216,7 +225,7 @@
                         const bool discard_on_error = true;
                         Error error;
                         result_code = ClangUserExpression::EvaluateWithError (exe_ctx,
-                                                                              eExecutionPolicyAlways,
+                                                                              eExecutionPolicyOnlyWhenNeeded,
                                                                               lldb::eLanguageTypeUnknown,
                                                                               ClangUserExpression::eResultTypeAny,
                                                                               discard_on_error,

Modified: lldb/trunk/source/Target/Thread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Thread.cpp?rev=155236&r1=155235&r2=155236&view=diff
==============================================================================
--- lldb/trunk/source/Target/Thread.cpp (original)
+++ lldb/trunk/source/Target/Thread.cpp Fri Apr 20 16:16:56 2012
@@ -333,6 +333,17 @@
     
     // The top most plan always gets to do the trace log...
     current_plan->DoTraceLog ();
+    
+    // First query the stop info's ShouldStopSynchronous.  This handles "synchronous" stop reasons, for example the breakpoint
+    // command on internal breakpoints.  If a synchronous stop reason says we should not stop, then we don't have to
+    // do any more work on this stop.
+    StopInfoSP private_stop_info (GetPrivateStopReason());
+    if (private_stop_info && private_stop_info->ShouldStopSynchronous(event_ptr) == false)
+    {
+        if (log)
+            log->Printf ("StopInfo::ShouldStop async callback says we should not stop, returning ShouldStop of false.");
+        return false;
+    }
 
     // If the base plan doesn't understand why we stopped, then we have to find a plan that does.
     // If that plan is still working, then we don't need to do any more work.  If the plan that explains 

Modified: lldb/trunk/source/Target/ThreadPlanStepRange.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/ThreadPlanStepRange.cpp?rev=155236&r1=155235&r2=155236&view=diff
==============================================================================
--- lldb/trunk/source/Target/ThreadPlanStepRange.cpp (original)
+++ lldb/trunk/source/Target/ThreadPlanStepRange.cpp Fri Apr 20 16:16:56 2012
@@ -368,6 +368,9 @@
         case eStopReasonBreakpoint:
             if (NextRangeBreakpointExplainsStop(stop_info_sp))
                 return true;
+            else
+                return false;
+            break;
         case eStopReasonWatchpoint:
         case eStopReasonSignal:
         case eStopReasonException:

Modified: lldb/trunk/test/functionalities/load_unload/TestLoadUnload.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/load_unload/TestLoadUnload.py?rev=155236&r1=155235&r2=155236&view=diff
==============================================================================
--- lldb/trunk/test/functionalities/load_unload/TestLoadUnload.py (original)
+++ lldb/trunk/test/functionalities/load_unload/TestLoadUnload.py Fri Apr 20 16:16:56 2012
@@ -222,6 +222,33 @@
         self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE,
             substrs = [' resolved, hit count = 2'])
 
+    def test_step_over_load (self):
+        """Test stepping over code that loads a shared library works correctly."""
+
+        # Invoke the default build rule.
+        self.buildDefault()
+
+        exe = os.path.join(os.getcwd(), "a.out")
+        self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+
+        # Break by function name a_function (not yet loaded).
+        self.expect("breakpoint set -f main.c -l %d"%(self.line), BREAKPOINT_CREATED,
+            substrs = ['Breakpoint created:',
+                      "file ='main.c', line = %d, locations = 1"%(self.line)])
+
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        # The stop reason of the thread should be breakpoint and at a_function.
+        self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+            substrs = ['stopped',
+                       'stop reason = breakpoint'])
+
+        self.runCmd("thread step-over", "Stepping over function that loads library")
+        
+        # The stop reason should be step end.
+        self.expect("thread list", "step over succeeded.", 
+            substrs = ['stopped',
+                      'stop reason = step over'])
 
 if __name__ == '__main__':
     import atexit





More information about the lldb-commits mailing list