[PATCH v4 11/11] gdb, gdbserver: add AlwaysNonStop remote protocol extension

Mohamed Bouhaouel <[email protected]>
Newsgroups gmane.comp.gdb.patches
Message-ID <[email protected]>
From: "Bouhaouel, Mohamed" <[email protected]>

Add a new RSP extension allowing remote stubs to declare that a target
prefers to operate in non-stop mode.

When a stub advertises "AlwaysNonStop+" in its qSupported response:

* The stub automatically initializes to non-stop mode.
* GDB will operate the stub in non-stop mode (with default settings).
* The stub may reject QNonStop:0 requests with descriptive error
  messages.
* GDB allows connection even if 'maint set target-non-stop off' is set,
  but lets the stub decide whether to accept mode switches.

Additionally, gdbserver now accepts --enable-packet=PACKET to enable
specific remote protocol packets or extensions at startup.  Currently
this supports the 'AlwaysNonStop' extension.

Reviewed-By: Eli Zaretskii <[email protected]>
---
 gdb/NEWS                                      | 13 +++
 gdb/doc/gdb.texinfo                           | 46 ++++++++++-
 gdb/remote.c                                  | 21 +++++
 .../gdb.server/enable-always-non-stop.exp     | 56 +++++++++++++
 gdbserver/server.cc                           | 79 +++++++++++++++++--
 gdbserver/server.h                            |  2 +
 gdbserver/target.cc                           |  6 ++
 gdbserver/target.h                            |  6 ++
 8 files changed, 223 insertions(+), 6 deletions(-)
 create mode 100644 gdb/testsuite/gdb.server/enable-always-non-stop.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index ec9b5a33787..4f75a55af5d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -22,6 +22,10 @@
   this flag is used gdbserver will not escape special shell characters
   within the inferior arguments.
 
+* gdbserver now accepts --enable-packet=PACKET to enable specific remote
+  protocol packets or extensions at startup.  Currently this supports
+  'AlwaysNonStop' to force the target to always operate in non-stop mode.
+
 * The add-inferior, clone-inferior, and MI -add-inferior commands will
   now give a warning, and create the new inferior without a
   connection, when the current inferior's connection, at the time the
@@ -116,6 +120,15 @@
   intent to remove it in a future release.
   The s390 64-bit target (s390x-*) remains supported.
 
+* New remote protocol extension 'AlwaysNonStop' allows gdbserver to
+  indicate that the target prefers to operate in non-stop mode.  When a
+  remote stub advertises 'AlwaysNonStop+' in its qSupported reply, GDB
+  will operate the stub in non-stop mode (when 'maint set target-non-stop'
+  is 'auto', the default).  If the user explicitly sets 'maint set
+  target-non-stop off', GDB will allow the connection but the stub may
+  reject attempts to switch to all-stop mode with a descriptive error
+  message.
+
 * Configure changes
 
 ** --with-babeltrace has been removed.  The babeltrace library was
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a698b2b8451..3019cb915f2 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -42922,7 +42922,9 @@ does not indicate support.
 
 @item maint set target-non-stop off
 @value{GDBN} does not control the target in non-stop mode even if the
-target supports it.
+target supports it.  Note: targets that prefer non-stop mode (via
+@samp{AlwaysNonStop+}) may reject attempts to switch to all-stop mode
+with a descriptive error message.
 @end table
 
 Here is how @code{set non-stop} and @code{maint set target-non-stop}
@@ -45094,6 +45096,12 @@ Reply:
 @table @samp
 @item OK
 The request succeeded.
+
+@item @samp{E.@var{message}}
+The request failed.  This may occur if the stub prefers non-stop mode
+and advertised @samp{AlwaysNonStop+} support, and @value{GDBN} attempted to
+switch to all-stop mode with @samp{QNonStop:0}.  The stub returns a
+textual error message describing why the mode switch was rejected.
 @end table
 
 This packet is not probed by default; the remote stub must request it,
@@ -45511,6 +45519,22 @@ multiple-watchpoint-addresses-packet} command (@pxref{Remote
 Configuration, set remote multiple-watchpoint-addresses-packet}).
 @end table
 
+@item AlwaysNonStop
+This feature indicates that @value{GDBN} supports remote stubs that
+prefer non-stop mode operation.  If the stub advertises
+@samp{AlwaysNonStop+}, it will run in non-stop mode, similar to
+the setting @code{maint set target-non-stop on}.  If @value{GDBN}
+attempts to switch the target to all-stop mode, the stub may reject
+the request with a descriptive error message.
+
+Note: @samp{AlwaysNonStop+} requires @samp{QNonStop+} support.
+If a stub advertises @samp{AlwaysNonStop+} without @samp{QNonStop+},
+the @samp{AlwaysNonStop+} extension will be silently ignored by @value{GDBN}.
+
+Use of this feature is controlled by the @code{set remote
+always-non-stop-feature-packet} command (@pxref{Remote
+Configuration, set remote always-non-stop-feature-packet}).
+
 Stubs should ignore any unknown values for
 @var{gdbfeature}.  Any @value{GDBN} which sends a @samp{qSupported}
 packet supports receiving packets of unlimited length (earlier
@@ -45907,6 +45931,13 @@ packet (@pxref{qXfer fdpic loadmap read}).
 The remote stub understands the @samp{QNonStop} packet
 (@pxref{QNonStop}).
 
+@item AlwaysNonStop
+The remote stub prefers non-stop mode operation, may reject
+@samp{QNonStop:0} requests with a descriptive error message, and
+automatically initializes to non-stop mode.  This feature must be
+advertised together with @samp{QNonStop+}; if advertised without it,
+the feature will be silently ignored by @value{GDBN}.  @xref{QNonStop}.
+
 @item QCatchSyscalls
 The remote stub understands the @samp{QCatchSyscalls} packet
 (@pxref{QCatchSyscalls}).
@@ -52204,6 +52235,19 @@ option special shell characters will not be escaped.  When
 @command{gdbserver} starts a new shell in order to invoke the
 inferior, this new shell will expand any special shell characters.
 
+@item --enable-packet=@var{packet}@r{[},@var{packet}@dots{}@r{]}
+Enable specific remote protocol packets or extensions at startup.
+The @var{packet} argument can be one of the following:
+
+@table @code
+@item AlwaysNonStop
+Force the target to always operate in non-stop mode.  When this
+extension is enabled, @command{gdbserver} will reject any attempts
+by @value{GDBN} to switch to all-stop mode with a descriptive error
+message.  This is useful for targets that require non-stop mode
+for correct operation.
+@end table
+
 @c --disable-packet is not documented for users.
 
 @c --disable-randomization and --no-disable-randomization are superseded by
diff --git a/gdb/remote.c b/gdb/remote.c
index b1bc1283b9a..fc5fb6cd160 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -323,6 +323,9 @@ enum {
   /* Support for the QNonStop packet.  */
   PACKET_QNonStop,
 
+  /* Support for AlwaysNonStop extension.  */
+  PACKET_AlwaysNonStop_feature,
+
   /* Support for the QThreadEvents packet.  */
   PACKET_QThreadEvents,
 
@@ -1442,6 +1445,8 @@ class remote_target : public process_stratum_target
 
   void check_binary_download (CORE_ADDR addr);
 
+  bool always_non_stop_p () override;
+
   target_xfer_status remote_write_bytes_aux (const char *header,
 					     CORE_ADDR memaddr,
 					     const gdb_byte *myaddr,
@@ -6133,6 +6138,8 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "multiprocess", PACKET_DISABLE, remote_supported_packet,
     PACKET_multiprocess_feature },
   { "QNonStop", PACKET_DISABLE, remote_supported_packet, PACKET_QNonStop },
+  { "AlwaysNonStop", PACKET_DISABLE, remote_supported_packet,
+    PACKET_AlwaysNonStop_feature },
   { "qXfer:siginfo:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_siginfo_read },
   { "qXfer:siginfo:write", PACKET_DISABLE, remote_supported_packet,
@@ -9817,6 +9824,18 @@ remote_target::check_binary_download (CORE_ADDR addr)
     }
 }
 
+/* Determine whether the remote target operates in non-stop mode.
+   Returns true if the target advertises AlwaysNonStop, and supports
+   QNonStop for asynchronous execution and stop notifications.  */
+
+bool
+remote_target::always_non_stop_p ()
+{
+  return ((m_features.packet_support (PACKET_QNonStop) == PACKET_ENABLE)
+	  && (m_features.packet_support (PACKET_AlwaysNonStop_feature)
+	      == PACKET_ENABLE));
+}
+
 /* Helper function to resize the payload in order to try to get a good
    alignment.  We try to write an amount of data such that the next write will
    start on an address aligned on REMOTE_ALIGN_WRITES.  */
@@ -17073,6 +17092,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
 
   add_packet_config_cmd (PACKET_multi_wp_addr,
 			 "multi-wp-addr", "multiple-watchpoint-addresses", 0);
+  add_packet_config_cmd (PACKET_AlwaysNonStop_feature,
+			 "AlwaysNonStop", "always-non-stop-feature", 0);
 
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
diff --git a/gdb/testsuite/gdb.server/enable-always-non-stop.exp b/gdb/testsuite/gdb.server/enable-always-non-stop.exp
new file mode 100644
index 00000000000..c05f58847bb
--- /dev/null
+++ b/gdb/testsuite/gdb.server/enable-always-non-stop.exp
@@ -0,0 +1,56 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test that the --enable-packet=AlwaysNonStop gdbserver command line option
+# causes GDB to operate gdbserver with target-non-stop mode enabled.
+
+load_lib gdbserver-support.exp
+
+require allow_gdbserver_tests
+
+standard_testfile server.c
+
+if { [build_executable "failed to prepare" $testfile $srcfile] == -1 } {
+    return
+}
+set target_binfile [gdb_remote_download target $binfile]
+
+save_vars { ::GDBFLAGS } {
+    if { ![is_remote host] && ![is_remote target] } {
+	set ::GDBFLAGS "$::GDBFLAGS -ex \"set sysroot\""
+    }
+    clean_restart $::testfile
+}
+gdb_test "disconnect" ".*"
+
+# Start gdbserver with --enable-packet=AlwaysNonStop
+set res [gdbserver_start "--enable-packet=AlwaysNonStop" $::target_binfile]
+set protocol [lindex $res 0]
+set port [lindex $res 1]
+
+set res [gdb_target_cmd $protocol $port]
+if { ![gdb_assert {$res == 0} "connect"] } {
+    return
+}
+
+# Check that target-non-stop is on
+gdb_test "maint show target-non-stop" \
+    "Whether the target is always in non-stop mode is auto \\(currently on\\)\\." \
+    "verify target-non-stop is on"
+
+# continue to end
+gdb_continue_to_end
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 4a727c465f0..614e807271f 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -147,6 +147,9 @@ bool disable_packet_qC;
 bool disable_packet_qfThreadInfo;
 bool disable_packet_T;
 
+/* Set if you want to enable optional packets or extensions via CLI.  */
+bool enable_always_non_stop;
+
 static unsigned char *mem_buf;
 
 /* A sub-class of 'struct notif_event' for stop, holding information
@@ -838,10 +841,19 @@ handle_general_set (char *own_buf)
 	}
 
       req_str = req ? "non-stop" : "all-stop";
-      if (the_target->start_non_stop (req == 1) != 0)
+
+      try
 	{
-	  fprintf (stderr, "Setting %s mode failed\n", req_str);
-	  write_enn (own_buf);
+	  if (the_target->start_non_stop (req == 1) != 0)
+	    {
+	      sprintf (own_buf, "E.Setting %s mode failed.", req_str);
+	      return;
+	    }
+	}
+      catch (const gdb_exception_error &exception)
+	{
+	  /* The target rejected the setting, forward the error message.  */
+	  sprintf (own_buf, "E.%s", exception.what ());
 	  return;
 	}
 
@@ -2812,7 +2824,11 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 	strcat (own_buf, ";exec-events+");
 
       if (target_supports_non_stop ())
-	strcat (own_buf, ";QNonStop+");
+	{
+	  strcat (own_buf, ";QNonStop+");
+	  if (target_always_non_stop ())
+	    strcat (own_buf, ";AlwaysNonStop+");
+	}
 
       if (target_supports_disable_randomization ())
 	strcat (own_buf, ";QDisableRandomization+");
@@ -2876,6 +2892,19 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (cs.single_inferior_argument)
 	strcat (own_buf, ";single-inf-arg+");
 
+      /* If the target requires non-stop mode, initialize it now rather
+	 than waiting for GDB to request it via QNonStop:1.  */
+      if (target_supports_non_stop () && target_always_non_stop ())
+	{
+	  if (the_target->start_non_stop (true) != 0)
+	    error ("Cannot initialize target: requires non-stop mode but "
+		   "non-stop mode initialization failed.");
+
+	  non_stop = true;
+	  remote_debug_printf ("Target requires non-stop mode: "
+			       "enabled at startup.\n");
+	}
+
       /* Reinitialize components as needed for the new connection.  */
       hostio_handle_new_gdb_connection ();
       target_handle_new_gdb_connection ();
@@ -3931,6 +3960,10 @@ gdbserver_usage (FILE *stream)
 	   "                          Options:\n"
 	   "                            vCont, vConts, T, Tthread, qC, qfThreadInfo and\n"
 	   "                            threads (disable all threading packets).\n"
+	   "  --enable-packet=OPT1[,OPT2,...]\n"
+	   "                        Enable support for RSP packets or extensions.\n"
+	   "                          Options:\n"
+	   "                            AlwaysNonStop.\n"
 	   "\n"
 	   "For more information, consult the GDB manual (available as on-line \n"
 	   "info or a printed manual).\n");
@@ -3951,6 +3984,13 @@ gdbserver_show_disableable (FILE *stream)
 	   "  T           \tAll 'T' packets\n");
 }
 
+static void
+gdbserver_show_enableable (FILE *stream)
+{
+  fprintf (stream, "Enableable packets:\n"
+	   "  AlwaysNonStop\tForce AlwaysNonStop extension\n");
+}
+
 /* Start up the event loop.  This is the entry point to the event
    loop.  */
 
@@ -4186,7 +4226,7 @@ captured_main (int argc, char *argv[])
 
   enum opts { OPT_VERSION = 1, OPT_HELP, OPT_ATTACH, OPT_MULTI, OPT_WRAPPER,
     OPT_DEBUG, OPT_DEBUG_FILE, OPT_DEBUG_FORMAT, OPT_DISABLE_PACKET,
-    OPT_DISABLE_RANDOMIZATION, OPT_NO_DISABLE_RANDOMIZATION,
+    OPT_ENABLE_PACKET, OPT_DISABLE_RANDOMIZATION, OPT_NO_DISABLE_RANDOMIZATION,
     OPT_STARTUP_WITH_SHELL, OPT_NO_STARTUP_WITH_SHELL, OPT_ONCE,
     OPT_SELFTEST, OPT_NO_ESCAPE
   };
@@ -4204,6 +4244,9 @@ captured_main (int argc, char *argv[])
       /* --disable-packet is optional_argument only so that we can print a
 	 better help list when the argument is missing.  */
       {"disable-packet", optional_argument, nullptr, OPT_DISABLE_PACKET},
+      /* --enable-packet is optional_argument only so that we can print a
+	 better help list when the argument is missing.  */
+      {"enable-packet", optional_argument, nullptr, OPT_ENABLE_PACKET},
       {"disable-randomization", no_argument, nullptr,
        OPT_DISABLE_RANDOMIZATION},
       {"no-disable-randomization", no_argument, nullptr,
@@ -4411,6 +4454,32 @@ captured_main (int argc, char *argv[])
 	  }
 	  break;
 
+	case OPT_ENABLE_PACKET:
+	  {
+	    char *packets = optarg;
+	    if (packets == nullptr)
+	      {
+		gdbserver_show_enableable (stdout);
+		exit (1);
+	      }
+	    char *saveptr;
+	    for (char *tok = strtok_r (packets, ",", &saveptr);
+		 tok != nullptr;
+		 tok = strtok_r (nullptr, ",", &saveptr))
+	      {
+		if (streq ("AlwaysNonStop", tok))
+		  enable_always_non_stop = true;
+		else
+		  {
+		    fprintf (stderr, "Don't know how to enable \"%s\".\n\n",
+			     tok);
+		    gdbserver_show_enableable (stderr);
+		    exit (1);
+		  }
+	      }
+	  }
+	  break;
+
 	case OPT_DISABLE_RANDOMIZATION:
 	  cs.disable_randomization = 1;
 	  break;
diff --git a/gdbserver/server.h b/gdbserver/server.h
index bbba3650de7..4238882dfc7 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -74,6 +74,8 @@ extern bool disable_packet_qC;
 extern bool disable_packet_qfThreadInfo;
 extern bool disable_packet_T;
 
+extern bool enable_always_non_stop;
+
 extern bool run_once;
 extern bool non_stop;
 
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 43ef421df74..6ce584c8aba 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -498,6 +498,12 @@ process_stratum_target::supports_non_stop ()
   return false;
 }
 
+bool
+process_stratum_target::always_non_stop ()
+{
+  return enable_always_non_stop;
+}
+
 bool
 process_stratum_target::async (bool enable)
 {
diff --git a/gdbserver/target.h b/gdbserver/target.h
index 82eab9d1243..cdd1e26f2d6 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -260,6 +260,9 @@ class process_stratum_target
   /* Return true if non-stop mode is supported.  */
   virtual bool supports_non_stop ();
 
+  /* Return true if target must run in non-stop mode.  */
+  virtual bool always_non_stop ();
+
   /* Enables async target events.  Returns the previous enable
      state.  */
   virtual bool async (bool enable);
@@ -583,6 +586,9 @@ int kill_inferior (process_info *proc);
 #define target_supports_non_stop() \
   the_target->supports_non_stop ()
 
+#define target_always_non_stop() \
+  the_target->always_non_stop ()
+
 #define target_async(enable) \
   the_target->async (enable)
 
-- 
2.43.0

Intel Deutschland GmbH

Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 89 991 430, www.intel.de
Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell
Chairperson of the Supervisory Board: Nicole Lau
Registered Seat: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.