Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h =================================================================== --- lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -174,7 +174,10 @@ eServerPacketType_QMemTags, // write memory tags eServerPacketType_qLLDBSaveCore, - eServerPacketType_QSetIgnoredExceptions + eServerPacketType_QSetIgnoredExceptions, + eServerPacketType_QNonStop, + eServerPacketType_vStopped, + eServerPacketType_vCtrlC, }; ServerPacketType GetServerPacketType() const; Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py =================================================================== --- lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py +++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -851,6 +851,7 @@ "memory-tagging", "qSaveCore", "native-signals", + "QNonStop", ] def parse_qSupported_response(self, context): Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py =================================================================== --- lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py +++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py @@ -866,7 +866,7 @@ class Server(object): - _GDB_REMOTE_PACKET_REGEX = re.compile(br'^\$([^\#]*)#[0-9a-fA-F]{2}') + _GDB_REMOTE_PACKET_REGEX = re.compile(br'^[\$%]([^\#]*)#[0-9a-fA-F]{2}') class ChecksumMismatch(Exception): pass Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -183,6 +183,9 @@ CompressionType m_compression_type; PacketResult SendPacketNoLock(llvm::StringRef payload); + PacketResult SendNotificationPacketNoLock(llvm::StringRef notify_type, + std::deque& queue, + llvm::StringRef payload); PacketResult SendRawPacketNoLock(llvm::StringRef payload, bool skip_ack = false); Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -124,6 +124,29 @@ return SendRawPacketNoLock(packet_str); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunication::SendNotificationPacketNoLock( + llvm::StringRef notify_type, std::deque &queue, + llvm::StringRef payload) { + PacketResult ret = PacketResult::Success; + + // If there are no notification in the queue, send the notification + // packet. + if (queue.empty()) { + StreamString packet(0, 4, eByteOrderBig); + packet.PutChar('%'); + packet.Write(notify_type.data(), notify_type.size()); + packet.PutChar(':'); + packet.Write(payload.data(), payload.size()); + packet.PutChar('#'); + packet.PutHex8(CalculcateChecksum(payload)); + ret = SendRawPacketNoLock(packet.GetString(), true); + } + + queue.push_back(payload.str()); + return ret; +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet, bool skip_ack) { 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 @@ -106,6 +106,8 @@ uint32_t m_next_saved_registers_id = 1; bool m_thread_suffix_supported = false; bool m_list_threads_in_stop_reply = false; + bool m_non_stop = false; + std::deque m_stop_notification_queue; NativeProcessProtocol::Extension m_extensions_supported = {}; @@ -113,9 +115,13 @@ PacketResult SendWResponse(NativeProcessProtocol *process); - PacketResult SendStopReplyPacketForThread(lldb::tid_t tid); + StreamString PrepareStopReplyPacketForThread(NativeThreadProtocol &thread); - PacketResult SendStopReasonForState(lldb::StateType process_state); + PacketResult SendStopReplyPacketForThread(lldb::tid_t tid, + bool allow_async, bool queue_all_threads); + + PacketResult SendStopReasonForState(lldb::StateType process_state, + bool allow_async, bool queue_all_threads); PacketResult Handle_k(StringExtractorGDBRemote &packet); @@ -217,6 +223,12 @@ PacketResult Handle_qSaveCore(StringExtractorGDBRemote &packet); + PacketResult Handle_QNonStop(StringExtractorGDBRemote &packet); + + PacketResult Handle_vStopped(StringExtractorGDBRemote &packet); + + PacketResult Handle_vCtrlC(StringExtractorGDBRemote &packet); + PacketResult Handle_g(StringExtractorGDBRemote &packet); PacketResult Handle_qMemTags(StringExtractorGDBRemote &packet); 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 @@ -235,6 +235,16 @@ RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_qLLDBSaveCore, &GDBRemoteCommunicationServerLLGS::Handle_qSaveCore); + + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_QNonStop, + &GDBRemoteCommunicationServerLLGS::Handle_QNonStop); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vStopped, + &GDBRemoteCommunicationServerLLGS::Handle_vStopped); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vCtrlC, + &GDBRemoteCommunicationServerLLGS::Handle_vCtrlC); } void GDBRemoteCommunicationServerLLGS::SetLaunchInfo(const ProcessLaunchInfo &info) { @@ -474,7 +484,11 @@ StreamGDBRemote response; response.Format("{0:g}", *wait_status); - return SendPacketNoLock(response.GetString()); + if (m_non_stop) + return SendNotificationPacketNoLock("Stop", m_stop_notification_queue, + response.GetString()); + else + return SendPacketNoLock(response.GetString()); } static void AppendHexValue(StreamString &response, const uint8_t *buf, @@ -764,29 +778,20 @@ return threads_array; } -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( - lldb::tid_t tid) { +StreamString +GDBRemoteCommunicationServerLLGS::PrepareStopReplyPacketForThread( + NativeThreadProtocol &thread) { Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread); - // Ensure we have a debugged process. - if (!m_current_process || - (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) - return SendErrorResponse(50); - LLDB_LOG(log, "preparing packet for pid {0} tid {1}", - m_current_process->GetID(), tid); - - // Ensure we can get info on the given thread. - NativeThreadProtocol *thread = m_current_process->GetThreadByID(tid); - if (!thread) - return SendErrorResponse(51); + m_current_process->GetID(), thread.GetID()); // Grab the reason this thread stopped. + StreamString response; struct ThreadStopInfo tid_stop_info; std::string description; - if (!thread->GetStopReason(tid_stop_info, description)) - return SendErrorResponse(52); + if (!thread.GetStopReason(tid_stop_info, description)) + return response; // FIXME implement register handling for exec'd inferiors. // if (tid_stop_info.reason == eStopReasonExec) { @@ -794,24 +799,23 @@ // InitializeRegisters(force); // } - StreamString response; // Output the T packet with the thread response.PutChar('T'); int signum = tid_stop_info.details.signal.signo; LLDB_LOG( log, "pid {0}, tid {1}, got signal signo = {2}, reason = {3}, exc_type = {4}", - m_current_process->GetID(), tid, signum, int(tid_stop_info.reason), - tid_stop_info.details.exception.type); + m_current_process->GetID(), thread.GetID(), signum, + int(tid_stop_info.reason), tid_stop_info.details.exception.type); // Print the signal number. response.PutHex8(signum & 0xff); // Include the tid. - response.Printf("thread:%" PRIx64 ";", tid); + response.Printf("thread:%" PRIx64 ";", thread.GetID()); // Include the thread name if there is one. - const std::string thread_name = thread->GetName(); + const std::string thread_name = thread.GetName(); if (!thread_name.empty()) { size_t thread_name_len = thread_name.length(); @@ -875,7 +879,7 @@ char delimiter = ':'; for (NativeThreadProtocol *thread; (thread = m_current_process->GetThreadAtIndex(i)) != nullptr; ++i) { - NativeRegisterContext& reg_ctx = thread->GetRegisterContext(); + NativeRegisterContext ®_ctx = thread->GetRegisterContext(); uint32_t reg_to_read = reg_ctx.ConvertRegisterKindToRegisterNumber( eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); @@ -906,7 +910,7 @@ // // Grab the register context. - NativeRegisterContext& reg_ctx = thread->GetRegisterContext(); + NativeRegisterContext ®_ctx = thread.GetRegisterContext(); const auto expedited_regs = reg_ctx.GetExpeditedRegisters(ExpeditedRegs::Full); @@ -923,8 +927,9 @@ ®_value, lldb::eByteOrderBig); response.PutChar(';'); } else { - LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s failed to read " - "register '%s' index %" PRIu32 ": %s", + LLDB_LOGF(log, + "GDBRemoteCommunicationServerLLGS::%s failed to read " + "register '%s' index %" PRIu32 ": %s", __FUNCTION__, reg_info_p->name ? reg_info_p->name : "", reg_num, error.AsCString()); @@ -973,7 +978,51 @@ tid_stop_info.details.fork.child_tid); } - return SendPacketNoLock(response.GetString()); + return response; +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread( + lldb::tid_t tid, bool allow_async, bool queue_all_threads) { + // Ensure we have a debugged process. + if (!m_current_process || + (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse(50); + + // Ensure we can get info on the given thread. + NativeThreadProtocol *thread = m_current_process->GetThreadByID(tid); + if (!thread) + return SendErrorResponse(51); + + StreamString response = PrepareStopReplyPacketForThread(*thread); + if (response.Empty()) + return SendErrorResponse(42); + + PacketResult ret; + if (m_non_stop && allow_async) + ret = SendNotificationPacketNoLock("Stop", m_stop_notification_queue, + response.GetString()); + else + ret = SendPacketNoLock(response.GetString()); + + if (m_non_stop && queue_all_threads) { + // Special case: for !async (`?` packet), we also need to put + // the initial stop reason into the queue, even though it is sent + // as synchronous response. + if (!allow_async) + m_stop_notification_queue.push_back(response.GetString().str()); + + // Queue notification events for the remaining threads. + uint32_t thread_index = 0; + while (NativeThreadProtocol *listed_thread = + m_current_process->GetThreadAtIndex(thread_index++)) { + if (listed_thread->GetID() != thread->GetID()) + m_stop_notification_queue.push_back( + PrepareStopReplyPacketForThread(*listed_thread).GetString().str()); + } + } + + return ret; } void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited( @@ -983,7 +1032,8 @@ Log *log = GetLog(LLDBLog::Process); LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__); - PacketResult result = SendStopReasonForState(StateType::eStateExited); + PacketResult result = + SendStopReasonForState(StateType::eStateExited, true, false); if (result != PacketResult::Success) { LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s failed to send stop " @@ -995,9 +1045,13 @@ // up. MaybeCloseInferiorTerminalConnection(); - // We are ready to exit the debug monitor. - m_exit_now = true; - m_mainloop.RequestTermination(); + // When running in non-stop mode, wait for the vStopped to clear + // the notification queue. + if (!m_non_stop) { + // We are ready to exit the debug monitor. + m_exit_now = true; + m_mainloop.RequestTermination(); + } } void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Stopped( @@ -1015,7 +1069,8 @@ break; default: // In all other cases, send the stop reason. - PacketResult result = SendStopReasonForState(StateType::eStateStopped); + PacketResult result = + SendStopReasonForState(StateType::eStateStopped, true, true); if (result != PacketResult::Success) { LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s failed to send stop " @@ -1371,9 +1426,11 @@ LLDB_LOG(log, "Failed to kill debugged process {0}: {1}", m_current_process->GetID(), error); - // No OK response for kill packet. - // return SendOKResponse (); - return PacketResult::Success; + // The response to kill packet is undefined per the spec. LLDB + // follows the same rules as for continue packets, i.e. no response + // in all-stop mode, and "OK" in non-stop mode; in both cases this + // is followed by the actual stop reason. + return m_non_stop ? SendOKResponse() : PacketResult::Success; } GDBRemoteCommunication::PacketResult @@ -1501,8 +1558,9 @@ return SendErrorResponse(0x38); } - // Don't send an "OK" packet; response is the stopped/exited message. - return PacketResult::Success; + // Don't send an "OK" packet, except in non-stop mode; + // otherwise, the response is the stopped/exited message. + return m_non_stop ? SendOKResponse() : PacketResult::Success; } GDBRemoteCommunication::PacketResult @@ -1541,8 +1599,9 @@ } LLDB_LOG(log, "continued process {0}", m_continue_process->GetID()); - // No response required from continue. - return PacketResult::Success; + + // No response required from continue, unless in non-stop mode. + return m_non_stop ? SendOKResponse() : PacketResult::Success; } GDBRemoteCommunication::PacketResult @@ -1580,6 +1639,9 @@ // 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(); } // Ensure we have a native process. @@ -1657,8 +1719,9 @@ } LLDB_LOG(log, "continued process {0}", m_continue_process->GetID()); - // No response required from vCont. - return PacketResult::Success; + + // No response required from vCont, unless in non-stop mode. + return m_non_stop ? SendOKResponse() : PacketResult::Success; } void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) { @@ -1686,12 +1749,15 @@ if (!m_current_process) return SendErrorResponse(02); - return SendStopReasonForState(m_current_process->GetState()); + // Clear the notification queue first. + m_stop_notification_queue.clear(); + + return SendStopReasonForState(m_current_process->GetState(), false, true); } GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendStopReasonForState( - lldb::StateType process_state) { + lldb::StateType process_state, bool allow_async, bool queue_all_threads) { Log *log = GetLog(LLDBLog::Process); switch (process_state) { @@ -1712,7 +1778,7 @@ // Make sure we set the current thread so g and p packets return the data // the gdb will expect. SetCurrentThreadID(tid); - return SendStopReplyPacketForThread(tid); + return SendStopReplyPacketForThread(tid, allow_async, queue_all_threads); } case eStateInvalid: @@ -2804,8 +2870,9 @@ return SendErrorResponse(0x49); } - // No response here - the stop or exit will come from the resulting action. - return PacketResult::Success; + // No response here, unless in non-stop mode. + // Otherwise, the stop or exit will come from the resulting action. + return m_non_stop ? SendOKResponse() : PacketResult::Success; } llvm::Expected> @@ -3172,7 +3239,7 @@ } // Notify we attached by sending a stop packet. - return SendStopReasonForState(m_current_process->GetState()); + return SendStopReasonForState(m_current_process->GetState(), true, true); } GDBRemoteCommunication::PacketResult @@ -3202,7 +3269,7 @@ } // Notify we attached by sending a stop packet. - return SendStopReasonForState(m_current_process->GetState()); + return SendStopReasonForState(m_current_process->GetState(), true, true); } GDBRemoteCommunication::PacketResult @@ -3238,7 +3305,7 @@ } // Notify we attached by sending a stop packet. - return SendStopReasonForState(m_current_process->GetState()); + return SendStopReasonForState(m_current_process->GetState(), true, true); } GDBRemoteCommunication::PacketResult @@ -3267,7 +3334,8 @@ m_process_launch_info.GetArguments()[0].ref(), FileSpec::Style::native); m_process_launch_error = LaunchProcess(); if (m_process_launch_error.Success()) - return SendStopReasonForState(m_current_process->GetState()); + return SendStopReasonForState(m_current_process->GetState(), false, + false); LLDB_LOG(log, "failed to launch exe: {0}", m_process_launch_error); } return SendErrorResponse(8); @@ -3334,7 +3402,7 @@ __FUNCTION__, packet.GetStringRef().data()); return SendErrorResponse(0x15); } - return SendStopReplyPacketForThread(tid); + return SendStopReplyPacketForThread(tid, false, false); } GDBRemoteCommunication::PacketResult @@ -3657,6 +3725,53 @@ return SendPacketNoLock(response.GetString()); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_QNonStop( + StringExtractorGDBRemote &packet) { + StringRef packet_str{packet.GetStringRef()}; + assert(packet_str.startswith("QNonStop:")); + packet_str.consume_front("QNonStop:"); + if (packet_str == "0") { + m_non_stop = false; + // TODO: stop all threads + } else if (packet_str == "1") { + m_non_stop = true; + } else + return SendErrorResponse(Status("Invalid QNonStop packet")); + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vStopped( + StringExtractorGDBRemote &packet) { + if (m_stop_notification_queue.empty()) + return SendErrorResponse(Status("No pending notification to ack")); + m_stop_notification_queue.pop_front(); + if (!m_stop_notification_queue.empty()) + return SendPacketNoLock(m_stop_notification_queue.front()); + // If this was the last notification and the process exited, terminate + // the server. + if (m_inferior_prev_state == eStateExited) { + m_exit_now = true; + m_mainloop.RequestTermination(); + } + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vCtrlC( + StringExtractorGDBRemote &packet) { + if (!m_non_stop) + return SendErrorResponse(Status("vCtrl is only valid in non-stop mode")); + + PacketResult interrupt_res = Handle_interrupt(packet); + // If interrupting the process failed, pass the result through. + if (interrupt_res != PacketResult::Success) + return interrupt_res; + // Otherwise, vCtrlC should issue an OK response (normal interrupts do not). + return SendOKResponse(); +} + void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() { Log *log = GetLog(LLDBLog::Process); @@ -3840,6 +3955,7 @@ "QThreadSuffixSupported+", "QListThreadsInStopReply+", "qXfer:features:read+", + "QNonStop+", }); // report server-only features Index: lldb/source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- lldb/source/Utility/StringExtractorGDBRemote.cpp +++ lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -150,6 +150,11 @@ return eServerPacketType_QMemTags; break; + case 'N': + if (PACKET_STARTS_WITH("QNonStop:")) + return eServerPacketType_QNonStop; + break; + case 'R': if (PACKET_STARTS_WITH("QRestoreRegisterState:")) return eServerPacketType_QRestoreRegisterState; @@ -369,6 +374,12 @@ return eServerPacketType_vCont_actions; if (PACKET_STARTS_WITH("vRun;")) return eServerPacketType_vRun; + if (PACKET_MATCHES("vStopped")) + return eServerPacketType_vStopped; + if (PACKET_MATCHES("vCtrlC")) + return eServerPacketType_vCtrlC; + break; + } break; case '_': Index: lldb/test/API/tools/lldb-server/TestLldbGdbServer.py =================================================================== --- lldb/test/API/tools/lldb-server/TestLldbGdbServer.py +++ lldb/test/API/tools/lldb-server/TestLldbGdbServer.py @@ -1410,3 +1410,128 @@ self.assertEqual(decoded[errno_idx], 0) # si_errno self.assertEqual(decoded[code_idx], SEGV_MAPERR) # si_code self.assertEqual(decoded[addr_idx], 0) # si_addr + + def test_QNonStop(self): + self.build() + self.set_inferior_startup_launch() + thread_num = 3 + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["thread:segfault"] + thread_num * ["thread:new"]) + self.test_sequence.add_log_lines( + ["read packet: $QNonStop:1#00", + "send packet: $OK#00", + "read packet: $c#63", + "send packet: $OK#00", + ], True) + self.expect_gdbremote_sequence() + + segv_signo = lldbutil.get_signal_number('SIGSEGV') + all_threads = set() + all_segv_threads = [] + + # we should get segfaults from all the threads + for segv_no in range(thread_num): + # first wait for the notification event + self.reset_test_sequence() + self.test_sequence.add_log_lines( + [{"direction": "send", + "regex": r"^%Stop:(T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);)", + "capture": {1: "packet", 2: "signo", 3: "thread_id"}, + }, + ], True) + m = self.expect_gdbremote_sequence() + del m["O_content"] + threads = [m] + + # then we may get events for the remaining threads + # (but note that not all threads may have been started yet) + while True: + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $vStopped#00", + {"direction": "send", + "regex": r"^\$(OK|T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);)", + "capture": {1: "packet", 2: "signo", 3: "thread_id"}, + }, + ], True) + m = self.expect_gdbremote_sequence() + if m["packet"] == "OK": + break + del m["O_content"] + threads.append(m) + + segv_threads = [] + other_threads = [] + for t in threads: + signo = int(t["signo"], 16) + if signo == segv_signo: + segv_threads.append(t["thread_id"]) + else: + self.assertEqual(signo, 0) + other_threads.append(t["thread_id"]) + + # verify that exactly one thread segfaulted + self.assertEqual(len(segv_threads), 1) + # we should get only one segv from every thread + self.assertNotIn(segv_threads[0], all_segv_threads) + all_segv_threads.extend(segv_threads) + # segv_threads + other_threads should always be a superset + # of all_threads, i.e. we should get states for all threads + # already started + self.assertFalse( + all_threads.difference(other_threads + segv_threads)) + all_threads.update(other_threads + segv_threads) + + # verify that `?` returns the same result + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $?#00", + ], True) + threads_verify = [] + while True: + self.test_sequence.add_log_lines( + [{"direction": "send", + "regex": r"^\$(OK|T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);)", + "capture": {1: "packet", 2: "signo", 3: "thread_id"}, + }, + ], True) + m = self.expect_gdbremote_sequence() + if m["packet"] == "OK": + break + del m["O_content"] + threads_verify.append(m) + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $vStopped#00", + ], True) + + self.assertEqual(threads, threads_verify) + + self.reset_test_sequence() + self.test_sequence.add_log_lines( + ["read packet: $vCont;C{:02x}:{};c#00" + .format(segv_signo, segv_threads[0]), + "send packet: $OK#00", + ], True) + self.expect_gdbremote_sequence() + + # finally, verify that all threads have started + self.assertEqual(len(all_threads), thread_num + 1) + + def test_QNonStop_vCtrlC(self): + self.build() + self.set_inferior_startup_launch() + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["thread:new"]) + 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: $vCtrlC#00", + "send packet: $OK#00", + {"direction": "send", + "regex": r"^%Stop:T13", + }, + ], True) + self.expect_gdbremote_sequence()