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 @@ -1751,6 +1751,7 @@ break; } + // If there's no thread-id (e.g. "vCont;c"), it's "p-1.-1". lldb::pid_t pid = StringExtractorGDBRemote::AllProcesses; lldb::tid_t tid = StringExtractorGDBRemote::AllThreads; @@ -1759,37 +1760,40 @@ // Consume the separator. packet.GetChar(); - auto pid_tid = packet.GetPidTid(StringExtractorGDBRemote::AllProcesses); + auto pid_tid = packet.GetPidTid(LLDB_INVALID_PROCESS_ID); if (!pid_tid) return SendIllFormedResponse(packet, "Malformed thread-id"); pid = pid_tid->first; tid = pid_tid->second; - } - if (pid == StringExtractorGDBRemote::AllProcesses) { - if (m_debugged_processes.size() > 1) - return SendIllFormedResponse( - packet, "Resuming multiple processes not supported yet"); - if (!m_continue_process) { - LLDB_LOG(log, "no debugged process"); - return SendErrorResponse(0x36); + // If we get TID without PID, it's the current process. + if (pid == LLDB_INVALID_PROCESS_ID) { + if (!m_continue_process) { + LLDB_LOG(log, "no process selected via Hc"); + return SendErrorResponse(0x36); + } + pid = m_continue_process->GetID(); } - pid = m_continue_process->GetID(); } + assert(pid != LLDB_INVALID_PROCESS_ID); if (tid == StringExtractorGDBRemote::AllThreads) tid = LLDB_INVALID_THREAD_ID; - thread_action.tid = tid; - thread_actions[pid].Append(thread_action); + if (pid == StringExtractorGDBRemote::AllProcesses) { + for (auto &process_it : m_debugged_processes) + thread_actions[process_it.first].Append(thread_action); + } else + thread_actions[pid].Append(thread_action); } assert(thread_actions.size() >= 1); - if (thread_actions.size() > 1) + if (thread_actions.size() > 1 && !m_non_stop) return SendIllFormedResponse( - packet, "Resuming multiple processes not supported yet"); + packet, + "Resuming multiple processes is supported in non-stop mode only"); for (std::pair x : thread_actions) { auto process_it = m_debugged_processes.find(x.first); Index: lldb/test/API/tools/lldb-server/TestGdbRemoteForkNonStop.py =================================================================== --- lldb/test/API/tools/lldb-server/TestGdbRemoteForkNonStop.py +++ lldb/test/API/tools/lldb-server/TestGdbRemoteForkNonStop.py @@ -137,3 +137,55 @@ ret["O_content"]) self.assertIn("PID: {}".format(int(child_pid, 16)).encode(), ret["O_content"]) + + @add_test_categories(["fork"]) + def test_vCont_both_nonstop(self): + parent_pid, parent_tid, child_pid, child_tid = ( + self.start_fork_test(["fork", "process:sync", "print-pid", + "process:sync", "stop"], + nonstop=True)) + + self.test_sequence.add_log_lines([ + "read packet: $vCont;c:p{}.{};c:p{}.{}#00".format( + parent_pid, parent_tid, child_pid, child_tid), + "send packet: $OK#00", + {"direction": "send", "regex": "%Stop:T.*"}, + # see the comment in TestNonStop.py, test_stdio + "read packet: $vStdio#00", + "read packet: $vStdio#00", + "send packet: $OK#00", + ], True) + ret = self.expect_gdbremote_sequence() + self.assertIn("PID: {}".format(int(parent_pid, 16)).encode(), + ret["O_content"]) + self.assertIn("PID: {}".format(int(child_pid, 16)).encode(), + ret["O_content"]) + + def vCont_both_nonstop_test(self, vCont_packet): + parent_pid, parent_tid, child_pid, child_tid = ( + self.start_fork_test(["fork", "process:sync", "print-pid", + "process:sync", "stop"], + nonstop=True)) + + self.test_sequence.add_log_lines([ + "read packet: ${}#00".format(vCont_packet), + "send packet: $OK#00", + {"direction": "send", "regex": "%Stop:T.*"}, + # see the comment in TestNonStop.py, test_stdio + "read packet: $vStdio#00", + "read packet: $vStdio#00", + "send packet: $OK#00", + ], True) + ret = self.expect_gdbremote_sequence() + self.assertIn("PID: {}".format(int(parent_pid, 16)).encode(), + ret["O_content"]) + self.assertIn("PID: {}".format(int(child_pid, 16)).encode(), + ret["O_content"]) + + @add_test_categories(["fork"]) + def test_vCont_both_implicit_nonstop(self): + self.vCont_both_nonstop_test("vCont;c") + + @add_test_categories(["fork"]) + def test_vCont_both_minus_one_nonstop(self): + self.vCont_both_nonstop_test("vCont;c:p-1.-1")