Index: lldb/packages/Python/lldbsuite/test/gdbclientutils.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/gdbclientutils.py
+++ lldb/packages/Python/lldbsuite/test/gdbclientutils.py
@@ -23,10 +23,12 @@
     Create a framed packet that's ready to send over the GDB connection
     channel.
 
-    Framing includes surrounding the message between $ and #, and appending
-    a two character hex checksum.
+    Framing includes adding a $ prefix (unless the message already starts
+    with a % prefix, in which case no additional prefix is added),
+    and appending # followed by a two character hex checksum.
     """
-    return "$%s#%02x" % (message, checksum(message))
+    prefix = "" if message.startswith("%") else "$"
+    return "%s%s#%02x" % (prefix, message, checksum(message))
 
 
 def escape_binary(message):
@@ -200,6 +202,14 @@
         if packet.startswith("qRegisterInfo"):
             regnum = int(packet[len("qRegisterInfo"):], 16)
             return self.qRegisterInfo(regnum)
+        if packet.startswith("QNonStop:"):
+            return self.QNonStop(int(packet.split(":", 1)[1]))
+        if packet == "vStdio":
+            return self.vStdio()
+        if packet == "vStopped":
+            return self.vStopped()
+        if packet == "vCtrlC":
+            return self.vCtrlC()
         if packet == "k":
             return self.k()
 
@@ -333,6 +343,18 @@
     def qRegisterInfo(self, num):
         return ""
 
+    def QNonStop(self, enabled):
+        return ""
+
+    def vStdio(self):
+        return ""
+
+    def vStopped(self):
+        return ""
+
+    def vCtrlC(self):
+        return ""
+
     def k(self):
         return ["W01", self.RESPONSE_DISCONNECT]
 
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h
@@ -59,6 +59,12 @@
       std::chrono::seconds interrupt_timeout,
       llvm::function_ref<void(llvm::StringRef)> output_callback);
 
+  bool GetNonStopEnabled() { return m_non_stop; }
+
+  void SetNonStopEnabled(bool enabled) { m_non_stop = enabled; }
+
+  bool DrainNotificationQueue(llvm::StringRef command);
+
   class Lock {
   public:
     // If interrupt_timeout == 0 seconds, only take the lock if the target is
@@ -133,6 +139,9 @@
 
   /// Whether we should resume after a stop.
   bool m_should_stop;
+
+  /// Whether non-stop protocol should be used.
+  bool m_non_stop = false;
   /// @}
 
   /// This handles the synchronization between individual async threads. For
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.cpp
@@ -52,15 +52,16 @@
   if (!cont_lock)
     return eStateInvalid;
   OnRunPacketSent(true);
-  // The main ReadPacket loop wakes up at computed_timeout intervals, just to 
+  // The main ReadPacket loop wakes up at computed_timeout intervals, just to
   // check that the connection hasn't dropped.  When we wake up we also check
   // whether there is an interrupt request that has reached its endpoint.
-  // If we want a shorter interrupt timeout that kWakeupInterval, we need to 
+  // If we want a shorter interrupt timeout that kWakeupInterval, we need to
   // choose the shorter interval for the wake up as well.
-  std::chrono::seconds computed_timeout = std::min(interrupt_timeout, 
-                                                   kWakeupInterval);
+  std::chrono::seconds computed_timeout =
+      std::min(interrupt_timeout, kWakeupInterval);
   for (;;) {
-    PacketResult read_result = ReadPacket(response, computed_timeout, false);
+    PacketResult read_result =
+        ReadPacket(response, computed_timeout, false, m_non_stop);
     // Reset the computed_timeout to the default value in case we are going
     // round again.
     computed_timeout = std::min(interrupt_timeout, kWakeupInterval);
@@ -79,12 +80,14 @@
         // time left till the interrupt timeout.  But don't wait longer
         // than our wakeup timeout.
         auto new_wait = m_interrupt_endpoint - cur_time;
-        computed_timeout = std::min(kWakeupInterval,
+        computed_timeout = std::min(
+            kWakeupInterval,
             std::chrono::duration_cast<std::chrono::seconds>(new_wait));
         continue;
       }
       break;
     }
+    case PacketResult::Notify:
     case PacketResult::Success:
       break;
     default:
@@ -95,9 +98,43 @@
     if (response.Empty())
       return eStateInvalid;
 
+    LLDB_LOG(log, "got result {0}, packet: {1}", read_result,
+             response.GetStringRef());
+
+    // We do not currently support full asynchronous communication.  Instead,
+    // when in non-stop mode, we wait for the asynchronous %Stop notification
+    // and then drain the notification queue
+    // TODO: issue vCont;t to ensure that all threads have actually stopped
+    // (this is not needed for LLGS but for gdbserver)
+
+    if (read_result == PacketResult::Notify &&
+        response.GetStringRef().startswith("Stdio:")) {
+      std::string inferior_stdout;
+      response.SetFilePos(6);
+      while (!response.IsOKResponse()) {
+        if (response.GetChar() == 'O') {
+          response.GetHexByteString(inferior_stdout);
+          delegate.HandleAsyncStdout(inferior_stdout);
+        }
+        if (SendPacketAndWaitForResponseNoLock("vStdio", response) !=
+                PacketResult::Success ||
+            response.IsUnsupportedResponse() || response.IsErrorResponse()) {
+          LLDB_LOG(log, "vStdio request failed");
+          return eStateInvalid;
+        }
+      }
+      continue;
+    }
+
+    if (read_result == PacketResult::Notify &&
+        response.GetStringRef().startswith("Stop:")) {
+      response.Reset(response.GetStringRef().substr(5));
+
+      if (!DrainNotificationQueue("vStopped"))
+        return eStateInvalid;
+    }
+
     const char stop_type = response.GetChar();
-    LLDB_LOGF(log, "GDBRemoteClientBase::%s () got packet: %s", __FUNCTION__,
-              response.GetStringRef().data());
 
     switch (stop_type) {
     case 'W':
@@ -283,6 +320,22 @@
     BroadcastEvent(eBroadcastBitRunPacketSent, nullptr);
 }
 
+bool GDBRemoteClientBase::DrainNotificationQueue(llvm::StringRef command) {
+  Log *log = GetLog(GDBRLog::Process);
+  for (;;) {
+    StringExtractorGDBRemote response;
+    if (SendPacketAndWaitForResponseNoLock(command, response) !=
+        PacketResult::Success || response.IsUnsupportedResponse()) {
+      LLDB_LOG(log, "{0} failed", command);
+      return false;
+    }
+
+    if (response.IsOKResponse())
+      return true;
+    // otherwise, loop
+  }
+}
+
 ///////////////////////////////////////
 // GDBRemoteClientBase::ContinueLock //
 ///////////////////////////////////////
@@ -322,9 +375,20 @@
               __FUNCTION__);
     return LockResult::Cancelled;
   }
-  if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=
-      PacketResult::Success)
-    return LockResult::Failed;
+
+  // When in non-stop mode, all continue packets should yield an immediate "OK"
+  // response before we consider the action successful.
+  if (m_comm.m_non_stop) {
+    StringExtractorGDBRemote response;
+    if (m_comm.SendPacketAndWaitForResponseNoLock(
+            m_comm.m_continue_packet, response) != PacketResult::Success ||
+        !response.IsOKResponse())
+      return LockResult::Failed;
+  } else {
+    if (m_comm.SendPacketNoLock(m_comm.m_continue_packet) !=
+        PacketResult::Success)
+      return LockResult::Failed;
+  }
 
   lldbassert(!m_comm.m_is_running);
   m_comm.m_is_running = true;
@@ -347,7 +411,7 @@
 }
 
 void GDBRemoteClientBase::Lock::SyncWithContinueThread() {
-  Log *log = GetLog(GDBRLog::Process|GDBRLog::Packets);
+  Log *log = GetLog(GDBRLog::Process | GDBRLog::Packets);
   std::unique_lock<std::mutex> lock(m_comm.m_mutex);
   if (m_comm.m_is_running && m_interrupt_timeout == std::chrono::seconds(0))
     return; // We were asked to avoid interrupting the sender. Lock is not
@@ -358,17 +422,25 @@
     if (m_comm.m_async_count == 1) {
       // The sender has sent the continue packet and we are the first async
       // packet. Let's interrupt it.
-      const char ctrl_c = '\x03';
-      ConnectionStatus status = eConnectionStatusSuccess;
-      size_t bytes_written = m_comm.Write(&ctrl_c, 1, status, nullptr);
-      if (bytes_written == 0) {
+      bool success;
+      if (m_comm.GetNonStopEnabled()) {
+        StringExtractorGDBRemote halt_response;
+        // Note: we expect an "OK" response here but due to the async logic,
+        // we need to check for it in SendContinuePacketAndWaitForResponse()
+        success = m_comm.SendPacketNoLock("vCtrlC") == PacketResult::Success;
+      } else {
+        const char ctrl_c = '\x03';
+        ConnectionStatus status = eConnectionStatusSuccess;
+        success = m_comm.Write(&ctrl_c, 1, status, nullptr) != 0;
+      }
+      if (!success) {
         --m_comm.m_async_count;
         LLDB_LOGF(log, "GDBRemoteClientBase::Lock::Lock failed to send "
                        "interrupt packet");
         return;
       }
       m_comm.m_interrupt_endpoint = steady_clock::now() + m_interrupt_timeout;
-      if (log)
+      if (log && !m_comm.GetNonStopEnabled())
         log->PutCString("GDBRemoteClientBase::Lock::Lock sent packet: \\x03");
     }
     m_comm.m_cv.wait(lock, [this] { return !m_comm.m_is_running; });
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
@@ -12,6 +12,7 @@
 #include "GDBRemoteCommunicationHistory.h"
 
 #include <condition_variable>
+#include <deque>
 #include <future>
 #include <mutex>
 #include <queue>
@@ -89,17 +90,18 @@
   enum class PacketType { Invalid = 0, Standard, Notify };
 
   enum class PacketResult {
-    Success = 0,        // Success
-    ErrorSendFailed,    // Status sending the packet
-    ErrorSendAck,       // Didn't get an ack back after sending a packet
-    ErrorReplyFailed,   // Status getting the reply
-    ErrorReplyTimeout,  // Timed out waiting for reply
-    ErrorReplyInvalid,  // Got a reply but it wasn't valid for the packet that
-                        // was sent
-    ErrorReplyAck,      // Sending reply ack failed
-    ErrorDisconnected,  // We were disconnected
-    ErrorNoSequenceLock // We couldn't get the sequence lock for a multi-packet
-                        // request
+    Success = 0,         // Success
+    ErrorSendFailed,     // Status sending the packet
+    ErrorSendAck,        // Didn't get an ack back after sending a packet
+    ErrorReplyFailed,    // Status getting the reply
+    ErrorReplyTimeout,   // Timed out waiting for reply
+    ErrorReplyInvalid,   // Got a reply but it wasn't valid for the packet that
+                         // was sent
+    ErrorReplyAck,       // Sending reply ack failed
+    ErrorDisconnected,   // We were disconnected
+    ErrorNoSequenceLock, // We couldn't get the sequence lock for a multi-packet
+                         // request
+    Notify,              // Successfully gotten a notification packet
   };
 
   // Class to change the timeout for a given scope and restore it to the
@@ -179,6 +181,7 @@
   bool m_is_platform; // Set to true if this class represents a platform,
                       // false if this class represents a debug session for
                       // a single process
+  std::deque<std::string> m_notification_packet_queue;
 
   CompressionType m_compression_type;
 
@@ -190,7 +193,8 @@
                                    bool skip_ack = false);
 
   PacketResult ReadPacket(StringExtractorGDBRemote &response,
-                          Timeout<std::micro> timeout, bool sync_on_timeout);
+                          Timeout<std::micro> timeout, bool sync_on_timeout,
+                          bool allow_notification = false);
 
   PacketResult ReadPacketWithOutputSupport(
       StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
@@ -199,7 +203,8 @@
 
   PacketResult WaitForPacketNoLock(StringExtractorGDBRemote &response,
                                    Timeout<std::micro> timeout,
-                                   bool sync_on_timeout);
+                                   bool sync_on_timeout,
+                                   bool allow_notification = false);
 
   bool CompressionIsEnabled() {
     return m_compression_type != CompressionType::None;
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
@@ -239,16 +239,15 @@
   return result;
 }
 
-GDBRemoteCommunication::PacketResult
-GDBRemoteCommunication::ReadPacket(StringExtractorGDBRemote &response,
-                                   Timeout<std::micro> timeout,
-                                   bool sync_on_timeout) {
+GDBRemoteCommunication::PacketResult GDBRemoteCommunication::ReadPacket(
+    StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
+    bool sync_on_timeout, bool allow_notification) {
   using ResponseType = StringExtractorGDBRemote::ResponseType;
 
   Log *log = GetLog(GDBRLog::Packets);
   for (;;) {
-    PacketResult result =
-        WaitForPacketNoLock(response, timeout, sync_on_timeout);
+    PacketResult result = WaitForPacketNoLock(
+        response, timeout, sync_on_timeout, allow_notification);
     if (result != PacketResult::Success ||
         (response.GetResponseType() != ResponseType::eAck &&
          response.GetResponseType() != ResponseType::eNack))
@@ -260,15 +259,30 @@
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunication::WaitForPacketNoLock(StringExtractorGDBRemote &packet,
                                             Timeout<std::micro> timeout,
-                                            bool sync_on_timeout) {
+                                            bool sync_on_timeout,
+                                            bool allow_notification) {
   uint8_t buffer[8192];
   Status error;
 
   Log *log = GetLog(GDBRLog::Packets);
 
+  // If the caller expects notifications, check the notification queue first.
+  if (allow_notification && !m_notification_packet_queue.empty()) {
+    packet.Reset(m_notification_packet_queue.front());
+    m_notification_packet_queue.pop_front();
+    return PacketResult::Notify;
+  }
+
   // Check for a packet from our cache first without trying any reading...
-  if (CheckForPacket(nullptr, 0, packet) != PacketType::Invalid)
-    return PacketResult::Success;
+  PacketType packet_type = CheckForPacket(nullptr, 0, packet);
+  if (packet_type == PacketType::Notify && !allow_notification) {
+    // If the caller does not support notifications, queue it for later.
+    m_notification_packet_queue.push_back(packet.GetStringRef().str());
+    packet_type = CheckForPacket(nullptr, 0, packet);
+  }
+  if (packet_type != PacketType::Invalid)
+    return packet_type == PacketType::Standard ? PacketResult::Success
+                                               : PacketResult::Notify;
 
   bool timed_out = false;
   bool disconnected = false;
@@ -283,8 +297,12 @@
               bytes_read);
 
     if (bytes_read > 0) {
-      if (CheckForPacket(buffer, bytes_read, packet) != PacketType::Invalid)
-        return PacketResult::Success;
+      packet_type = CheckForPacket(buffer, bytes_read, packet);
+      if (packet_type == PacketType::Notify && !allow_notification)
+        m_notification_packet_queue.push_back(packet.GetStringRef().str());
+      else if (packet_type != PacketType::Invalid)
+        return packet_type == PacketType::Standard ? PacketResult::Success
+                                                   : PacketResult::Notify;
     } else {
       switch (status) {
       case eConnectionStatusTimedOut:
@@ -1315,6 +1333,9 @@
   case PacketResult::ErrorNoSequenceLock:
     Stream << "ErrorNoSequenceLock";
     break;
+  case PacketResult::Notify:
+    Stream << "Notify";
+    break;
   }
 }
 
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -321,6 +321,8 @@
 
   void EnableErrorStringInPacket();
 
+  void EnableNonStop();
+
   bool GetQXferLibrariesReadSupported();
 
   bool GetQXferLibrariesSVR4ReadSupported();
@@ -523,6 +525,8 @@
 
   bool GetSaveCoreSupported() const;
 
+  bool GetNonStopSupported() const;
+
 protected:
   LazyBool m_supports_not_sending_acks = eLazyBoolCalculate;
   LazyBool m_supports_thread_suffix = eLazyBoolCalculate;
@@ -563,6 +567,7 @@
   LazyBool m_supports_multiprocess = eLazyBoolCalculate;
   LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
   LazyBool m_supports_qSaveCore = eLazyBoolCalculate;
+  LazyBool m_supports_QNonStop = eLazyBoolCalculate;
   LazyBool m_uses_native_signals = eLazyBoolCalculate;
 
   bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -276,6 +276,7 @@
     m_avoid_g_packets = eLazyBoolCalculate;
     m_supports_multiprocess = eLazyBoolCalculate;
     m_supports_qSaveCore = eLazyBoolCalculate;
+    m_supports_QNonStop = eLazyBoolCalculate;
     m_supports_qXfer_auxv_read = eLazyBoolCalculate;
     m_supports_qXfer_libraries_read = eLazyBoolCalculate;
     m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
@@ -335,6 +336,7 @@
   m_supports_QPassSignals = eLazyBoolNo;
   m_supports_memory_tagging = eLazyBoolNo;
   m_supports_qSaveCore = eLazyBoolNo;
+  m_supports_QNonStop = eLazyBoolNo;
   m_uses_native_signals = eLazyBoolNo;
 
   m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
@@ -384,6 +386,8 @@
         m_supports_memory_tagging = eLazyBoolYes;
       else if (x == "qSaveCore+")
         m_supports_qSaveCore = eLazyBoolYes;
+      else if (x == "QNonStop+")
+        m_supports_QNonStop = eLazyBoolYes;
       else if (x == "native-signals+")
         m_uses_native_signals = eLazyBoolYes;
       // Look for a list of compressions in the features list e.g.
@@ -530,6 +534,10 @@
   return m_supports_qSaveCore == eLazyBoolYes;
 }
 
+bool GDBRemoteCommunicationClient::GetNonStopSupported() const {
+  return m_supports_QNonStop == eLazyBoolYes;
+}
+
 StructuredData::ObjectSP GDBRemoteCommunicationClient::GetThreadsInfo() {
   // Get information on all threads at one using the "jThreadsInfo" packet
   StructuredData::ObjectSP object_sp;
@@ -579,6 +587,18 @@
   }
 }
 
+void GDBRemoteCommunicationClient::EnableNonStop() {
+  if (!GetNonStopSupported())
+    return;
+
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse("QNonStop:1", response) ==
+      PacketResult::Success) {
+    if (response.IsOKResponse())
+      SetNonStopEnabled(true);
+  }
+}
+
 bool GDBRemoteCommunicationClient::GetLoadedDynamicLibrariesInfosSupported() {
   if (m_supports_jLoadedDynamicLibrariesInfos == eLazyBoolCalculate) {
     StringExtractorGDBRemote response;
@@ -2747,8 +2767,13 @@
 
 bool GDBRemoteCommunicationClient::GetStopReply(
     StringExtractorGDBRemote &response) {
-  if (SendPacketAndWaitForResponse("?", response) == PacketResult::Success)
+  if (SendPacketAndWaitForResponse("?", response) == PacketResult::Success) {
+    if (GetNonStopEnabled()) {
+      if (!DrainNotificationQueue("vStopped"))
+        return false;  // TODO: better error handling
+    }
     return response.IsNormalResponse();
+  }
   return false;
 }
 
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -287,6 +287,7 @@
   int64_t m_breakpoint_pc_offset;
   lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
   bool m_use_g_packet_for_reading;
+  bool m_use_non_stop_protocol;
 
   bool m_allow_flash_writes;
   using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>;
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -164,6 +164,11 @@
     const uint32_t idx = ePropertyUseGPacketForReading;
     return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
   }
+
+  bool GetUseNonStopProtocol() const {
+    const uint32_t idx = ePropertyUseNonStopProtocol;
+    return m_collection_sp->GetPropertyAtIndexAsBoolean(nullptr, idx, true);
+  }
 };
 
 static PluginProperties &GetGlobalPluginProperties() {
@@ -301,6 +306,8 @@
 
   m_use_g_packet_for_reading =
       GetGlobalPluginProperties().GetUseGPacketForReading();
+  m_use_non_stop_protocol =
+      GetGlobalPluginProperties().GetUseNonStopProtocol();
 }
 
 // Destructor
@@ -1077,6 +1084,11 @@
     else
       SetUnixSignals(UnixSignals::Create(GetTarget().GetArchitecture()));
   }
+
+  // Delay enabling non-stop protocol until we've taken care of full setup
+  // to workaround a bug in gdbserver.
+  if (m_use_non_stop_protocol)
+    m_gdb_comm.EnableNonStop();
 }
 
 void ProcessGDBRemote::MaybeLoadExecutableModule() {
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemoteProperties.td
@@ -9,6 +9,10 @@
     Global,
     DefaultStringValue<"">,
     Desc<"The file that provides the description for remote target registers.">;
+  def UseNonStopProtocol: Property<"use-non-stop-protocol", "Boolean">,
+    Global,
+    DefaultFalse,
+    Desc<"If true, LLDB will enable non-stop mode on the server and expect non-stop-style notifications from it. Note that LLDB currently requires that all threads are stopped anyway in non-stop mode.">;
   def UseSVR4: Property<"use-libraries-svr4", "Boolean">,
     Global,
     DefaultTrue,
Index: lldb/test/API/functionalities/gdb_remote_client/TestNonStop.py
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/gdb_remote_client/TestNonStop.py
@@ -0,0 +1,151 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+
+class TestNonStop(GDBRemoteTestBase):
+    def test_run(self):
+        class MyResponder(MockGDBServerResponder):
+            vStopped_counter = 0
+
+            def qSupported(self, client_supported):
+                return "QNonStop+;" + super().qSupported(client_supported)
+
+            def QNonStop(self, val):
+                return "OK"
+
+            def qfThreadInfo(self):
+                return "m10,12"
+
+            def qsThreadInfo(self):
+                return "l"
+
+            def vStopped(self):
+                self.vStopped_counter += 1
+                return ("OK" if self.vStopped_counter > 1
+                        else "T00;thread:10")
+
+            def cont(self):
+                self.vStopped_counter = 0
+                return ["OK", "%Stop:T02;thread:12"]
+
+        self.dbg.HandleCommand(
+            "settings set plugin.process.gdb-remote.use-non-stop-protocol true")
+        self.addTearDownHook(lambda:
+            self.runCmd(
+                "settings set plugin.process.gdb-remote.use-non-stop-protocol "
+                "false"))
+        self.server.responder = MyResponder()
+        target = self.dbg.CreateTarget("")
+        process = self.connect(target)
+        self.assertPacketLogContains(["QNonStop:1"])
+
+        process.Continue()
+        self.assertPacketLogContains(["vStopped"])
+        self.assertEqual(process.GetSelectedThread().GetStopReason(),
+                         lldb.eStopReasonSignal)
+        self.assertEqual(process.GetSelectedThread().GetStopDescription(100),
+                         "signal SIGINT")
+
+    def test_stdio(self):
+        class MyResponder(MockGDBServerResponder):
+            vStdio_counter = 0
+            vStopped_counter = 0
+
+            def qSupported(self, client_supported):
+                return "QNonStop+;" + super().qSupported(client_supported)
+
+            def QNonStop(self, val):
+                return "OK"
+
+            def qfThreadInfo(self):
+                return "m10,12"
+
+            def qsThreadInfo(self):
+                return "l"
+
+            def vStdio(self):
+                self.vStdio_counter += 1
+                # intersperse notifications with replies for better testing
+                return ("OK" if self.vStdio_counter > 1
+                        else ["%Stop:T02;thread:12",
+                              "O7365636f6e64206c696e650d0a"])
+
+            def vStopped(self):
+                self.vStopped_counter += 1
+                return ("OK" if self.vStopped_counter > 1
+                        else "T00;thread:10")
+
+            def cont(self):
+                self.vStopped_counter = 0
+                self.vStdio_counter = 0
+                return ["OK",
+                        "%Stdio:O6669727374206c696e650d0a",]
+
+        self.dbg.HandleCommand(
+            "settings set plugin.process.gdb-remote.use-non-stop-protocol true")
+        self.addTearDownHook(lambda:
+            self.runCmd(
+                "settings set plugin.process.gdb-remote.use-non-stop-protocol "
+                "false"))
+        self.server.responder = MyResponder()
+        target = self.dbg.CreateTarget("")
+        process = self.connect(target)
+        self.assertPacketLogContains(["QNonStop:1"])
+
+        process.Continue()
+        self.assertPacketLogContains(["vStdio", "vStopped"])
+        self.assertEqual(process.GetSelectedThread().GetStopReason(),
+                         lldb.eStopReasonSignal)
+        self.assertEqual(process.GetSelectedThread().GetStopDescription(100),
+                         "signal SIGINT")
+
+    def test_vCtrlC(self):
+        class MyResponder(MockGDBServerResponder):
+            vStopped_counter = 0
+
+            def qSupported(self, client_supported):
+                return "QNonStop+;" + super().qSupported(client_supported)
+
+            def QNonStop(self, val):
+                return "OK"
+
+            def qfThreadInfo(self):
+                return "m10,12"
+
+            def vStopped(self):
+                self.vStopped_counter += 1
+                return ("OK" if self.vStopped_counter % 2 == 0
+                        else "T00;thread:10")
+
+            def cont(self):
+                return ["OK"]
+
+            def vCtrlC(self):
+                return ["OK", "%Stop:T00;thread:10"]
+
+        self.dbg.HandleCommand(
+            "settings set plugin.process.gdb-remote.use-non-stop-protocol true")
+        self.addTearDownHook(lambda:
+            self.runCmd(
+                "settings set plugin.process.gdb-remote.use-non-stop-protocol "
+                "false"))
+        self.server.responder = MyResponder()
+        target = self.dbg.CreateTarget("")
+        process = self.connect(target)
+        self.assertPacketLogContains(["QNonStop:1"])
+
+        self.dbg.SetAsync(True)
+        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+                                      [lldb.eStateStopped])
+        process.Continue()
+        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+                                      [lldb.eStateRunning])
+        process.Stop()
+        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process,
+                                      [lldb.eStateStopped])
+        self.assertPacketLogContains(["vStopped"])
+        self.assertEqual(process.GetSelectedThread().GetStopReason(),
+                         lldb.eStopReasonNone)