jingham created this revision.
jingham added a reviewer: JDevlieghere.
jingham requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This setting is supposed to make us stop when we hit a shared library event.  
The shared library event callbacks happen as synchronous breakpoint callbacks - 
below the level of user callbacks.  The information that the synchronous 
callback wanted to stop was getting propagated to the StopInfoBreakpoint 
correctly, but then we lost that bit of information when we went to do 
PerformAction.  This patch takes that into account.

In this patch, if the synchronous callback says to stop, but the user has 
another breakpoint that's auto-continue, or a callback that says to continue, 
at the same spot, I obey the other breakpoint's opinions rather than the 
callback one.  That seems to me right since the sync callbacks we have around 
are implementation details and the user can't even really know where they might 
be easily.  So having them collide with the user's intentions doesn't seem 
right.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D98914

Files:
  lldb/source/Breakpoint/BreakpointOptions.cpp
  lldb/source/Target/StopInfo.cpp
  lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile
  
lldb/test/API/functionalities/stop-on-sharedlibrary-load/TestStopOnSharedlibraryEvents.py
  lldb/test/API/functionalities/stop-on-sharedlibrary-load/a.cpp
  lldb/test/API/functionalities/stop-on-sharedlibrary-load/main.cpp

Index: lldb/test/API/functionalities/stop-on-sharedlibrary-load/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/stop-on-sharedlibrary-load/main.cpp
@@ -0,0 +1,17 @@
+#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);
+  }
+  return 0;
+}
Index: lldb/test/API/functionalities/stop-on-sharedlibrary-load/a.cpp
===================================================================
--- /dev/null
+++ 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;
+}
Index: lldb/test/API/functionalities/stop-on-sharedlibrary-load/TestStopOnSharedlibraryEvents.py
===================================================================
--- /dev/null
+++ 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.")
+        
+        
Index: lldb/test/API/functionalities/stop-on-sharedlibrary-load/Makefile
===================================================================
--- /dev/null
+++ 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
+
+
Index: lldb/source/Target/StopInfo.cpp
===================================================================
--- lldb/source/Target/StopInfo.cpp
+++ 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.
Index: lldb/source/Breakpoint/BreakpointOptions.cpp
===================================================================
--- lldb/source/Breakpoint/BreakpointOptions.cpp
+++ 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;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to