[Lldb-commits] [lldb] r237880 - [NativeProcessLinux] Fix handling of SIGSTOP

Pavel Labath labath at google.com
Thu May 21 01:32:18 PDT 2015


Author: labath
Date: Thu May 21 03:32:18 2015
New Revision: 237880

URL: http://llvm.org/viewvc/llvm-project?rev=237880&view=rev
Log:
[NativeProcessLinux] Fix handling of SIGSTOP

Summary:
Previously, NPL tried to reinject SIGSTOP into the inferior in an attempt to get the process to
start in the group-stop state. This was:
a) wrong (reinjection should be controlled by "process handle" lldb setting)
b) racy (it should use Resume for transparent resuming instead of RequestResume)
c) broken (llgs crashed on inferior SIGSTOP)

With this change, SIGSTOP is handled just like any other signal delivered to the inferior: we
stop all threads and report signal reception to lldb. SIGSTOP reinjection does not behave the
same way as it would outside the debugger, but simulating this is a hard problem and is not
normally necessary.

Test Plan: I have added a test which verifies we get SIGSTOP reports and we do not crash.

Reviewers: ovyalov, chaoren

Subscribers: lldb-commits

Differential Revision: http://reviews.llvm.org/D9852

Added:
    lldb/trunk/test/functionalities/signal/raise/
    lldb/trunk/test/functionalities/signal/raise/Makefile
    lldb/trunk/test/functionalities/signal/raise/TestRaise.py
    lldb/trunk/test/functionalities/signal/raise/main.c
Modified:
    lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp?rev=237880&r1=237879&r2=237880&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp Thu May 21 03:32:18 2015
@@ -2266,9 +2266,16 @@ NativeProcessLinux::MonitorCallback(lldb
         if (err.GetError() == EINVAL)
         {
             // This is a group stop reception for this tid.
+            // We can reach here if we reinject SIGSTOP, SIGSTP, SIGTTIN or SIGTTOU into the
+            // tracee, triggering the group-stop mechanism. Normally receiving these would stop
+            // the process, pending a SIGCONT. Simulating this state in a debugger is hard and is
+            // generally not needed (one use case is debugging background task being managed by a
+            // shell). For general use, it is sufficient to stop the process in a signal-delivery
+            // stop which happens before the group stop. This done by MonitorSignal and works
+            // correctly for all signals.
             if (log)
-                log->Printf ("NativeProcessLinux::%s received a group stop for pid %" PRIu64 " tid %" PRIu64, __FUNCTION__, GetID (), pid);
-            ThreadDidStop(pid, false);
+                log->Printf("NativeProcessLinux::%s received a group stop for pid %" PRIu64 " tid %" PRIu64 ". Transparent handling of group stops not supported, resuming the thread.", __FUNCTION__, GetID (), pid);
+            Resume(pid, signal);
         }
         else
         {
@@ -2776,29 +2783,6 @@ NativeProcessLinux::MonitorSignal(const
 
     switch (signo)
     {
-    case SIGSTOP:
-        {
-            std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetStoppedBySignal (signo);
-            if (log)
-            {
-                if (is_from_llgs)
-                    log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " received SIGSTOP from llgs, most likely an interrupt", __FUNCTION__, GetID (), pid);
-                else
-                    log->Printf ("NativeProcessLinux::%s pid = %" PRIu64 " tid %" PRIu64 " received SIGSTOP from outside of debugger", __FUNCTION__, GetID (), pid);
-            }
-
-            // Resume this thread to get the group-stop mechanism to fire off the true group stops.
-            // This thread will get stopped again as part of the group-stop completion.
-            ResumeThread(pid,
-                    [=](lldb::tid_t tid_to_resume, bool supress_signal)
-                    {
-                        std::static_pointer_cast<NativeThreadLinux> (thread_sp)->SetRunning ();
-                        // Pass this signal number on to the inferior to handle.
-                        return Resume (tid_to_resume, (supress_signal) ? LLDB_INVALID_SIGNAL_NUMBER : signo);
-                    },
-                    true);
-        }
-        break;
     case SIGSEGV:
     case SIGILL:
     case SIGFPE:

Added: lldb/trunk/test/functionalities/signal/raise/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/signal/raise/Makefile?rev=237880&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/signal/raise/Makefile (added)
+++ lldb/trunk/test/functionalities/signal/raise/Makefile Thu May 21 03:32:18 2015
@@ -0,0 +1,5 @@
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/test/functionalities/signal/raise/TestRaise.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/signal/raise/TestRaise.py?rev=237880&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/signal/raise/TestRaise.py (added)
+++ lldb/trunk/test/functionalities/signal/raise/TestRaise.py Thu May 21 03:32:18 2015
@@ -0,0 +1,95 @@
+"""Test that we handle inferiors that send signals to themselves"""
+
+import os
+import unittest2
+import lldb
+from lldbtest import *
+import lldbutil
+
+
+class RaiseTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    @skipIfWindows # signals do not exist on Windows
+    @skipUnlessDarwin
+    @dsym_test
+    def test_sigstop_with_dsym(self):
+        self.buildDsym()
+        self.sigstop()
+
+    @skipIfWindows # signals do not exist on Windows
+    @dwarf_test
+    def test_sigstop_with_dwarf(self):
+        self.buildDwarf()
+        self.sigstop()
+
+    def launch(self, target):
+        # launch the process, do not stop at entry point.
+        process = target.LaunchSimple(['SIGSTOP'], None, self.get_process_working_directory())
+        self.assertTrue(process, PROCESS_IS_VALID)
+        self.assertEqual(process.GetState(), lldb.eStateStopped)
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
+        self.assertTrue(thread.IsValid(), "Thread should be stopped due to a breakpoint")
+        return process
+
+    def set_handle(self, signal, stop_at_signal, pass_signal, notify_signal):
+        return_obj = lldb.SBCommandReturnObject()
+        self.dbg.GetCommandInterpreter().HandleCommand(
+                "process handle %s -s %d -p %d -n %d" % (signal, stop_at_signal, pass_signal, notify_signal),
+                return_obj)
+        self.assertTrue (return_obj.Succeeded() == True, "Setting signal handling failed")
+
+
+    def sigstop(self):
+        """Test that we handle inferior raising SIGSTOP"""
+        exe = os.path.join(os.getcwd(), "a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+        lldbutil.run_break_set_by_symbol(self, "main")
+
+        # launch
+        process = self.launch(target)
+
+        # Make sure we stop at the signal
+        self.set_handle("SIGSTOP", 1, 0, 1)
+        process.Continue()
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
+        self.assertTrue(thread.IsValid(), "Thread should be stopped due to a signal")
+        self.assertTrue(thread.GetStopReasonDataCount() >= 1, "There was data in the event.")
+        self.assertEqual(thread.GetStopReasonDataAtIndex(0),
+                process.GetUnixSignals().GetSignalNumberFromName('SIGSTOP'),
+                "The stop signal was SIGSTOP")
+
+        # Continue until we exit.
+        process.Continue()
+        self.assertEqual(process.GetState(), lldb.eStateExited)
+
+        # launch again
+        process = self.launch(target)
+
+        # Make sure we do not stop at the signal. We should still get the notification.
+        self.set_handle("SIGSTOP", 0, 0, 1)
+        self.expect("process continue", substrs=["stopped and restarted", "SIGSTOP"])
+        self.assertEqual(process.GetState(), lldb.eStateExited)
+
+        # launch again
+        process = self.launch(target)
+
+        # Make sure we do not stop at the signal, and we do not get the notification.
+        self.set_handle("SIGSTOP", 0, 0, 0)
+        self.expect("process continue", substrs=["stopped and restarted"], matching=False)
+        self.assertEqual(process.GetState(), lldb.eStateExited)
+
+        # passing of SIGSTOP is not correctly handled, so not testing that scenario: https://llvm.org/bugs/show_bug.cgi?id=23574
+
+        # reset signal handling to default
+        self.set_handle("SIGSTOP", 1, 0, 1)
+
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()

Added: lldb/trunk/test/functionalities/signal/raise/main.c
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/signal/raise/main.c?rev=237880&view=auto
==============================================================================
--- lldb/trunk/test/functionalities/signal/raise/main.c (added)
+++ lldb/trunk/test/functionalities/signal/raise/main.c Thu May 21 03:32:18 2015
@@ -0,0 +1,23 @@
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+int main (int argc, char *argv[])
+{
+    if (argc < 2)
+    {
+        puts("Please specify a signal to raise");
+        return 1;
+    }
+
+    if (strcmp(argv[1], "SIGSTOP") == 0)
+        raise(SIGSTOP);
+    else
+    {
+        printf("Unknown signal: %s\n", argv[1]);
+        return 2;
+    }
+
+    return 0;
+}
+





More information about the lldb-commits mailing list