forked from OSchip/llvm-project
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:
parent
80c3b17410
commit
4a705e7ea0
|
@ -17,6 +17,8 @@
|
||||||
#include "lldb/Utility/Error.h"
|
#include "lldb/Utility/Error.h"
|
||||||
#include "lldb/lldb-private-forward.h"
|
#include "lldb/lldb-private-forward.h"
|
||||||
#include "lldb/lldb-types.h"
|
#include "lldb/lldb-types.h"
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
#include "llvm/ADT/DenseSet.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
|
||||||
#include "NativeBreakpointList.h"
|
#include "NativeBreakpointList.h"
|
||||||
|
@ -64,6 +66,12 @@ public:
|
||||||
|
|
||||||
virtual Error Kill() = 0;
|
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
|
// Memory and memory region functions
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
@ -308,6 +316,10 @@ protected:
|
||||||
int m_terminal_fd;
|
int m_terminal_fd;
|
||||||
uint32_t m_stop_id;
|
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,
|
// lldb_private::Host calls should be used to launch a process for debugging,
|
||||||
// and
|
// and
|
||||||
// then the process should be attached to. When attaching to a process
|
// then the process should be attached to. When attaching to a process
|
||||||
|
|
|
@ -929,7 +929,8 @@ class GdbRemoteTestCaseBase(TestBase):
|
||||||
"qXfer:libraries:read",
|
"qXfer:libraries:read",
|
||||||
"qXfer:libraries-svr4:read",
|
"qXfer:libraries-svr4:read",
|
||||||
"qXfer:features:read",
|
"qXfer:features:read",
|
||||||
"qEcho"
|
"qEcho",
|
||||||
|
"QPassSignals"
|
||||||
]
|
]
|
||||||
|
|
||||||
def parse_qSupported_response(self, context):
|
def parse_qSupported_response(self, context):
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
LEVEL = ../../../make
|
||||||
|
|
||||||
|
CXX_SOURCES := main.cpp
|
||||||
|
|
||||||
|
include $(LEVEL)/Makefile.rules
|
|
@ -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"], "+")
|
|
@ -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;
|
||||||
|
}
|
|
@ -46,6 +46,12 @@ lldb_private::Error NativeProcessProtocol::Interrupt() {
|
||||||
#endif
|
#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
|
lldb_private::Error
|
||||||
NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr,
|
NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr,
|
||||||
MemoryRegionInfo &range_info) {
|
MemoryRegionInfo &range_info) {
|
||||||
|
|
|
@ -1037,6 +1037,13 @@ void NativeProcessLinux::MonitorSignal(const siginfo_t &info,
|
||||||
return;
|
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.
|
// This thread is stopped.
|
||||||
LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo));
|
LLDB_LOG(log, "received signal {0}", Host::GetSignalAsCString(signo));
|
||||||
thread.SetStoppedBySignal(signo, &info);
|
thread.SetStoppedBySignal(signo, &info);
|
||||||
|
|
|
@ -62,7 +62,6 @@ CrashReason GetCrashReasonForSIGSEGV(const siginfo_t &info) {
|
||||||
return CrashReason::eBoundViolation;
|
return CrashReason::eBoundViolation;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false && "unexpected si_code for SIGSEGV");
|
|
||||||
return CrashReason::eInvalidCrashReason;
|
return CrashReason::eInvalidCrashReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +87,6 @@ CrashReason GetCrashReasonForSIGILL(const siginfo_t &info) {
|
||||||
return CrashReason::eInternalStackError;
|
return CrashReason::eInternalStackError;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false && "unexpected si_code for SIGILL");
|
|
||||||
return CrashReason::eInvalidCrashReason;
|
return CrashReason::eInvalidCrashReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +112,6 @@ CrashReason GetCrashReasonForSIGFPE(const siginfo_t &info) {
|
||||||
return CrashReason::eFloatSubscriptRange;
|
return CrashReason::eFloatSubscriptRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false && "unexpected si_code for SIGFPE");
|
|
||||||
return CrashReason::eInvalidCrashReason;
|
return CrashReason::eInvalidCrashReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +127,6 @@ CrashReason GetCrashReasonForSIGBUS(const siginfo_t &info) {
|
||||||
return CrashReason::eHardwareError;
|
return CrashReason::eHardwareError;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false && "unexpected si_code for SIGBUS");
|
|
||||||
return CrashReason::eInvalidCrashReason;
|
return CrashReason::eInvalidCrashReason;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +154,7 @@ std::string GetCrashReasonString(CrashReason reason, lldb::addr_t fault_addr) {
|
||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
default:
|
default:
|
||||||
assert(false && "invalid CrashReason");
|
str = "unknown crash reason";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CrashReason::eInvalidAddress:
|
case CrashReason::eInvalidAddress:
|
||||||
|
|
|
@ -838,6 +838,7 @@ GDBRemoteCommunicationServerCommon::Handle_qSupported(
|
||||||
response.PutCString(";QListThreadsInStopReply+");
|
response.PutCString(";QListThreadsInStopReply+");
|
||||||
response.PutCString(";qEcho+");
|
response.PutCString(";qEcho+");
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
|
response.PutCString(";QPassSignals+");
|
||||||
response.PutCString(";qXfer:auxv:read+");
|
response.PutCString(";qXfer:auxv:read+");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -181,6 +181,9 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
|
||||||
&GDBRemoteCommunicationServerLLGS::Handle_Z);
|
&GDBRemoteCommunicationServerLLGS::Handle_Z);
|
||||||
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z,
|
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_z,
|
||||||
&GDBRemoteCommunicationServerLLGS::Handle_z);
|
&GDBRemoteCommunicationServerLLGS::Handle_z);
|
||||||
|
RegisterMemberFunctionHandler(
|
||||||
|
StringExtractorGDBRemote::eServerPacketType_QPassSignals,
|
||||||
|
&GDBRemoteCommunicationServerLLGS::Handle_QPassSignals);
|
||||||
|
|
||||||
RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k,
|
RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k,
|
||||||
[this](StringExtractorGDBRemote packet, Error &error,
|
[this](StringExtractorGDBRemote packet, Error &error,
|
||||||
|
@ -3072,6 +3075,40 @@ GDBRemoteCommunicationServerLLGS::Handle_qFileLoadAddress(
|
||||||
return SendPacketNoLock(response.GetString());
|
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() {
|
void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() {
|
||||||
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
|
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,8 @@ protected:
|
||||||
|
|
||||||
PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet);
|
PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet);
|
||||||
|
|
||||||
|
PacketResult Handle_QPassSignals(StringExtractorGDBRemote &packet);
|
||||||
|
|
||||||
void SetCurrentThreadID(lldb::tid_t tid);
|
void SetCurrentThreadID(lldb::tid_t tid);
|
||||||
|
|
||||||
lldb::tid_t GetCurrentThreadID() const;
|
lldb::tid_t GetCurrentThreadID() const;
|
||||||
|
|
|
@ -91,6 +91,10 @@ StringExtractorGDBRemote::GetServerPacketType() const {
|
||||||
return eServerPacketType_QEnvironmentHexEncoded;
|
return eServerPacketType_QEnvironmentHexEncoded;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
if (PACKET_STARTS_WITH("QPassSignals:"))
|
||||||
|
return eServerPacketType_QPassSignals;
|
||||||
|
|
||||||
case 'S':
|
case 'S':
|
||||||
if (PACKET_MATCHES("QStartNoAckMode"))
|
if (PACKET_MATCHES("QStartNoAckMode"))
|
||||||
return eServerPacketType_QStartNoAckMode;
|
return eServerPacketType_QStartNoAckMode;
|
||||||
|
|
|
@ -96,6 +96,7 @@ public:
|
||||||
// debug server packages
|
// debug server packages
|
||||||
eServerPacketType_QEnvironmentHexEncoded,
|
eServerPacketType_QEnvironmentHexEncoded,
|
||||||
eServerPacketType_QListThreadsInStopReply,
|
eServerPacketType_QListThreadsInStopReply,
|
||||||
|
eServerPacketType_QPassSignals,
|
||||||
eServerPacketType_QRestoreRegisterState,
|
eServerPacketType_QRestoreRegisterState,
|
||||||
eServerPacketType_QSaveRegisterState,
|
eServerPacketType_QSaveRegisterState,
|
||||||
eServerPacketType_QSetLogging,
|
eServerPacketType_QSetLogging,
|
||||||
|
|
Loading…
Reference in New Issue