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/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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -96,6 +96,7 @@ public:
|
|||
// debug server packages
|
||||
eServerPacketType_QEnvironmentHexEncoded,
|
||||
eServerPacketType_QListThreadsInStopReply,
|
||||
eServerPacketType_QPassSignals,
|
||||
eServerPacketType_QRestoreRegisterState,
|
||||
eServerPacketType_QSaveRegisterState,
|
||||
eServerPacketType_QSetLogging,
|
||||
|
|
Loading…
Reference in New Issue