Implement QPassSignals GDB package in lldb-server

Summary: QPassSignals package allows lldb client to tell lldb-server to ignore certain types of signals and re-inject them back to inferior without stopping execution.

Reviewers: jmajors, labath

Subscribers: danalbert, srhines, emaste, lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D30286
Author: Eugene Zemtsov <ezemtsov@google.com>

llvm-svn: 296101
This commit is contained in:
Pavel Labath 2017-02-24 09:29:14 +00:00
parent 80c3b17410
commit 4a705e7ea0
13 changed files with 228 additions and 6 deletions

View File

@ -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 @@ public:
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<int> signals);
//----------------------------------------------------------------------
// Memory and memory region functions
//----------------------------------------------------------------------
@ -308,6 +316,10 @@ protected:
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<int> 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

View File

@ -929,7 +929,8 @@ class GdbRemoteTestCaseBase(TestBase):
"qXfer:libraries:read",
"qXfer:libraries-svr4:read",
"qXfer:features:read",
"qEcho"
"qEcho",
"QPassSignals"
]
def parse_qSupported_response(self, context):

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

@ -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"], "+")

View File

@ -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 <signal.h>
#include <stdio.h>
#include <vector>
static int signal_counter = 0;
static void count_signal(int signo) {
++signal_counter;
printf("Signal %d\n", signo);
}
static void raise_signals() {
std::vector<int> 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;
}

View File

@ -46,6 +46,12 @@ lldb_private::Error NativeProcessProtocol::Interrupt() {
#endif
}
Error NativeProcessProtocol::IgnoreSignals(llvm::ArrayRef<int> 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) {

View File

@ -1037,6 +1037,13 @@ void NativeProcessLinux::MonitorSignal(const siginfo_t &info,
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);

View File

@ -62,7 +62,6 @@ CrashReason GetCrashReasonForSIGSEGV(const siginfo_t &info) {
return CrashReason::eBoundViolation;
}
assert(false && "unexpected si_code for SIGSEGV");
return CrashReason::eInvalidCrashReason;
}
@ -88,7 +87,6 @@ CrashReason GetCrashReasonForSIGILL(const siginfo_t &info) {
return CrashReason::eInternalStackError;
}
assert(false && "unexpected si_code for SIGILL");
return CrashReason::eInvalidCrashReason;
}
@ -114,7 +112,6 @@ CrashReason GetCrashReasonForSIGFPE(const siginfo_t &info) {
return CrashReason::eFloatSubscriptRange;
}
assert(false && "unexpected si_code for SIGFPE");
return CrashReason::eInvalidCrashReason;
}
@ -130,7 +127,6 @@ CrashReason GetCrashReasonForSIGBUS(const siginfo_t &info) {
return CrashReason::eHardwareError;
}
assert(false && "unexpected si_code for SIGBUS");
return CrashReason::eInvalidCrashReason;
}
}
@ -158,7 +154,7 @@ std::string GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr) {
switch (reason) {
default:
assert(false && "invalid CrashReason");
str = "unknown crash reason";
break;
case CrashReason::eInvalidAddress:

View File

@ -838,6 +838,7 @@ GDBRemoteCommunicationServerCommon::Handle_qSupported(
response.PutCString(";QListThreadsInStopReply+");
response.PutCString(";qEcho+");
#if defined(__linux__)
response.PutCString(";QPassSignals+");
response.PutCString(";qXfer:auxv:read+");
#endif

View File

@ -181,6 +181,9 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
&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 @@ GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress(
return SendPacketNoLock(response.GetString());
}
GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::Handle_QPassSignals(
StringExtractorGDBRemote &packet) {
std::vector<int> 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));

View File

@ -203,6 +203,8 @@ protected:
PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet);
PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet);
void SetCurrentThreadID(lldb::tid_t tid);
lldb::tid_t GetCurrentThreadID() const;

View File

@ -91,6 +91,10 @@ StringExtractorGDBRemote::GetServerPacketType() const {
return eServerPacketType_QEnvironmentHexEncoded;
break;
case 'P':
if (PACKET_STARTS_WITH("QPassSignals:"))
return eServerPacketType_QPassSignals;
case 'S':
if (PACKET_MATCHES("QStartNoAckMode"))
return eServerPacketType_QStartNoAckMode;

View File

@ -96,6 +96,7 @@ public:
// debug server packages
eServerPacketType_QEnvironmentHexEncoded,
eServerPacketType_QListThreadsInStopReply,
eServerPacketType_QPassSignals,
eServerPacketType_QRestoreRegisterState,
eServerPacketType_QSaveRegisterState,
eServerPacketType_QSetLogging,