Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -95,9 +95,11 @@ std::recursive_mutex m_debugged_process_mutex; std::unordered_map> m_debugged_processes; + std::unique_ptr m_last_exited_process; Communication m_stdio_communication; MainLoop::ReadHandleUP m_stdio_handle_up; + int m_stdio_handle_refcount = 0; lldb::StateType m_inferior_prev_state = lldb::StateType::eStateInvalid; llvm::StringMap> m_xfer_buffer_map; Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -1002,13 +1002,26 @@ __FUNCTION__, process->GetID()); } - // Close the pipe to the inferior terminal i/o if we launched it and set one - // up. - MaybeCloseInferiorTerminalConnection(); + if (m_current_process == process) + m_current_process = nullptr; + if (m_continue_process == process) + m_continue_process = nullptr; + auto process_it = m_debugged_processes.find(process->GetID()); + assert(process_it != m_debugged_processes.end()); + // Keep a reference to prevent the instance from being destroyed before + // state change notification finishes. + m_last_exited_process = std::move(process_it->second); + m_debugged_processes.erase(process_it); - // We are ready to exit the debug monitor. - m_exit_now = true; - m_mainloop.RequestTermination(); + if (m_debugged_processes.empty()) { + // Close the pipe to the inferior terminal i/o if we launched it and set one + // up. + MaybeCloseInferiorTerminalConnection(); + + // We are ready to exit the debug monitor. + m_exit_now = true; + m_mainloop.RequestTermination(); + } } void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped( @@ -1171,12 +1184,19 @@ } void GDBRemoteCommunicationServerLLGS::StartSTDIOForwarding() { + Log *log = GetLog(LLDBLog::Process); + // Don't forward if not connected (e.g. when attaching). if (!m_stdio_communication.IsConnected()) return; + // If we're not debugging multiple processes, this should be called + // once. + if (!bool(m_extensions_supported & + NativeProcessProtocol::Extension::multiprocess)) + assert(!m_stdio_handle_up); + Status error; - lldbassert(!m_stdio_handle_up); m_stdio_handle_up = m_mainloop.RegisterReadObject( m_stdio_communication.GetConnection()->GetReadObject(), [this](MainLoopBase &) { SendProcessOutput(); }, error); @@ -1184,15 +1204,27 @@ if (!m_stdio_handle_up) { // Not much we can do about the failure. Log it and continue without // forwarding. - if (Log *log = GetLog(LLDBLog::Process)) - LLDB_LOGF(log, - "GDBRemoteCommunicationServerLLGS::%s Failed to set up stdio " - "forwarding: %s", - __FUNCTION__, error.AsCString()); + LLDB_LOG(log, "Failed to set up stdio forwarding: {0}", error); } } void GDBRemoteCommunicationServerLLGS::StopSTDIOForwarding() { + Log *log = GetLog(LLDBLog::Process); + + if (bool(m_extensions_supported & + NativeProcessProtocol::Extension::multiprocess)) { + // Leave stdio forwarding open if there's at least one running process + // still. + for (auto &x : m_debugged_processes) { + if (x.second->IsRunning()) { + LLDB_LOG(log, + "process {0} is still running, stdio forwarding stays open", + x.first); + return; + } + } + } + m_stdio_handle_up.reset(); } @@ -1373,15 +1405,19 @@ StopSTDIOForwarding(); - if (!m_current_process) { + if (m_debugged_processes.empty()) { LLDB_LOG(log, "No debugged process found."); return PacketResult::Success; } - Status error = m_current_process->Kill(); - if (error.Fail()) - LLDB_LOG(log, "Failed to kill debugged process {0}: {1}", - m_current_process->GetID(), error); + for (auto it = m_debugged_processes.begin(); it != m_debugged_processes.end(); + ++it) { + LLDB_LOG(log, "Killing process {0}", it->first); + Status error = it->second->Kill(); + if (error.Fail()) + LLDB_LOG(log, "Failed to kill debugged process {0}: {1}", it->first, + error); + } // No OK response for kill packet. // return SendOKResponse (); Index: lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py =================================================================== --- lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py +++ lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py @@ -263,3 +263,37 @@ "send packet: $Eff#00", ], True) self.expect_gdbremote_sequence() + + @add_test_categories(["fork"]) + def test_kill_all(self): + 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"] + child_pid = ret["child_pid"] + self.reset_test_sequence() + + exit_regex = "[$]X09;process:([0-9a-f]+)#.*" + self.test_sequence.add_log_lines([ + # kill all processes + "read packet: $k#00", + {"direction": "send", "regex": exit_regex, + "capture": {1: "pid1"}}, + {"direction": "send", "regex": exit_regex, + "capture": {1: "pid2"}}, + ], True) + ret = self.expect_gdbremote_sequence() + self.assertEqual(set([ret["pid1"], ret["pid2"]]), + set([parent_pid, child_pid]))