Index: lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -2266,9 +2266,16 @@ 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 @@ switch (signo) { - case SIGSTOP: - { - std::static_pointer_cast (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 (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: Index: lldb/trunk/test/functionalities/signal/raise/Makefile =================================================================== --- lldb/trunk/test/functionalities/signal/raise/Makefile +++ lldb/trunk/test/functionalities/signal/raise/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules Index: lldb/trunk/test/functionalities/signal/raise/TestRaise.py =================================================================== --- lldb/trunk/test/functionalities/signal/raise/TestRaise.py +++ lldb/trunk/test/functionalities/signal/raise/TestRaise.py @@ -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() Index: lldb/trunk/test/functionalities/signal/raise/main.c =================================================================== --- lldb/trunk/test/functionalities/signal/raise/main.c +++ lldb/trunk/test/functionalities/signal/raise/main.c @@ -0,0 +1,23 @@ +#include +#include +#include + +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; +} +