diff --git a/lldb/source/Breakpoint/BreakpointOptions.cpp b/lldb/source/Breakpoint/BreakpointOptions.cpp --- a/lldb/source/Breakpoint/BreakpointOptions.cpp +++ b/lldb/source/Breakpoint/BreakpointOptions.cpp @@ -453,9 +453,12 @@ : 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; + // If a synchronous callback is called at async time, we will say we + // should stop, we're really expression no opinion about stopping, and + // the StopInfoBreakpoint::PerformAction will note whether an async + // callback had already made a claim to stop or not based on the incoming + // values of m_should_stop & m_should_stop_is_valid. + return true; } } return true; diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -305,6 +305,20 @@ // 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 @@ -422,9 +436,10 @@ 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. @@ -462,6 +477,7 @@ // 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; } } @@ -504,6 +520,9 @@ 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. @@ -517,9 +536,15 @@ // 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. diff --git a/lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile @@ -0,0 +1,12 @@ +CXX_SOURCES := main.cpp +USE_LIBDL := 1 + +a.out: lib_a + +include Makefile.rules + +lib_a: + $(MAKE) -f $(MAKEFILE_RULES) \ + DYLIB_ONLY=YES DYLIB_CXX_SOURCES=a.cpp DYLIB_NAME=load_a + + diff --git a/lldb/test/API/functionalities/stop-on-sharedlibrary-load/TestStopOnSharedlibraryEvents.py b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/TestStopOnSharedlibraryEvents.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/TestStopOnSharedlibraryEvents.py @@ -0,0 +1,43 @@ +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(self): + self.build() + main_spec = lldb.SBFileSpec("main.cpp") + # Launch and stop before the dlopen call. + target, process, _, _ = 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: + bkpt = target.BreakpointCreateBySourceRegex("Set another here - we should not hit this one", main_spec) + self.assertGreater(bkpt.GetNumLocations(), 0, "Set our second breakpoint") + + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateStopped, "We didn't stop for the load") + self.assertEqual(bkpt.GetHitCount(), 0, "Hit our backstop breakpoint") + + # Finally, we should be stopped after the library is loaded: + 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.") + + diff --git a/lldb/test/API/functionalities/stop-on-sharedlibrary-load/a.cpp b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/a.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/a.cpp @@ -0,0 +1,6 @@ +extern int i_have_a_function(); + +int +i_have_a_function() { + return 10; +} diff --git a/lldb/test/API/functionalities/stop-on-sharedlibrary-load/main.cpp b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/stop-on-sharedlibrary-load/main.cpp @@ -0,0 +1,17 @@ +#include "dylib.h" +#include +#include +#include +#include + +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); + } + return 0; +}