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 @@ -1107,14 +1107,16 @@ SendProcessOutput(); // Then stop the forwarding, so that any late output (see llvm.org/pr25652) // does not interfere with our protocol. - StopSTDIOForwarding(); + if (!m_non_stop) + StopSTDIOForwarding(); HandleInferiorState_Stopped(process); break; case StateType::eStateExited: // Same as above SendProcessOutput(); - StopSTDIOForwarding(); + if (!m_non_stop) + StopSTDIOForwarding(); HandleInferiorState_Exited(process); break; @@ -1417,7 +1419,8 @@ GDBRemoteCommunicationServerLLGS::Handle_k(StringExtractorGDBRemote &packet) { Log *log = GetLog(LLDBLog::Process); - StopSTDIOForwarding(); + if (!m_non_stop) + StopSTDIOForwarding(); if (m_debugged_processes.empty()) { LLDB_LOG(log, "No debugged process found."); @@ -1443,7 +1446,8 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_vKill( StringExtractorGDBRemote &packet) { - StopSTDIOForwarding(); + if (!m_non_stop) + StopSTDIOForwarding(); packet.SetFilePos(6); // vKill; uint32_t pid = packet.GetU32(LLDB_INVALID_PROCESS_ID, 16); @@ -3519,7 +3523,8 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) { Log *log = GetLog(LLDBLog::Process); - StopSTDIOForwarding(); + if (!m_non_stop) + StopSTDIOForwarding(); lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; @@ -3918,6 +3923,8 @@ assert(packet_str.startswith("QNonStop:")); packet_str.consume_front("QNonStop:"); if (packet_str == "0") { + if (m_non_stop) + StopSTDIOForwarding(); for (const auto &process_it : m_debugged_processes) { if (process_it.second->IsRunning()) { Status error = process_it.second->Interrupt(); @@ -3935,6 +3942,8 @@ m_stop_notification_queue.clear(); m_non_stop = false; } else if (packet_str == "1") { + if (!m_non_stop) + StartSTDIOForwarding(); m_non_stop = true; } else return SendErrorResponse(Status("Invalid QNonStop packet")); @@ -4228,9 +4237,10 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendContinueSuccessResponse() { - // TODO: how to handle forwarding in non-stop mode? + if (m_non_stop) + return SendOKResponse(); StartSTDIOForwarding(); - return m_non_stop ? SendOKResponse() : PacketResult::Success; + return PacketResult::Success; } void GDBRemoteCommunicationServerLLGS::AppendThreadIDToResponse( 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 @@ -1,3 +1,5 @@ +import binascii + from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -107,3 +109,31 @@ def test_vCont_interspersed_nonstop(self): self.resume_one_test(run_order=["parent", "child", "parent", "child"], use_vCont=True, nonstop=True) + + @add_test_categories(["fork"]) + def test_c_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: $Hcp{}.{}#00".format(parent_pid, parent_tid), + "send packet: $OK#00", + "read packet: $c#00", + "send packet: $OK#00", + "read packet: $Hcp{}.{}#00".format(child_pid, child_tid), + "send packet: $OK#00", + "read packet: $c#00", + "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"]) Index: lldb/test/API/tools/lldb-server/main.cpp =================================================================== --- lldb/test/API/tools/lldb-server/main.cpp +++ lldb/test/API/tools/lldb-server/main.cpp @@ -10,8 +10,10 @@ #include #if !defined(_WIN32) #include +#include #include #include +#include #endif #include "thread.h" #include @@ -26,6 +28,13 @@ #include #endif +#if !defined(_WIN32) +struct semaphores { + sem_t parent_ready; + sem_t child_ready; +}; +#endif + static const char *const PRINT_PID_COMMAND = "print-pid"; static bool g_print_thread_ids = false; @@ -224,6 +233,9 @@ int return_value = 0; #if !defined(_WIN32) + semaphores *sem = nullptr; + bool is_child = false; + // Set the signal handler. sig_t sig_result = signal(SIGALRM, signal_handler); if (sig_result == SIG_ERR) { @@ -324,10 +336,29 @@ func_p(); #if !defined(_WIN32) && !defined(TARGET_OS_WATCH) && !defined(TARGET_OS_TV) } else if (arg == "fork") { - assert (fork() != -1); + // Prepare the semaphores for parent-child synchronization. + if (sem == nullptr) { + sem = static_cast(mmap(nullptr, sizeof(*sem), + PROT_READ | PROT_WRITE, + MAP_ANON | MAP_SHARED, -1, 0)); + assert(sem); + assert(sem_init(&sem->parent_ready, 1, 0) == 0); + assert(sem_init(&sem->child_ready, 1, 0) == 0); + } + + pid_t fork_pid = fork(); + assert(fork_pid != -1); + is_child = fork_pid == 0; } else if (arg == "vfork") { if (vfork() == 0) _exit(0); + } else if (arg == "process:sync") { + // this is only valid after fork + assert(sem); + sem_t *my_sem = is_child ? &sem->child_ready : &sem->parent_ready; + sem_t *other_sem = !is_child ? &sem->child_ready : &sem->parent_ready; + assert(sem_post(my_sem) == 0); + assert(sem_wait(other_sem) == 0); #endif } else if (consume_front(arg, "thread:new")) { std::promise promise; @@ -368,5 +399,13 @@ it != threads.end(); ++it) it->join(); + if (sem != nullptr) { + if (!is_child) { + assert(sem_destroy(&sem->child_ready)); + assert(sem_destroy(&sem->parent_ready)); + } + assert(munmap(sem, sizeof(*sem)) == 0); + } + return return_value; }