This revision was automatically updated to reflect the committed changes.
Closed by commit rGbc04d240850b: [lldb] [llgs] Implement non-stop style stop 
notification packets (authored by mgorny).
Herald added a project: LLDB.

Changed prior to commit:
  https://reviews.llvm.org/D125575?vs=438007&id=438747#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D125575/new/

https://reviews.llvm.org/D125575

Files:
  lldb/include/lldb/Utility/StringExtractorGDBRemote.h
  lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
  lldb/packages/Python/lldbsuite/test/tools/lldb-server/lldbgdbserverutils.py
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
  lldb/source/Utility/StringExtractorGDBRemote.cpp
  lldb/test/API/tools/lldb-server/TestNonStop.py

Index: lldb/test/API/tools/lldb-server/TestNonStop.py
===================================================================
--- /dev/null
+++ lldb/test/API/tools/lldb-server/TestNonStop.py
@@ -0,0 +1,165 @@
+from lldbsuite.test.lldbtest import *
+
+import gdbremote_testcase
+
+
+class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    def test_run(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_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()
+
+    def test_exit(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        procs = self.prep_debug_monitor_and_inferior()
+        self.test_sequence.add_log_lines(
+            ["read packet: $QNonStop:1#00",
+             "send packet: $OK#00",
+             "read packet: $c#63",
+             "send packet: $OK#00",
+             "send packet: %Stop:W00#00",
+             "read packet: $vStopped#00",
+             "send packet: $OK#00",
+             ], True)
+        self.expect_gdbremote_sequence()
+
+    def test_exit_query(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        procs = self.prep_debug_monitor_and_inferior()
+        self.test_sequence.add_log_lines(
+            ["read packet: $QNonStop:1#00",
+             "send packet: $OK#00",
+             "read packet: $c#63",
+             "send packet: $OK#00",
+             "send packet: %Stop:W00#00",
+             "read packet: $?#00",
+             "send packet: $W00#00",
+             "read packet: $vStopped#00",
+             "send packet: $OK#00",
+             ], True)
+        self.expect_gdbremote_sequence()
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/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<std::string> m_stop_notification_queue;
 
   NativeProcessProtocol::Extension m_extensions_supported = {};
 
@@ -113,11 +115,17 @@
 
   PacketResult SendWResponse(NativeProcessProtocol *process);
 
+  StreamString PrepareStopReplyPacketForThread(NativeThreadProtocol &thread);
+
   PacketResult SendStopReplyPacketForThread(NativeProcessProtocol &process,
-                                            lldb::tid_t tid);
+                                            lldb::tid_t tid,
+                                            bool force_synchronous);
 
   PacketResult SendStopReasonForState(NativeProcessProtocol &process,
-                                      lldb::StateType process_state);
+                                      lldb::StateType process_state,
+                                      bool force_synchronous);
+
+  void EnqueueStopReplyPackets(lldb::tid_t thread_to_skip);
 
   PacketResult Handle_k(StringExtractorGDBRemote &packet);
 
@@ -219,6 +227,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);
@@ -246,6 +260,10 @@
   std::vector<std::string> HandleFeatures(
       const llvm::ArrayRef<llvm::StringRef> client_features) override;
 
+  // Provide a response for successful continue action, i.e. send "OK"
+  // in non-stop mode, no response otherwise.
+  PacketResult SendContinueSuccessResponse();
+
 private:
   llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> BuildTargetXml();
 
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) {
@@ -476,6 +486,9 @@
   response.Format("{0:g}", *wait_status);
   if (bool(m_extensions_supported & NativeProcessProtocol::Extension::multiprocess))
     response.Format(";process:{0:x-}", process->GetID());
+  if (m_non_stop)
+    return SendNotificationPacketNoLock("Stop", m_stop_notification_queue,
+                                        response.GetString());
   return SendPacketNoLock(response.GetString());
 }
 
@@ -766,23 +779,22 @@
   return threads_array;
 }
 
-GDBRemoteCommunication::PacketResult
-GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
-    NativeProcessProtocol &process, lldb::tid_t tid) {
+StreamString
+GDBRemoteCommunicationServerLLGS::PrepareStopReplyPacketForThread(
+    NativeThreadProtocol &thread) {
   Log *log = GetLog(LLDBLog::Process | LLDBLog::Thread);
 
-  LLDB_LOG(log, "preparing packet for pid {0} tid {1}", process.GetID(), tid);
+  NativeProcessProtocol &process = thread.GetProcess();
 
-  // Ensure we can get info on the given thread.
-  NativeThreadProtocol *thread = process.GetThreadByID(tid);
-  if (!thread)
-    return SendErrorResponse(51);
+  LLDB_LOG(log, "preparing packet for pid {0} tid {1}", 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) {
@@ -790,14 +802,13 @@
   //     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}",
-      process.GetID(), tid, signum, int(tid_stop_info.reason),
+      process.GetID(), thread.GetID(), signum, int(tid_stop_info.reason),
       tid_stop_info.details.exception.type);
 
   // Print the signal number.
@@ -808,10 +819,10 @@
   if (bool(m_extensions_supported &
            NativeProcessProtocol::Extension::multiprocess))
     response.Format("p{0:x-}.", process.GetID());
-  response.Format("{0:x-};", tid);
+  response.Format("{0:x-};", 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 +886,7 @@
     char delimiter = ':';
     for (NativeThreadProtocol *thread;
          (thread = process.GetThreadAtIndex(i)) != nullptr; ++i) {
-      NativeRegisterContext& reg_ctx = thread->GetRegisterContext();
+      NativeRegisterContext &reg_ctx = thread->GetRegisterContext();
 
       uint32_t reg_to_read = reg_ctx.ConvertRegisterKindToRegisterNumber(
           eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
@@ -906,7 +917,7 @@
   //
 
   // Grab the register context.
-  NativeRegisterContext& reg_ctx = thread->GetRegisterContext();
+  NativeRegisterContext &reg_ctx = thread.GetRegisterContext();
   const auto expedited_regs =
       reg_ctx.GetExpeditedRegisters(ExpeditedRegs::Full);
 
@@ -923,8 +934,9 @@
                                           &reg_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 : "<unnamed-register>",
                   reg_num, error.AsCString());
@@ -973,9 +985,46 @@
                     tid_stop_info.details.fork.child_tid);
   }
 
+  return response;
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread(
+    NativeProcessProtocol &process, lldb::tid_t tid, bool force_synchronous) {
+  // Ensure we can get info on the given thread.
+  NativeThreadProtocol *thread = process.GetThreadByID(tid);
+  if (!thread)
+    return SendErrorResponse(51);
+
+  StreamString response = PrepareStopReplyPacketForThread(*thread);
+  if (response.Empty())
+    return SendErrorResponse(42);
+
+  if (m_non_stop && !force_synchronous) {
+    PacketResult ret = SendNotificationPacketNoLock(
+        "Stop", m_stop_notification_queue, response.GetString());
+    // Queue notification events for the remaining threads.
+    EnqueueStopReplyPackets(tid);
+    return ret;
+  }
+
   return SendPacketNoLock(response.GetString());
 }
 
+void GDBRemoteCommunicationServerLLGS::EnqueueStopReplyPackets(
+    lldb::tid_t thread_to_skip) {
+  if (!m_non_stop)
+    return;
+
+  uint32_t thread_index = 0;
+  while (NativeThreadProtocol *listed_thread =
+             m_current_process->GetThreadAtIndex(thread_index++)) {
+    if (listed_thread->GetID() != thread_to_skip)
+      m_stop_notification_queue.push_back(
+          PrepareStopReplyPacketForThread(*listed_thread).GetString().str());
+  }
+}
+
 void GDBRemoteCommunicationServerLLGS::HandleInferiorState_Exited(
     NativeProcessProtocol *process) {
   assert(process && "process cannot be NULL");
@@ -983,8 +1032,8 @@
   Log *log = GetLog(LLDBLog::Process);
   LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s called", __FUNCTION__);
 
-  PacketResult result =
-      SendStopReasonForState(*process, StateType::eStateExited);
+  PacketResult result = SendStopReasonForState(
+      *process, StateType::eStateExited, /*force_synchronous=*/false);
   if (result != PacketResult::Success) {
     LLDB_LOGF(log,
               "GDBRemoteCommunicationServerLLGS::%s failed to send stop "
@@ -996,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(
@@ -1016,8 +1069,8 @@
     break;
   default:
     // In all other cases, send the stop reason.
-    PacketResult result =
-        SendStopReasonForState(*process, StateType::eStateStopped);
+    PacketResult result = SendStopReasonForState(
+        *process, StateType::eStateStopped, /*force_synchronous=*/false);
     if (result != PacketResult::Success) {
       LLDB_LOGF(log,
                 "GDBRemoteCommunicationServerLLGS::%s failed to send stop "
@@ -1373,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 SendContinueSuccessResponse();
 }
 
 GDBRemoteCommunication::PacketResult
@@ -1503,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 SendContinueSuccessResponse();
 }
 
 GDBRemoteCommunication::PacketResult
@@ -1543,8 +1599,8 @@
   }
 
   LLDB_LOG(log, "continued process {0}", m_continue_process->GetID());
-  // No response required from continue.
-  return PacketResult::Success;
+
+  return SendContinueSuccessResponse();
 }
 
 GDBRemoteCommunication::PacketResult
@@ -1582,6 +1638,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.
@@ -1659,8 +1718,8 @@
   }
 
   LLDB_LOG(log, "continued process {0}", m_continue_process->GetID());
-  // No response required from vCont.
-  return PacketResult::Success;
+
+  return SendContinueSuccessResponse();
 }
 
 void GDBRemoteCommunicationServerLLGS::SetCurrentThreadID(lldb::tid_t tid) {
@@ -1688,13 +1747,38 @@
   if (!m_current_process)
     return SendErrorResponse(02);
 
+  if (m_non_stop) {
+    // Clear the notification queue first, except for pending exit
+    // notifications.
+    llvm::erase_if(m_stop_notification_queue, [](const std::string &x) {
+      return x.front() != 'W' && x.front() != 'X';
+    });
+
+    // Queue stop reply packets for all active threads.  Start with the current
+    // thread (for clients that don't actually support multiple stop reasons).
+    NativeThreadProtocol *thread = m_current_process->GetCurrentThread();
+    if (thread)
+      m_stop_notification_queue.push_back(
+          PrepareStopReplyPacketForThread(*thread).GetString().str());
+    EnqueueStopReplyPackets(thread ? thread->GetID() : LLDB_INVALID_THREAD_ID);
+
+    // If the notification queue is empty (i.e. everything is running), send OK.
+    if (m_stop_notification_queue.empty())
+      return SendOKResponse();
+
+    // Send the first item from the new notification queue synchronously.
+    return SendPacketNoLock(m_stop_notification_queue.front());
+  }
+
   return SendStopReasonForState(*m_current_process,
-                                m_current_process->GetState());
+                                m_current_process->GetState(),
+                                /*force_synchronous=*/true);
 }
 
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::SendStopReasonForState(
-    NativeProcessProtocol &process, lldb::StateType process_state) {
+    NativeProcessProtocol &process, lldb::StateType process_state,
+    bool force_synchronous) {
   Log *log = GetLog(LLDBLog::Process);
 
   switch (process_state) {
@@ -1714,7 +1798,7 @@
     // Make sure we set the current thread so g and p packets return the data
     // the gdb will expect.
     SetCurrentThreadID(tid);
-    return SendStopReplyPacketForThread(process, tid);
+    return SendStopReplyPacketForThread(process, tid, force_synchronous);
   }
 
   case eStateInvalid:
@@ -2806,8 +2890,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 SendContinueSuccessResponse();
 }
 
 llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
@@ -3176,7 +3261,8 @@
   // Notify we attached by sending a stop packet.
   assert(m_current_process);
   return SendStopReasonForState(*m_current_process,
-                                m_current_process->GetState());
+                                m_current_process->GetState(),
+                                /*force_synchronous=*/false);
 }
 
 GDBRemoteCommunication::PacketResult
@@ -3208,7 +3294,8 @@
   // Notify we attached by sending a stop packet.
   assert(m_current_process);
   return SendStopReasonForState(*m_current_process,
-                                m_current_process->GetState());
+                                m_current_process->GetState(),
+                                /*force_synchronous=*/false);
 }
 
 GDBRemoteCommunication::PacketResult
@@ -3246,7 +3333,8 @@
   // Notify we attached by sending a stop packet.
   assert(m_current_process);
   return SendStopReasonForState(*m_current_process,
-                                m_current_process->GetState());
+                                m_current_process->GetState(),
+                                /*force_synchronous=*/false);
 }
 
 GDBRemoteCommunication::PacketResult
@@ -3277,7 +3365,8 @@
     if (m_process_launch_error.Success()) {
       assert(m_current_process);
       return SendStopReasonForState(*m_current_process,
-                                    m_current_process->GetState());
+                                    m_current_process->GetState(),
+                                    /*force_synchronous=*/true);
     }
     LLDB_LOG(log, "failed to launch exe: {0}", m_process_launch_error);
   }
@@ -3349,7 +3438,8 @@
               __FUNCTION__, packet.GetStringRef().data());
     return SendErrorResponse(0x15);
   }
-  return SendStopReplyPacketForThread(*m_current_process, tid);
+  return SendStopReplyPacketForThread(*m_current_process, tid,
+                                      /*force_synchronous=*/true);
 }
 
 GDBRemoteCommunication::PacketResult
@@ -3672,6 +3762,59 @@
   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) {
+  // Per the protocol, the first message put into the queue is sent
+  // immediately.  However, it remains the queue until the client ACKs
+  // it via vStopped -- then we pop it and send the next message.
+  // The process repeats until the last message in the queue is ACK-ed,
+  // in which case the vStopped packet sends an OK response.
+
+  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);
 
@@ -3855,6 +3998,7 @@
                             "QThreadSuffixSupported+",
                             "QListThreadsInStopReply+",
                             "qXfer:features:read+",
+                            "QNonStop+",
                         });
 
   // report server-only features
@@ -3909,6 +4053,11 @@
   process.SetEnabledExtensions(flags);
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::SendContinueSuccessResponse() {
+  return m_non_stop ? SendOKResponse() : PacketResult::Success;
+}
+
 std::string
 lldb_private::process_gdb_remote::LLGSArgToURL(llvm::StringRef url_arg,
                                                bool reverse_connect) {
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<std::string>& 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<std::string> &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/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/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/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;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to