[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