Index: lldb/trunk/include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- lldb/trunk/include/lldb/Host/common/NativeProcessProtocol.h +++ lldb/trunk/include/lldb/Host/common/NativeProcessProtocol.h @@ -17,6 +17,8 @@ #include "lldb/Utility/Error.h" #include "lldb/lldb-private-forward.h" #include "lldb/lldb-types.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "NativeBreakpointList.h" @@ -64,6 +66,12 @@ virtual Error Kill() = 0; + //------------------------------------------------------------------ + // Tells a process not to stop the inferior on given signals + // and just reinject them back. + //------------------------------------------------------------------ + virtual Error IgnoreSignals(llvm::ArrayRef signals); + //---------------------------------------------------------------------- // Memory and memory region functions //---------------------------------------------------------------------- @@ -308,6 +316,10 @@ int m_terminal_fd; uint32_t m_stop_id; + // Set of signal numbers that LLDB directly injects back to inferior + // without stopping it. + llvm::DenseSet m_signals_to_ignore; + // lldb_private::Host calls should be used to launch a process for debugging, // and // then the process should be attached to. When attaching to a process Index: lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py +++ lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -929,7 +929,8 @@ "qXfer:libraries:read", "qXfer:libraries-svr4:read", "qXfer:features:read", - "qEcho" + "qEcho", + "QPassSignals" ] def parse_qSupported_response(self, context): Index: lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/Makefile =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/Makefile +++ lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules Index: lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/TestGdbRemote_QPassSignals.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/TestGdbRemote_QPassSignals.py +++ lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/TestGdbRemote_QPassSignals.py @@ -0,0 +1,113 @@ +from __future__ import print_function + +import gdbremote_testcase +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class TestGdbRemote_QPassSignals(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def expect_signal(self, expected_signo): + self.test_sequence.add_log_lines(["read packet: $vCont;c#a8", + {"direction": "send", + "regex": r"^\$T([0-9a-fA-F]{2}).*#[0-9a-fA-F]{2}$", + "capture": {1: "hex_exit_code"}}, + ], + True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + hex_exit_code = context.get("hex_exit_code") + self.assertIsNotNone(hex_exit_code) + self.assertEqual(int(hex_exit_code, 16), expected_signo) + + def expect_exit_code(self, exit_code): + self.test_sequence.add_log_lines( + ["read packet: $vCont;c#a8", + "send packet: $W{0:02x}#00".format(exit_code)], + True) + self.expect_gdbremote_sequence() + + + def ignore_signals(self, signals): + def signal_name_to_hex(signame): + return format(lldbutil.get_signal_number(signame), 'x') + signals_str = ";".join(map(signal_name_to_hex, signals)) + + self.test_sequence.add_log_lines(["read packet: $QPassSignals:" + + signals_str + " #00", + "send packet: $OK#00"], + True) + + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + @llgs_test + @skipUnlessPlatform(["linux", "android"]) + def test_q_pass_signals(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + expected_signals = ["SIGSEGV", + "SIGALRM", "SIGFPE", "SIGBUS", "SIGINT", "SIGHUP"] + signals_to_ignore = ["SIGUSR1", "SIGUSR2"] + self.ignore_signals(signals_to_ignore) + for signal_name in expected_signals: + signo = lldbutil.get_signal_number(signal_name) + self.expect_signal(signo) + self.expect_exit_code(len(signals_to_ignore)) + + @llgs_test + @skipUnlessPlatform(["linux", "android"]) + def test_change_signals_at_runtime(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + expected_signals = ["SIGSEGV", "SIGUSR1", "SIGUSR2", + "SIGALRM", "SIGHUP"] + signals_to_ignore = ["SIGFPE", "SIGBUS", "SIGINT"] + + for signal_name in expected_signals: + signo = lldbutil.get_signal_number(signal_name) + self.expect_signal(signo) + if signal_name == "SIGALRM": + self.ignore_signals(signals_to_ignore) + self.expect_exit_code(len(signals_to_ignore)) + + @llgs_test + def test_default_signals_behavior(self): + self.init_llgs_test() + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + expected_signals = ["SIGSEGV", "SIGUSR1", "SIGUSR2", + "SIGALRM", "SIGFPE", "SIGBUS", "SIGINT", "SIGHUP"] + for signal_name in expected_signals: + signo = lldbutil.get_signal_number(signal_name) + self.expect_signal(signo) + self.expect_exit_code(0) + + + @llgs_test + @skipUnlessPlatform(["linux", "android"]) + def test_support_q_pass_signals(self): + self.init_llgs_test() + self.build() + + # Start up the stub and start/prep the inferior. + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior() + self.add_qSupported_packets() + + # Run the packet stream. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Retrieve the qSupported features and check QPassSignals+ + supported_dict = self.parse_qSupported_response(context) + self.assertEqual(supported_dict["QPassSignals"], "+") Index: lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/main.cpp =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/main.cpp +++ lldb/trunk/packages/Python/lldbsuite/test/tools/lldb-server/signal-filtering/main.cpp @@ -0,0 +1,37 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +static int signal_counter = 0; + +static void count_signal(int signo) { + ++signal_counter; + printf("Signal %d\n", signo); +} + +static void raise_signals() { + std::vector signals( + {SIGSEGV, SIGUSR1, SIGUSR2, SIGALRM, SIGFPE, SIGBUS, SIGINT, SIGHUP}); + + for (int signal_num : signals) { + signal(signal_num, count_signal); + } + + for (int signal_num : signals) { + raise(signal_num); + } +} + +int main() { + raise_signals(); + return signal_counter; +} Index: lldb/trunk/source/Host/common/NativeProcessProtocol.cpp =================================================================== --- lldb/trunk/source/Host/common/NativeProcessProtocol.cpp +++ lldb/trunk/source/Host/common/NativeProcessProtocol.cpp @@ -46,6 +46,12 @@ #endif } +Error NativeProcessProtocol::IgnoreSignals(llvm::ArrayRef signals) { + m_signals_to_ignore.clear(); + m_signals_to_ignore.insert(signals.begin(), signals.end()); + return Error(); +} + lldb_private::Error NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr, MemoryRegionInfo &range_info) { 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 @@ -1037,6 +1037,13 @@ return; } + // Check if debugger should stop at this signal or just ignore it + // and resume the inferior. + if (m_signals_to_ignore.find(signo) != m_signals_to_ignore.end()) { + ResumeThread(thread, thread.GetState(), signo); + return; + } + // This thread is stopped. LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo)); thread.SetStoppedBySignal(signo, &info); Index: lldb/trunk/source/Plugins/Process/POSIX/CrashReason.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/POSIX/CrashReason.cpp +++ lldb/trunk/source/Plugins/Process/POSIX/CrashReason.cpp @@ -62,7 +62,6 @@ return CrashReason::eBoundViolation; } - assert(false && "unexpected si_code for SIGSEGV"); return CrashReason::eInvalidCrashReason; } @@ -88,7 +87,6 @@ return CrashReason::eInternalStackError; } - assert(false && "unexpected si_code for SIGILL"); return CrashReason::eInvalidCrashReason; } @@ -114,7 +112,6 @@ return CrashReason::eFloatSubscriptRange; } - assert(false && "unexpected si_code for SIGFPE"); return CrashReason::eInvalidCrashReason; } @@ -130,7 +127,6 @@ return CrashReason::eHardwareError; } - assert(false && "unexpected si_code for SIGBUS"); return CrashReason::eInvalidCrashReason; } } @@ -158,7 +154,7 @@ switch (reason) { default: - assert(false && "invalid CrashReason"); + str = "unknown crash reason"; break; case CrashReason::eInvalidAddress: Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -838,6 +838,7 @@ response.PutCString(";QListThreadsInStopReply+"); response.PutCString(";qEcho+"); #if defined(__linux__) + response.PutCString(";QPassSignals+"); response.PutCString(";qXfer:auxv:read+"); #endif Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -203,6 +203,8 @@ PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet); + PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet); + void SetCurrentThreadID(lldb::tid_t tid); lldb::tid_t GetCurrentThreadID() const; Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -181,6 +181,9 @@ &GDBRemoteCommunicationServerLLGS::Handle_Z); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z, &GDBRemoteCommunicationServerLLGS::Handle_z); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QPassSignals, + &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k, [this](StringExtractorGDBRemote packet, Error &error, @@ -3072,6 +3075,40 @@ return SendPacketNoLock(response.GetString()); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QPassSignals( + StringExtractorGDBRemote &packet) { + std::vector signals; + packet.SetFilePos(strlen("QPassSignals:")); + + // Read sequence of hex signal numbers divided by a semicolon and + // optionally spaces. + while (packet.GetBytesLeft() > 0) { + int signal = packet.GetS32(-1, 16); + if (signal < 0) + return SendIllFormedResponse(packet, "Failed to parse signal number."); + signals.push_back(signal); + + packet.SkipSpaces(); + char separator = packet.GetChar(); + if (separator == '\0') + break; // End of string + if (separator != ';') + return SendIllFormedResponse(packet, "Invalid separator," + " expected semicolon."); + } + + // Fail if we don't have a current process. + if (!m_debugged_process_sp) + return SendErrorResponse(68); + + Error error = m_debugged_process_sp->IgnoreSignals(signals); + if (error.Fail()) + return SendErrorResponse(69); + + return SendOKResponse(); +} + void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); Index: lldb/trunk/source/Utility/StringExtractorGDBRemote.h =================================================================== --- lldb/trunk/source/Utility/StringExtractorGDBRemote.h +++ lldb/trunk/source/Utility/StringExtractorGDBRemote.h @@ -96,6 +96,7 @@ // debug server packages eServerPacketType_QEnvironmentHexEncoded, eServerPacketType_QListThreadsInStopReply, + eServerPacketType_QPassSignals, eServerPacketType_QRestoreRegisterState, eServerPacketType_QSaveRegisterState, eServerPacketType_QSetLogging, Index: lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp +++ lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp @@ -91,6 +91,10 @@ return eServerPacketType_QEnvironmentHexEncoded; break; + case 'P': + if (PACKET_STARTS_WITH("QPassSignals:")) + return eServerPacketType_QPassSignals; + case 'S': if (PACKET_MATCHES("QStartNoAckMode")) return eServerPacketType_QStartNoAckMode;