Index: lldb/packages/Python/lldbsuite/test/gdbclientutils.py =================================================================== --- lldb/packages/Python/lldbsuite/test/gdbclientutils.py +++ lldb/packages/Python/lldbsuite/test/gdbclientutils.py @@ -212,6 +212,8 @@ return self.vCtrlC() if packet == "vCont;t": return self.vContT() + if packet.startswith("vKill;"): + return self.vKill(int(packet[6:], 16)) if packet == "k": return self.k() @@ -363,6 +365,9 @@ def k(self): return ["W01", self.RESPONSE_DISCONNECT] + def vKill(self, pid): + return "" + """ Raised when we receive a packet for which there is no default action. Override the responder class to implement behavior suitable for the test at Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -4252,8 +4252,27 @@ llvm::Expected GDBRemoteCommunicationClient::KillProcess(lldb::pid_t pid) { StringExtractorGDBRemote response; + StreamString packet; GDBRemoteCommunication::ScopedTimeout(*this, seconds(3)); + if (m_supports_multiprocess == eLazyBoolYes && + pid != LLDB_INVALID_PROCESS_ID) { + packet.Format("vKill;{0:x-}", pid); + if (SendPacketAndWaitForResponse(packet.GetString(), response, + GetPacketTimeout()) != + PacketResult::Success) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "failed to send vKill packet"); + + if (response.IsOKResponse()) + return SIGKILL; + + if (!response.IsUnsupportedResponse()) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "unexpected response to vKill packet: %s", + response.GetStringRef().str().c_str()); + } + if (SendPacketAndWaitForResponse("k", response, GetPacketTimeout()) != PacketResult::Success) return llvm::createStringError(llvm::inconvertibleErrorCode(), Index: lldb/test/API/functionalities/gdb_remote_client/TestMultiprocess.py =================================================================== --- lldb/test/API/functionalities/gdb_remote_client/TestMultiprocess.py +++ lldb/test/API/functionalities/gdb_remote_client/TestMultiprocess.py @@ -46,3 +46,97 @@ lldb.eStopReasonSignal) self.assertEqual(process.GetThreadByID(0x10204).stop_reason, lldb.eStopReasonNone) + + def test_kill_no_mp(self): + class MyResponder(MockGDBServerResponder): + got_vKill = False + + def qfThreadInfo(self): + return "mp100.100" + + def vKill(self, pid): + self.got_vKill = True + return "E01" + + def k(self): + return "X01" + + self.server.responder = MyResponder() + target = self.dbg.CreateTarget("") + process = self.connect(target) + lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, + [lldb.eStateStopped]) + process.Kill() + lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, + [lldb.eStateExited]) + self.assertPacketLogContains(["k"]) + self.assertFalse(self.server.responder.got_vKill) + self.assertEqual(process.GetExitStatus(), 1) + + def test_kill_mp(self): + class MyResponder(MockGDBServerResponder): + got_k = False + + def __init__(self, test_case): + super().__init__() + self.test_case = test_case + + def qSupported(self, client_supported): + self.test_case.assertIn("multiprocess+", client_supported) + return "multiprocess+;" + super().qSupported(client_supported) + + def qfThreadInfo(self): + return "mp100.100" + + def vKill(self, pid): + return "OK" + + def k(self): + self.got_k = True + return "X00" + + self.server.responder = MyResponder(self) + target = self.dbg.CreateTarget("") + process = self.connect(target) + lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, + [lldb.eStateStopped]) + process.Kill() + lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, + [lldb.eStateExited]) + self.assertPacketLogContains(["vKill;100"]) + self.assertFalse(self.server.responder.got_k) + self.assertEqual(process.GetExitStatus(), + lldbutil.get_signal_number("SIGKILL")) + + def test_kill_mp_no_vKill(self): + class MyResponder(MockGDBServerResponder): + def __init__(self, test_case): + super().__init__() + self.test_case = test_case + + def qSupported(self, client_supported): + self.test_case.assertIn("multiprocess+", client_supported) + return "multiprocess+;" + super().qSupported(client_supported) + + def qC(self): + return "QC100" + + def qfThreadInfo(self): + return "mp100.100" + + def cont(self): + return "S02" + + def k(self): + return "X01" + + self.server.responder = MyResponder(self) + target = self.dbg.CreateTarget("") + process = self.connect(target) + lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, + [lldb.eStateStopped]) + process.Kill() + lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, + [lldb.eStateExited]) + self.assertPacketLogContains(["vKill;100", "k"]) + self.assertEqual(process.GetExitStatus(), 1)