[Lldb-commits] [lldb] e655769 - Fix a bug in Launch when using an async debugger & remote platform.

Jim Ingham via lldb-commits lldb-commits at lists.llvm.org
Thu Oct 28 17:02:52 PDT 2021


Author: Jim Ingham
Date: 2021-10-28T17:02:43-07:00
New Revision: e655769c4a7b5a17b17eace3bd160b3b015b75ed

URL: https://github.com/llvm/llvm-project/commit/e655769c4a7b5a17b17eace3bd160b3b015b75ed
DIFF: https://github.com/llvm/llvm-project/commit/e655769c4a7b5a17b17eace3bd160b3b015b75ed.diff

LOG: Fix a bug in Launch when using an async debugger & remote platform.

We weren't setting the listener back to the unhijacked one in this
case, so that a continue after the stop fails.  It thinks the process
is still running.  Also add tests for this behavior.

Differential Revision: https://reviews.llvm.org/D112747

Added: 
    lldb/test/API/functionalities/launch_stop_at_entry/Makefile
    lldb/test/API/functionalities/launch_stop_at_entry/TestStopAtEntry.py
    lldb/test/API/functionalities/launch_stop_at_entry/main.c

Modified: 
    lldb/source/Target/Process.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 79ede760fb819..d901d523a1036 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2580,6 +2580,9 @@ Status Process::Launch(ProcessLaunchInfo &launch_info) {
           // stopped or crashed. Directly set the state.  This is done to
           // prevent a stop message with a bunch of spurious output on thread
           // status, as well as not pop a ProcessIOHandler.
+          // We are done with the launch hijack listener, and this stop should
+          // go to the public state listener:
+          RestoreProcessEvents();
           SetPublicState(state, false);
 
           if (PrivateStateThreadIsValid())

diff  --git a/lldb/test/API/functionalities/launch_stop_at_entry/Makefile b/lldb/test/API/functionalities/launch_stop_at_entry/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/functionalities/launch_stop_at_entry/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules

diff  --git a/lldb/test/API/functionalities/launch_stop_at_entry/TestStopAtEntry.py b/lldb/test/API/functionalities/launch_stop_at_entry/TestStopAtEntry.py
new file mode 100644
index 0000000000000..157d16fe025b7
--- /dev/null
+++ b/lldb/test/API/functionalities/launch_stop_at_entry/TestStopAtEntry.py
@@ -0,0 +1,163 @@
+import lldb
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbgdbserverutils import get_debugserver_exe
+
+import os
+import platform
+import shutil
+import time
+import socket
+
+
+class TestStopAtEntry(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    # The port used by debugserver.
+    PORT = 54637
+
+    # The number of attempts.
+    ATTEMPTS = 10
+
+    # Time given to the binary to launch and to debugserver to attach to it for
+    # every attempt. We'll wait a maximum of 10 times 2 seconds while the
+    # inferior will wait 10 times 10 seconds.
+    TIMEOUT = 2
+
+    def no_debugserver(self):
+        if get_debugserver_exe() is None:
+            return 'no debugserver'
+        return None
+
+    def port_not_available(self):
+        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        if s.connect_ex(('127.0.0.1', self.PORT)) == 0:
+            return '{} not available'.format(self.PORT)
+        return None
+
+    @skipUnlessDarwin
+    def test_stop_default_platform_sync(self):
+        self.do_test_stop_at_entry(True, False)
+
+    @skipUnlessDarwin
+    def test_stop_default_platform_async(self):
+        self.do_test_stop_at_entry(False, False)
+
+    @skipUnlessDarwin
+    @expectedFailureIfFn(no_debugserver)
+    @expectedFailureIfFn(port_not_available)
+    def test_stop_remote_platform_sync(self):
+        self.do_test_stop_at_entry(True, True)
+
+    @skipUnlessDarwin
+    @expectedFailureIfFn(no_debugserver)
+    @expectedFailureIfFn(port_not_available)
+    def test_stop_remote_platform_async(self):
+        self.do_test_stop_at_entry(False, True)
+
+    def do_test_stop_at_entry(self, synchronous, remote):        
+        """Test the normal launch path in either sync or async mode"""
+        self.build()
+
+        target = lldbutil.run_to_breakpoint_make_target(self)
+        launch_info = target.GetLaunchInfo()
+        launch_info.SetLaunchFlags(lldb.eLaunchFlagStopAtEntry)
+        old_async = self.dbg.GetAsync()
+        def cleanup ():
+            self.dbg.SetAsync(old_async)
+        self.addTearDownHook(cleanup)
+
+        if not synchronous:
+            self.dbg.SetAsync(True)
+            listener = lldb.SBListener("test-process-listener")
+            mask = listener.StartListeningForEventClass(self.dbg, lldb.SBProcess.GetBroadcasterClassName(), lldb.SBProcess.eBroadcastBitStateChanged)
+            self.assertEqual(mask, lldb.SBProcess.eBroadcastBitStateChanged, "Got right mask for listener")
+            launch_info.SetListener(listener)
+        else:
+            self.dbg.SetAsync(False)
+
+        if remote:
+            self.setup_remote_platform()
+
+        error = lldb.SBError()
+
+        process = target.Launch(launch_info, error)
+        self.assertTrue(error.Success(), "Launch failed: {0}".format(error.description))
+        # If we are asynchronous, we have to wait for the events:
+        if not synchronous:
+            listener = launch_info.GetListener()
+            event = lldb.SBEvent()
+            result = listener.WaitForEvent(30, event)
+            self.assertTrue(result, "Timed out waiting for event from process")
+            state = lldb.SBProcess.GetStateFromEvent(event)
+            self.assertEqual(state, lldb.eStateStopped, "Didn't get a stopped state after launch")
+
+        # Okay, we should be stopped.  Make sure we are indeed at the
+        # entry point.  I only know how to do this on darwin:
+        self.assertEqual(len(process.threads), 1, "Should only have one thread at entry")
+        thread = process.threads[0]
+        frame = thread.GetFrameAtIndex(0)
+        stop_func = frame.name
+        self.assertEqual(stop_func, "_dyld_start")
+
+        # Now make sure that we can resume the process and have it exit.
+        error = process.Continue()
+        self.assertTrue(error.Success(), "Error continuing: {0}".format(error.description))
+        # Fetch events till we get eStateExited:
+        if not synchronous:
+            # Get events till exited.
+            listener = launch_info.GetListener()
+            event = lldb.SBEvent()
+            # We get two running events in a row here???  That's a bug
+            # but not the one I'm testing for, so for now just fetch as
+            # many as were sent.
+            num_running = 0
+            state = lldb.eStateRunning
+            while state == lldb.eStateRunning:
+                num_running += 1
+                result = listener.WaitForEvent(30, event)
+                self.assertTrue(result, "Timed out waiting for running")
+                state = lldb.SBProcess.GetStateFromEvent(event)
+                if num_running == 1:
+                    self.assertEqual(state, lldb.eStateRunning, "Got running event")
+            # The last event we should get is the exited event
+            self.assertEqual(state, lldb.eStateExited, "Got running event")
+        else:
+            # Make sure that the process has indeed exited
+            state = process.GetState()
+            self.assertEqual(state, lldb.eStateExited);
+
+    def setup_remote_platform(self):
+        return
+        self.build()
+
+        exe = self.getBuildArtifact('a.out')
+        # Launch our test binary.
+
+        # Attach to it with debugserver.
+        debugserver = get_debugserver_exe()
+        debugserver_args = [
+            'localhost:{}'.format(self.PORT)
+        ]
+        self.spawnSubprocess(debugserver, debugserver_args)
+
+        # Select the platform.
+        self.expect('platform select remote-macosx', substrs=[sdk_dir])
+
+        # Connect to debugserver
+        interpreter = self.dbg.GetCommandInterpreter()
+        connected = False
+        for i in range(self.ATTEMPTS):
+            result = lldb.SBCommandReturnObject()
+            interpreter.HandleCommand('gdb-remote {}'.format(self.PORT),
+                                      result)
+            connected = result.Succeeded()
+            if connected:
+                break
+            time.sleep(self.TIMEOUT)
+
+        self.assertTrue(connected, "could not connect to debugserver")

diff  --git a/lldb/test/API/functionalities/launch_stop_at_entry/main.c b/lldb/test/API/functionalities/launch_stop_at_entry/main.c
new file mode 100644
index 0000000000000..716f330e1788a
--- /dev/null
+++ b/lldb/test/API/functionalities/launch_stop_at_entry/main.c
@@ -0,0 +1,5 @@
+int main(int argc, char **argv) {
+  /* We just want to make sure this ran, so
+     it doesn't actually need to do anything. */
+  return 0;
+}


        


More information about the lldb-commits mailing list