Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Burgess <aburgess@redhat.com>
To: gdb-patches@sourceware.org
Cc: Andrew Burgess <aburgess@redhat.com>
Subject: [PATCH] gdbserver: reset the report_thread_events feature for new connections
Date: Wed, 28 Oct 2026 17:41:54 +0000	[thread overview]
Message-ID: <7845b61991b27ed7e0b91237b00da2fcab3c2d8c.1793209296.git.aburgess@redhat.com> (raw)

I noticed that the report_thread_events feature flag (in gdbserver)
was not being reset to its default state for each new connection.

This flag is turned on when gdbserver receives a QThreadEvents:1
packet, and turned back off with a QThreadEvents:0 packet.  GDB
usually sends these packets before and after waiting for some event.

Normally, by the time GDB returns to a user prompt the QThreadEvents:0
packet will have been sent and report_thread_events will have been
returned to its default `off` state.

However, it is possible that if GDB broke the connection with
gdbserver unexpectedly, for example, if GDB crashed, or the connection
dropped, then gdbserver might be left with report_thread_events in the
`on` state.

If this happened, then when a new GDB reconnected, the flag should be
restored to its default `off` state.  This is what happens to other
similar flags, e.g. the noack_mode flag.  See the code in
gdbserver/server.cc in the captured_main function, at the top of the
'while (1)' loop; this is the code that resets flags for a new
connection.  The report_thread_events flag is missing from this block.

This commit adds the reset of report_thread_events to the top of this
while loop, ensuring the flag is reset on each new connection, and
also adds a test for this.

The test is a little artificial as we are trying to simulate an
unexpected disconnection, but in a controlled manner.  To do this I
manually send a QThreadEvents:1 packet to turn report_thread_events on
and then disconnect.  I then turn the thread-events-packet off and
reconnect.  Finally, I 'set remote debug on' and look in the packet
output for an indication that gdbserver is sending thread-event
packets.

Without the fix this test fails, but passes with the patch in this
commit.
---
 .../gdb.server/reconnect-thread-events.c      |  61 ++++++++
 .../gdb.server/reconnect-thread-events.exp    | 145 ++++++++++++++++++
 gdbserver/server.cc                           |   1 +
 3 files changed, 207 insertions(+)
 create mode 100644 gdb/testsuite/gdb.server/reconnect-thread-events.c
 create mode 100644 gdb/testsuite/gdb.server/reconnect-thread-events.exp

diff --git a/gdb/testsuite/gdb.server/reconnect-thread-events.c b/gdb/testsuite/gdb.server/reconnect-thread-events.c
new file mode 100644
index 00000000000..c939030451c
--- /dev/null
+++ b/gdb/testsuite/gdb.server/reconnect-thread-events.c
@@ -0,0 +1,61 @@
+/* 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/>.  */
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "gdb_watchdog.h"
+
+volatile int ready = 0;
+
+void
+breakpt_before_thread (void)
+{
+}
+
+void
+breakpt_after_thread (void)
+{
+}
+
+static void *
+worker (void *arg)
+{
+  while (!ready)
+    usleep (1000);
+  return NULL;
+}
+
+int
+main (void)
+{
+  pthread_t thr;
+
+  gdb_watchdog (120);
+
+  breakpt_before_thread ();
+
+  if (pthread_create (&thr, NULL, worker, NULL) != 0)
+    abort ();
+
+  breakpt_after_thread ();
+
+  ready = 1;
+  pthread_join (thr, NULL);
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.server/reconnect-thread-events.exp b/gdb/testsuite/gdb.server/reconnect-thread-events.exp
new file mode 100644
index 00000000000..4f84bdce11b
--- /dev/null
+++ b/gdb/testsuite/gdb.server/reconnect-thread-events.exp
@@ -0,0 +1,145 @@
+# 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 gdbserver correctly resets the report_thread_events flag
+# when a new connection is established.
+#
+# In a normal GDB session the report_thread_events will only be
+# switched on (via "QThreadEvents:1" packet) while GDB is waiting for
+# some command to complete, after which the flag is disabled again
+# using a "QThreadEvents:0" packet.
+#
+# However, if GDB unexpectedly disconnects while this feature is
+# turned on, then when a new GDB connects gdbserver should
+# automatically reset this flag.  At one point this was not happening.
+# This test exists to check for this case.
+#
+# Connect to gdbserver, manually send QThreadEvents:1 to latch the
+# flag on, disconnect, then reconnect with the thread-events packet
+# disabled.  If gdbserver fails to reset the flag, it will send
+# unsolicited T05create:; stop-reply packets for thread creation
+# events.  Detect this using 'set remote debug on'.
+
+require {!is_remote target} {!is_remote host}
+
+load_lib gdbserver-support.exp
+
+standard_testfile
+
+require allow_gdbserver_tests
+
+if { [build_executable "build executable" $testfile $srcfile \
+	  {debug pthreads}] == -1 } {
+    return
+}
+
+proc reconnection_and_thread_events {} {
+    global GDBFLAGS gdbserver_reconnect_p use_gdb_stub
+
+    save_vars { GDBFLAGS } {
+	# If possible set the sysroot to avoid copying files.
+	if { ![is_remote host] } {
+	    set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\""
+	}
+	clean_restart $::testfile
+
+	# Make sure we're disconnected, in case we're testing with an
+	# extended-remote board, therefore already connected.
+	gdb_test "disconnect" ".*"
+    }
+
+    save_vars { gdbserver_reconnect_p use_gdb_stub } {
+	# Setting this flag prevents gdbserver being started with the
+	# --once option; this is required so that we can reconnect.
+	set gdbserver_reconnect_p 1
+
+	# This test always starts gdbserver in --multi mode, so clear
+	# the use_gdb_stub global to allow gdb_start_cmd to work.
+	set use_gdb_stub 0
+
+	set res [gdbserver_start "--multi" "$::binfile"]
+	set gdbserver_protocol "extended-remote"
+	set gdbserver_gdbport [lindex $res 1]
+
+	with_test_prefix "first connection" {
+	    gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport
+
+	    # Start a new inferior.
+	    if { [gdb_start_cmd] < 0 } {
+		fail "start command"
+		return
+	    }
+
+	    # gdb_start_cmd doesn't consume the prompt.  Collect it now.
+	    gdb_test "" "" "wait for prompt after start"
+
+	    gdb_breakpoint "breakpt_before_thread"
+	    gdb_continue_to_breakpoint "before thread creation"
+
+	    # Directly send QThreadEvents:1 to latch the flag on in
+	    # gdbserver.  This is equivalent to what GDB does during resume
+	    # when thread events are needed, but avoids depending on infrun
+	    # internals that may also send QThreadEvents:0 before we can
+	    # disconnect.
+	    #
+	    # The point here is to emulate an abnormal disconnection; the
+	    # flag is turned on, but never turned off.
+	    gdb_test "maintenance packet QThreadEvents:1" \
+		"received: .*OK.*" \
+		"send QThreadEvents:1"
+
+	    gdb_test "disconnect" ".*"
+	}
+
+	with_test_prefix "second connection" {
+	    # Disable both thread-events and thread-options packets so
+	    # GDB will not send either on the new connection.
+	    gdb_test "set remote thread-events-packet off" ".*"
+	    gdb_test "set remote thread-options-packet off" ".*"
+
+	    # Reconnect.  At this point there is already an inferior
+	    # running, we can just pick up the debug session.
+	    gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport
+
+	    gdb_breakpoint "breakpt_after_thread"
+
+	    # Continue past the thread creation with debug remote enabled.
+	    # If gdbserver failed to reset the report_thread_events flag,
+	    # the thread creation will generate a T05create:; stop-reply
+	    # packet.
+	    gdb_test_no_output "set debug remote on"
+
+	    # Continue.  Process the output line by line to avoid buffering
+	    # excessive output.  Look for a 'T' packet mentioning the
+	    # 'create' stop reason, this should only happen when the
+	    # thread-events-packet is on.
+	    set saw_create false
+	    gdb_test_multiple "continue" \
+		"continue without thread-create events" -lbl {
+		    -re "\r\n\[^\r\n\]+Packet received: T\[^\r\n\]+create:\[^\r\n\]+(?=\r\n)" {
+			set saw_create true
+			exp_continue
+		    }
+		    -re "\r\n$::gdb_prompt $" {
+			gdb_assert { !$saw_create } $gdb_test_name
+		    }
+		}
+
+	    gdb_test_no_output -nopass "set debug remote off"
+	}
+    }
+}
+
+reconnection_and_thread_events
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 74774fcde7c..34a49cec746 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -4621,6 +4621,7 @@ captured_main (int argc, char *argv[])
       cs.report_fork_events = 0;
       cs.report_vfork_events = 0;
       cs.report_exec_events = 0;
+      cs.report_thread_events = 0;
       /* Be sure we're out of tfind mode.  */
       cs.current_traceframe = -1;
       cs.cont_thread = null_ptid;

base-commit: b2fe3a449cb5d130ea2fec65bb214601826cdeff
-- 
2.25.4


                 reply	other threads:[~2026-10-28 17:42 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7845b61991b27ed7e0b91237b00da2fcab3c2d8c.1793209296.git.aburgess@redhat.com \
    --to=aburgess@redhat.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox