/// \b true if the target should stop at this breakpoint and \b
/// false not.
bool InvokeCallback(StoppointCallbackContext *context);
+
+ /// Report whether the callback for this location is synchronous or not.
+ ///
+ /// \return
+ /// \b true if the callback is synchronous and \b false if not.
+ bool IsCallbackSynchronous();
/// Returns whether we should resolve Indirect functions in setting the
/// breakpoint site for this location.
return m_owner.InvokeCallback(context, GetID());
}
+bool BreakpointLocation::IsCallbackSynchronous() {
+ if (m_options_up != nullptr && m_options_up->HasCallback())
+ return m_options_up->IsCallbackSynchronous();
+ else
+ return m_owner.GetOptions()->IsCallbackSynchronous();
+}
+
void BreakpointLocation::SetCallback(BreakpointHitCallback callback,
void *baton, bool is_synchronous) {
// The default "Baton" class will keep a copy of "baton" and won't free or
: nullptr,
context, break_id, break_loc_id);
} else if (IsCallbackSynchronous()) {
- // If a synchronous callback is called at async time, it should not say
- // to stop.
return false;
}
}
// location said we should stop. But that's better than not running
// all the callbacks.
+ // There's one other complication here. We may have run an async
+ // breakpoint callback that said we should stop. We only want to
+ // override that if another breakpoint action says we shouldn't
+ // stop. If nobody else has an opinion, then we should stop if the
+ // async callback says we should. An example of this is the async
+ // shared library load notification breakpoint and the setting
+ // stop-on-sharedlibrary-events.
+ // We'll keep the async value in async_should_stop, and track whether
+ // anyone said we should NOT stop in actually_said_continue.
+ bool async_should_stop = false;
+ if (m_should_stop_is_valid)
+ async_should_stop = m_should_stop;
+ bool actually_said_continue = false;
+
m_should_stop = false;
// We don't select threads as we go through them testing breakpoint
bool precondition_result =
bp_loc_sp->GetBreakpoint().EvaluatePrecondition(context);
- if (!precondition_result)
+ if (!precondition_result) {
+ actually_said_continue = true;
continue;
-
+ }
// Next run the condition for the breakpoint. If that says we
// should stop, then we'll run the callback for the breakpoint. If
// the callback says we shouldn't stop that will win.
// the condition fails. We've already bumped it by the time
// we get here, so undo the bump:
bp_loc_sp->UndoBumpHitCount();
+ actually_said_continue = true;
continue;
}
}
// When we figure out how to nest breakpoint hits then this will
// change.
- Debugger &debugger = thread_sp->CalculateTarget()->GetDebugger();
- bool old_async = debugger.GetAsyncExecution();
- debugger.SetAsyncExecution(true);
+ // Don't run async callbacks in PerformAction. They have already
+ // been taken into account with async_should_stop.
+ if (!bp_loc_sp->IsCallbackSynchronous()) {
+ Debugger &debugger = thread_sp->CalculateTarget()->GetDebugger();
+ bool old_async = debugger.GetAsyncExecution();
+ debugger.SetAsyncExecution(true);
- callback_says_stop = bp_loc_sp->InvokeCallback(&context);
+ callback_says_stop = bp_loc_sp->InvokeCallback(&context);
- debugger.SetAsyncExecution(old_async);
+ debugger.SetAsyncExecution(old_async);
- if (callback_says_stop && auto_continue_says_stop)
- m_should_stop = true;
+ if (callback_says_stop && auto_continue_says_stop)
+ m_should_stop = true;
+ else
+ actually_said_continue = true;
+ }
// If we are going to stop for this breakpoint, then remove the
// breakpoint.
// here.
if (HasTargetRunSinceMe()) {
m_should_stop = false;
+ actually_said_continue = true;
break;
}
}
+ // At this point if nobody actually told us to continue, we should
+ // give the async breakpoint callback a chance to weigh in:
+ if (!actually_said_continue && !m_should_stop) {
+ m_should_stop = async_should_stop;
+ }
}
// We've figured out what this stop wants to do, so mark it as valid so
// we don't compute it again.
--- /dev/null
+CXX_SOURCES := main.cpp
+USE_LIBDL := 1
+
+a.out: lib_a lib_b
+
+include Makefile.rules
+
+lib_a:
+ $(MAKE) -f $(MAKEFILE_RULES) \
+ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=a.cpp DYLIB_NAME=load_a
+
+lib_b:
+ $(MAKE) -f $(MAKEFILE_RULES) \
+ DYLIB_ONLY=YES DYLIB_CXX_SOURCES=b.cpp DYLIB_NAME=load_b
+
+
--- /dev/null
+""" Test that stop-on-sharedlibrary-events works and cooperates with breakpoints. """
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class TestStopOnSharedlibraryEvents(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipIfRemote
+ @skipIfWindows
+ @no_debug_info_test
+ def test_stopping_breakpoints(self):
+ self.do_test()
+
+ @skipIfRemote
+ @skipIfWindows
+ @no_debug_info_test
+ def test_auto_continue(self):
+ def auto_continue(bkpt):
+ bkpt.SetAutoContinue(True)
+ self.do_test(auto_continue)
+
+ @skipIfRemote
+ @skipIfWindows
+ @no_debug_info_test
+ def test_failing_condition(self):
+ def condition(bkpt):
+ bkpt.SetCondition("1 == 2")
+ self.do_test(condition)
+
+ @skipIfRemote
+ @skipIfWindows
+ @no_debug_info_test
+ def test_continue_callback(self):
+ def bkpt_callback(bkpt):
+ bkpt.SetScriptCallbackBody("return False")
+ self.do_test(bkpt_callback)
+
+ def do_test(self, bkpt_modifier = None):
+ self.build()
+ main_spec = lldb.SBFileSpec("main.cpp")
+ # Launch and stop before the dlopen call.
+ target, process, thread, _ = lldbutil.run_to_source_breakpoint(self,
+ "// Set a breakpoint here", main_spec)
+
+ # Now turn on shared library events, continue and make sure we stop for the event.
+ self.runCmd("settings set target.process.stop-on-sharedlibrary-events 1")
+ self.addTearDownHook(lambda: self.runCmd(
+ "settings set target.process.stop-on-sharedlibrary-events 0"))
+
+ # Since I don't know how to check that we are at the "right place" to stop for
+ # shared library events, make an breakpoint after the load is done and
+ # make sure we don't stop there:
+ backstop_bkpt_1 = target.BreakpointCreateBySourceRegex("Set another here - we should not hit this one", main_spec)
+ self.assertGreater(backstop_bkpt_1.GetNumLocations(), 0, "Set our second breakpoint")
+
+ process.Continue()
+ self.assertEqual(process.GetState(), lldb.eStateStopped, "We didn't stop for the load")
+ self.assertEqual(backstop_bkpt_1.GetHitCount(), 0, "Hit our backstop breakpoint")
+
+ # We should be stopped after the library is loaded, check that:
+ found_it = False
+ for module in target.modules:
+ if module.file.basename.find("load_a") > -1:
+ found_it = True
+ break
+ self.assertTrue(found_it, "Found the loaded module.")
+
+ # Now capture the place where we stopped so we can set a breakpoint and make
+ # sure the breakpoint there works correctly:
+ load_address = process.GetSelectedThread().frames[0].addr
+ load_bkpt = target.BreakpointCreateBySBAddress(load_address)
+ self.assertGreater(load_bkpt.GetNumLocations(), 0, "Set the load breakpoint")
+
+ backstop_bkpt_1.SetEnabled(False)
+
+ backstop_bkpt_2 = target.BreakpointCreateBySourceRegex("Set a third here - we should not hit this one", main_spec)
+ self.assertGreater(backstop_bkpt_2.GetNumLocations(), 0, "Set our third breakpoint")
+
+ if bkpt_modifier == None:
+ process.Continue()
+ self.assertEqual(process.GetState(), lldb.eStateStopped, "We didn't stop for the load")
+ self.assertEqual(backstop_bkpt_2.GetHitCount(), 0, "Hit our backstop breakpoint")
+ self.assertEqual(thread.stop_reason, lldb.eStopReasonBreakpoint, "We attributed the stop to the breakpoint")
+ self.assertEqual(load_bkpt.GetHitCount(), 1, "We hit our breakpoint at the load address")
+ else:
+ bkpt_modifier(load_bkpt)
+ process.Continue()
+ self.assertEqual(process.GetState(), lldb.eStateStopped, "We didn't stop")
+ self.assertTrue(thread.IsValid(), "Our thread was no longer valid.")
+ self.assertEqual(thread.stop_reason, lldb.eStopReasonBreakpoint, "We didn't hit some breakpoint")
+ self.assertEqual(backstop_bkpt_2.GetHitCount(), 1, "We continued to the right breakpoint")
+
+
+
+
+
--- /dev/null
+extern int a_has_a_function();
+
+int
+a_has_a_function() {
+ return 10;
+}
--- /dev/null
+extern int b_has_a_function();
+
+int
+b_has_a_function() {
+ return 100;
+}
--- /dev/null
+#include "dylib.h"
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char const *argv[]) {
+ const char *a_name = "load_a";
+ void *a_dylib_handle = NULL;
+
+ a_dylib_handle = dylib_open(a_name); // Set a breakpoint here.
+ if (a_dylib_handle == NULL) { // Set another here - we should not hit this one
+ fprintf(stderr, "%s\n", dylib_last_error());
+ exit(1);
+ }
+
+ const char *b_name = "load_b";
+ void *b_dylib_handle = NULL;
+
+ b_dylib_handle = dylib_open(b_name);
+ if (b_dylib_handle == NULL) { // Set a third here - we should not hit this one
+ fprintf(stderr, "%s\n", dylib_last_error());
+ exit(1);
+ }
+
+ return 0;
+}