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 @@ -88,8 +88,9 @@ struct DebuggedProcess { enum class Flag { vkilled = (1u << 0), + stopping_due_to_qnonstop = (1u << 1), - LLVM_MARK_AS_BITMASK_ENUM(vkilled) + LLVM_MARK_AS_BITMASK_ENUM(stopping_due_to_qnonstop) }; std::unique_ptr process_up; 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 @@ -1920,6 +1920,24 @@ bool force_synchronous) { Log *log = GetLog(LLDBLog::Process); + DebuggedProcess &process_info = m_debugged_processes.at(process.GetID()); + if (bool(process_info.flags & + DebuggedProcess::Flag::stopping_due_to_qnonstop)) { + process_info.flags &= ~DebuggedProcess::Flag::stopping_due_to_qnonstop; + + // Check if we are waiting for any more processes to stop. If we are, + // do not send the OK response yet. + for (const auto &it : m_debugged_processes) { + if (bool(it.second.flags & + DebuggedProcess::Flag::stopping_due_to_qnonstop)) + return PacketResult::Success; + } + + // If all expected processes were stopped after a QNonStop:0 request, + // send the OK response. + return SendOKResponse(); + } + switch (process_state) { case eStateAttaching: case eStateLaunching: @@ -3917,12 +3935,35 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_QNonStop( StringExtractorGDBRemote &packet) { + Log *log = GetLog(LLDBLog::Process); + StringRef packet_str{packet.GetStringRef()}; assert(packet_str.startswith("QNonStop:")); packet_str.consume_front("QNonStop:"); if (packet_str == "0") { + bool any_running = false; + for (auto &process_it : m_debugged_processes) { + if (process_it.second.process_up->IsRunning()) { + Status error = process_it.second.process_up->Interrupt(); + if (error.Fail()) { + LLDB_LOG(log, + "while disabling nonstop, failed to halt process {0}: {1}", + process_it.first, error); + return SendErrorResponse(0x41); + } + // we must not send stop reasons after QNonStop + process_it.second.flags |= + DebuggedProcess::Flag::stopping_due_to_qnonstop; + any_running = true; + } + } + m_stdio_notification_queue.clear(); + m_stop_notification_queue.clear(); m_non_stop = false; - // TODO: stop all threads + // If we are stopping anything, defer sending the OK response until we're + // done. + if (any_running) + return PacketResult::Success; } else if (packet_str == "1") { m_non_stop = true; } else Index: lldb/test/API/tools/lldb-server/TestNonStop.py =================================================================== --- lldb/test/API/tools/lldb-server/TestNonStop.py +++ lldb/test/API/tools/lldb-server/TestNonStop.py @@ -366,3 +366,33 @@ "send packet: $OK#00", ], True) self.expect_gdbremote_sequence() + + @add_test_categories(["llgs"]) + def test_leave_nonstop(self): + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["thread:new", "thread:new", "stop", "sleep:15"]) + self.test_sequence.add_log_lines( + ["read packet: $QNonStop:1#00", + "send packet: $OK#00", + # stop is used to synchronize starting threads + "read packet: $c#63", + "send packet: $OK#00", + {"direction": "send", "regex": "%Stop:T.*"}, + "read packet: $c#63", + "send packet: $OK#00", + # verify that the threads are running now + "read packet: $?#00", + "send packet: $OK#00", + "read packet: $QNonStop:0#00", + "send packet: $OK#00", + # we should issue some random request now to verify that the stub + # did not send stop reasons -- we may verify whether notification + # queue was cleared while at it + "read packet: $vStopped#00", + "send packet: $Eff#00", + "read packet: $?#00", + {"direction": "send", "regex": "[$]T.*"}, + ], True) + self.expect_gdbremote_sequence()