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 @@ -1552,6 +1552,14 @@ packet, "unexpected content after $C{signal-number}"); } + // In non-stop protocol mode, the process could be running already. + // We do not support resuming threads independently, so just error out. + if (!m_continue_process->CanResume()) { + LLDB_LOG(log, "process cannot be resumed (state={0})", + m_continue_process->GetState()); + return SendErrorResponse(0x37); + } + ResumeActionList resume_actions(StateType::eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); Status error; @@ -1623,6 +1631,14 @@ return SendErrorResponse(0x36); } + // In non-stop protocol mode, the process could be running already. + // We do not support resuming threads independently, so just error out. + if (!m_continue_process->CanResume()) { + LLDB_LOG(log, "process cannot be resumed (state={0})", + m_continue_process->GetState()); + return SendErrorResponse(0x37); + } + // Build the ResumeActionList ResumeActionList actions(StateType::eStateRunning, LLDB_INVALID_SIGNAL_NUMBER); @@ -1648,6 +1664,23 @@ return SendPacketNoLock(response.GetString()); } +static bool ResumeActionListStopsAllThreads(ResumeActionList &actions, + NativeProcessProtocol &process) { + for (NativeThreadProtocol &thread : process.Threads()) { + const ResumeAction *action = + actions.GetActionForThread(thread.GetID(), true); + // If there's at least one non-"t" action, we're not stopping them all. + if (action && action->state != eStateSuspended) + return false; + // If there's no action (i.e. no change) and the thread is running + // currently, it would remain running. + if (!action && thread.GetState() != eStateStopped && + thread.GetState() != eStateSuspended) + return false; + } + return true; +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_vCont( StringExtractorGDBRemote &packet) { @@ -1669,9 +1702,6 @@ // Move past the ';', then do a simple 's'. packet.SetFilePos(packet.GetFilePos() + 1); return Handle_s(packet); - } else if (m_non_stop && ::strcmp(packet.Peek(), ";t") == 0) { - // TODO: add full support for "t" action - return SendOKResponse(); } std::unordered_map thread_actions; @@ -1770,13 +1800,51 @@ return SendErrorResponse(GDBRemoteServerError::eErrorResume); } - Status error = process_it->second->Resume(x.second); - if (error.Fail()) { - LLDB_LOG(log, "vCont failed for process {0}: {1}", x.first, error); - return SendErrorResponse(GDBRemoteServerError::eErrorResume); - } + // There are four possible scenarios here. These are: + // 1. vCont on a stopped process that resumes at least one thread. + // In this case, we call Resume(). + // 2. vCont on a stopped process that leaves all threads suspended. + // A no-op. + // 3. vCont on a running process that requests suspending all + // running threads. In this case, we call Interrupt(). + // 4. vCont on a running process that requests suspending a subset + // of running threads or resuming a subset of suspended threads. + // Since we do not support full nonstop mode, this is unsupported + // and we return an error. + + assert(process_it->second); + if (ResumeActionListStopsAllThreads(x.second, *process_it->second)) { + if (process_it->second->IsRunning()) { + assert(m_non_stop); + + Status error = process_it->second->Interrupt(); + if (error.Fail()) { + LLDB_LOG(log, "vCont failed to halt process {0}: {1}", x.first, error); + return SendErrorResponse(GDBRemoteServerError::eErrorResume); + } - LLDB_LOG(log, "continued process {0}", x.first); + LLDB_LOG(log, "halted process {0}", x.first); + + // hack to avoid enabling stdio forwarding after stop + // TODO: remove this when we improve stdio forwarding for nonstop + assert(thread_actions.size() == 1); + return SendOKResponse(); + } + } else { + if (!process_it->second->CanResume()) { + LLDB_LOG(log, "process cannot be resumed (pid={0}, state={1})", + process_it->first, process_it->second->GetState()); + return SendErrorResponse(0x37); + } + + Status error = process_it->second->Resume(x.second); + if (error.Fail()) { + LLDB_LOG(log, "vCont failed for process {0}: {1}", x.first, error); + return SendErrorResponse(GDBRemoteServerError::eErrorResume); + } + + LLDB_LOG(log, "continued process {0}", x.first); + } } return SendContinueSuccessResponse(); @@ -2921,6 +2989,14 @@ return SendErrorResponse(0x32); } + // In non-stop protocol mode, the process could be running already. + // We do not support resuming threads independently, so just error out. + if (!m_continue_process->CanResume()) { + LLDB_LOG(log, "process cannot be resumed (state={0})", + m_continue_process->GetState()); + return SendErrorResponse(0x37); + } + // We first try to use a continue thread id. If any one or any all set, use // the current thread. Bail out if we don't have a thread id. lldb::tid_t tid = GetContinueThreadID(); 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 @@ -170,3 +170,128 @@ "send packet: $OK#00", ], True) self.expect_gdbremote_sequence() + + def multiple_resume_test(self, second_command): + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["sleep:15"]) + self.test_sequence.add_log_lines( + ["read packet: $QNonStop:1#00", + "send packet: $OK#00", + "read packet: $c#63", + "send packet: $OK#00", + "read packet: ${}#00".format(second_command), + "send packet: $E37#00", + ], True) + self.expect_gdbremote_sequence() + + @add_test_categories(["llgs"]) + def test_multiple_C(self): + self.multiple_resume_test("C05") + + @add_test_categories(["llgs"]) + def test_multiple_c(self): + self.multiple_resume_test("c") + + @add_test_categories(["llgs"]) + def test_multiple_s(self): + self.multiple_resume_test("s") + + @add_test_categories(["llgs"]) + def test_multiple_vCont(self): + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["thread:new", "trap", "sleep:15"]) + self.test_sequence.add_log_lines( + ["read packet: $QNonStop:1#00", + "send packet: $OK#00", + "read packet: $c#63", + "send packet: $OK#00", + {"direction": "send", + "regex": r"^%Stop:T[0-9a-fA-F]{2}thread:([0-9a-fA-F]+);", + "capture": {1: "tid1"}, + }, + "read packet: $vStopped#63", + {"direction": "send", + "regex": r"^[$]T[0-9a-fA-F]{2}thread:([0-9a-fA-F]+);", + "capture": {1: "tid2"}, + }, + "read packet: $vStopped#63", + "send packet: $OK#00", + ], True) + ret = self.expect_gdbremote_sequence() + + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $vCont;c:{}#00".format(ret["tid1"]), + "send packet: $OK#00", + "read packet: $vCont;c:{}#00".format(ret["tid2"]), + "send packet: $E37#00", + ], True) + self.expect_gdbremote_sequence() + + @add_test_categories(["llgs"]) + def test_vCont_then_stop(self): + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["sleep:15"]) + self.test_sequence.add_log_lines( + ["read packet: $QNonStop:1#00", + "send packet: $OK#00", + "read packet: $c#63", + "send packet: $OK#00", + "read packet: $vCont;t#00", + "send packet: $OK#00", + ], True) + self.expect_gdbremote_sequence() + + def vCont_then_partial_stop_test(self, run_both): + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["thread:new", "trap", "sleep:15"]) + self.test_sequence.add_log_lines( + ["read packet: $QNonStop:1#00", + "send packet: $OK#00", + "read packet: $c#63", + "send packet: $OK#00", + {"direction": "send", + "regex": r"^%Stop:T[0-9a-fA-F]{2}thread:([0-9a-fA-F]+);", + "capture": {1: "tid1"}, + }, + "read packet: $vStopped#63", + {"direction": "send", + "regex": r"^[$]T[0-9a-fA-F]{2}thread:([0-9a-fA-F]+);", + "capture": {1: "tid2"}, + }, + "read packet: $vStopped#63", + "send packet: $OK#00", + ], True) + ret = self.expect_gdbremote_sequence() + + self.reset_test_sequence() + if run_both: + self.test_sequence.add_log_lines( + ["read packet: $vCont;c#00", + ], True) + else: + self.test_sequence.add_log_lines( + ["read packet: $vCont;c:{}#00".format(ret["tid1"]), + ], True) + self.test_sequence.add_log_lines( + ["send packet: $OK#00", + "read packet: $vCont;t:{}#00".format(ret["tid2"]), + "send packet: $E37#00", + ], True) + self.expect_gdbremote_sequence() + + @add_test_categories(["llgs"]) + def test_vCont_then_partial_stop(self): + self.vCont_then_partial_stop_test(False) + + @add_test_categories(["llgs"]) + def test_vCont_then_partial_stop_run_both(self): + self.vCont_then_partial_stop_test(True)