Skip to content

Commit

Permalink
[lldb] [llgs] Implement the vKill packet
Browse files Browse the repository at this point in the history
Implement the support for the vKill packet.  This is the modern packet
used by the GDB Remote Serial Protocol to kill one of the debugged
processes.  Unlike the `k` packet, it has well-defined semantics.

The `vKill` packet takes the PID of the process to kill, and always
replies with an `OK` reply (rather than the exit status, as LLGS does
for `k` packets at the moment).  Additionally, unlike the `k` packet
it does not cause the connection to be terminated once the last process
is killed — the client needs to close it explicitly.

Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.llvm.org/D127667
  • Loading branch information
mgorny committed Jun 24, 2022
1 parent e8fe7e9 commit c18784b
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 1 deletion.
1 change: 1 addition & 0 deletions lldb/include/lldb/Utility/StringExtractorGDBRemote.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class StringExtractorGDBRemote : public StringExtractor {
eServerPacketType_vAttachName,
eServerPacketType_vCont,
eServerPacketType_vCont_actions, // vCont?
eServerPacketType_vKill,
eServerPacketType_vRun,

eServerPacketType_stop_reason, // '?'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
return this->Handle_k(packet);
});

RegisterMemberFunctionHandler(
StringExtractorGDBRemote::eServerPacketType_vKill,
&GDBRemoteCommunicationServerLLGS::Handle_vKill);

RegisterMemberFunctionHandler(
StringExtractorGDBRemote::eServerPacketType_qLLDBSaveCore,
&GDBRemoteCommunicationServerLLGS::Handle_qSaveCore);
Expand Down Expand Up @@ -482,6 +486,10 @@ GDBRemoteCommunicationServerLLGS::SendWResponse(
LLDB_LOG(log, "pid = {0}, returning exit type {1}", process->GetID(),
*wait_status);

// If the process was killed through vKill, return "OK".
if (m_vkilled_processes.find(process->GetID()) != m_vkilled_processes.end())
return SendOKResponse();

StreamGDBRemote response;
response.Format("{0:g}", *wait_status);
if (bool(m_extensions_supported & NativeProcessProtocol::Extension::multiprocess))
Expand Down Expand Up @@ -1049,9 +1057,13 @@ void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited(
lldb::pid_t pid = process->GetID();
m_mainloop.AddPendingCallback([this, pid](MainLoopBase &loop) {
m_debugged_processes.erase(pid);
auto vkill_it = m_vkilled_processes.find(pid);
if (vkill_it != m_vkilled_processes.end())
m_vkilled_processes.erase(vkill_it);
// Terminate the main loop only if vKill has not been used.
// When running in non-stop mode, wait for the vStopped to clear
// the notification queue.
if (m_debugged_processes.empty() && !m_non_stop) {
else if (m_debugged_processes.empty() && !m_non_stop) {
// Close the pipe to the inferior terminal i/o if we launched it and set
// one up.
MaybeCloseInferiorTerminalConnection();
Expand Down Expand Up @@ -1442,6 +1454,30 @@ GDBRemoteCommunicationServerLLGS::Handle_k(StringExtractorGDBRemote &packet) {
return SendContinueSuccessResponse();
}

GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::Handle_vKill(
StringExtractorGDBRemote &packet) {
StopSTDIOForwarding();

packet.SetFilePos(6); // vKill;
uint32_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16);
if (pid == LLDB_INVALID_PROCESS_ID)
return SendIllFormedResponse(packet,
"vKill failed to parse the process id");

auto it = m_debugged_processes.find(pid);
if (it == m_debugged_processes.end())
return SendErrorResponse(42);

Status error = it->second->Kill();
if (error.Fail())
return SendErrorResponse(error.ToError());

// OK response is sent when the process dies.
m_vkilled_processes.insert(pid);
return PacketResult::Success;
}

GDBRemoteCommunication::PacketResult
GDBRemoteCommunicationServerLLGS::Handle_QSetDisableASLR(
StringExtractorGDBRemote &packet) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <mutex>
#include <unordered_map>
#include <unordered_set>

#include "lldb/Core/Communication.h"
#include "lldb/Host/MainLoop.h"
Expand Down Expand Up @@ -95,6 +96,7 @@ class GDBRemoteCommunicationServerLLGS
std::recursive_mutex m_debugged_process_mutex;
std::unordered_map<lldb::pid_t, std::unique_ptr<NativeProcessProtocol>>
m_debugged_processes;
std::unordered_set<lldb::pid_t> m_vkilled_processes;

Communication m_stdio_communication;
MainLoop::ReadHandleUP m_stdio_handle_up;
Expand Down Expand Up @@ -129,6 +131,8 @@ class GDBRemoteCommunicationServerLLGS

PacketResult Handle_k(StringExtractorGDBRemote &packet);

PacketResult Handle_vKill(StringExtractorGDBRemote &packet);

PacketResult Handle_qProcessInfo(StringExtractorGDBRemote &packet);

PacketResult Handle_qC(StringExtractorGDBRemote &packet);
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Utility/StringExtractorGDBRemote.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
return eServerPacketType_vCont;
if (PACKET_MATCHES("vCont?"))
return eServerPacketType_vCont_actions;
if (PACKET_STARTS_WITH("vKill;"))
return eServerPacketType_vKill;
if (PACKET_STARTS_WITH("vRun;"))
return eServerPacketType_vRun;
if (PACKET_MATCHES("vStopped"))
Expand Down
57 changes: 57 additions & 0 deletions lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,3 +296,60 @@ def test_kill_all(self):
ret = self.expect_gdbremote_sequence()
self.assertEqual(set([ret["pid1"], ret["pid2"]]),
set([parent_pid, child_pid]))

def vkill_test(self, kill_parent=False, kill_child=False):
assert kill_parent or kill_child
self.build()
self.prep_debug_monitor_and_inferior(inferior_args=["fork"])
self.add_qSupported_packets(["multiprocess+",
"fork-events+"])
ret = self.expect_gdbremote_sequence()
self.assertIn("fork-events+", ret["qSupported_response"])
self.reset_test_sequence()

# continue and expect fork
self.test_sequence.add_log_lines([
"read packet: $c#00",
{"direction": "send", "regex": self.fork_regex.format("fork"),
"capture": self.fork_capture},
], True)
ret = self.expect_gdbremote_sequence()
parent_pid = ret["parent_pid"]
parent_tid = ret["parent_tid"]
child_pid = ret["child_pid"]
child_tid = ret["child_tid"]
self.reset_test_sequence()

if kill_parent:
self.test_sequence.add_log_lines([
# kill the process
"read packet: $vKill;{}#00".format(parent_pid),
"send packet: $OK#00",
], True)
if kill_child:
self.test_sequence.add_log_lines([
# kill the process
"read packet: $vKill;{}#00".format(child_pid),
"send packet: $OK#00",
], True)
self.test_sequence.add_log_lines([
# check child PID/TID
"read packet: $Hgp{}.{}#00".format(child_pid, child_tid),
"send packet: ${}#00".format("Eff" if kill_child else "OK"),
# check parent PID/TID
"read packet: $Hgp{}.{}#00".format(parent_pid, parent_tid),
"send packet: ${}#00".format("Eff" if kill_parent else "OK"),
], True)
self.expect_gdbremote_sequence()

@add_test_categories(["fork"])
def test_vkill_child(self):
self.vkill_test(kill_child=True)

@add_test_categories(["fork"])
def test_vkill_parent(self):
self.vkill_test(kill_parent=True)

@add_test_categories(["fork"])
def test_vkill_both(self):
self.vkill_test(kill_parent=True, kill_child=True)

0 comments on commit c18784b

Please sign in to comment.