[PATCH] Create API for checking and clearing GDB connection status

Keith Packard via posted 1 patch 1 week ago
Test checkpatch passed
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20210112210418.1471412-1-keithp@keithp.com
Maintainers: "Philippe Mathieu-Daudé" <philmd@redhat.com>, "Alex Bennée" <alex.bennee@linaro.org>
gdbstub.c | 51 +++++++++++++++++++++++++++++++++------------------
1 file changed, 33 insertions(+), 18 deletions(-)

[PATCH] Create API for checking and clearing GDB connection status

Posted by Keith Packard via 1 week ago
When checking whether there is a live gdb connection, code shouldn't
use 'gdbserver_state.init' as that value is set when the
gdbserver_state structure is initialized in init_gdbserver_state, not
when the gdb socket has a valid connection.

I've created two new functions to manage the gdb connection status:

	/* Check whether GDB is currently connected */
	static int gdb_is_connected(void)

	#ifdef CONFIG_USER_ONLY

	/* Close GDB connection */
	static void gdb_disconnect(void)

	#endif

The first checks whether there is an active GDB connection, the second
closes that connection and resets the connection status indication.

The 'handle_detach' function used 'gdbserver_state.c_cpu' as an
indication of whether there is a connection, so I've used the same in
gdb_is_connected as that is independent of CONFIG_USER_ONLY.

This avoids a segfault when qemu is run with the '-s' flag (create a
gdb protocol socket), but without the '-S' flag (delay until 'c'
command is received).

Signed-off-by: Keith Packard <keithp@keithp.com>
---
 gdbstub.c | 51 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 33 insertions(+), 18 deletions(-)

diff --git a/gdbstub.c b/gdbstub.c
index d99bc0bf2e..8ee7e442d5 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -413,8 +413,28 @@ static void reset_gdbserver_state(void)
 
 bool gdb_has_xml;
 
+/* Check whether GDB is currently connected */
+static int gdb_is_connected(void)
+{
+    /*
+     * XXX c_cpu is NULL until gdb_accept_init has been called, so use
+     * this as a proxy for whether the gdb connection is active
+     */
+    return gdbserver_state.c_cpu != NULL;
+}
+
 #ifdef CONFIG_USER_ONLY
 
+/* Close GDB connection */
+static void gdb_disconnect(void)
+{
+    if (gdb_is_connected()) {
+        close(gdbserver_state.fd);
+        gdbserver_state.fd = -1;
+        gdbserver_state.c_cpu = NULL;
+    }
+}
+
 static int get_char(void)
 {
     uint8_t ch;
@@ -424,12 +444,11 @@ static int get_char(void)
         ret = qemu_recv(gdbserver_state.fd, &ch, 1, 0);
         if (ret < 0) {
             if (errno == ECONNRESET)
-                gdbserver_state.fd = -1;
+                gdb_disconnect();
             if (errno != EINTR)
                 return -1;
         } else if (ret == 0) {
-            close(gdbserver_state.fd);
-            gdbserver_state.fd = -1;
+            gdb_disconnect();
             return -1;
         } else {
             break;
@@ -2796,7 +2815,7 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va)
     target_ulong addr;
     uint64_t i64;
 
-    if (!gdbserver_state.init) {
+    if (!gdb_is_connected()) {
         return;
     }
 
@@ -3025,9 +3044,9 @@ void gdb_exit(CPUArchState *env, int code)
   if (gdbserver_state.socket_path) {
       unlink(gdbserver_state.socket_path);
   }
-  if (gdbserver_state.fd < 0) {
-      return;
-  }
+    if (!gdb_is_connected()) {
+        return;
+    }
 #endif
 
   trace_gdbstub_op_exiting((uint8_t)code);
@@ -3072,7 +3091,7 @@ gdb_handlesig(CPUState *cpu, int sig)
     char buf[256];
     int n;
 
-    if (!gdbserver_state.init || gdbserver_state.fd < 0) {
+    if (!gdb_is_connected()) {
         return sig;
     }
 
@@ -3086,14 +3105,14 @@ gdb_handlesig(CPUState *cpu, int sig)
     }
     /* put_packet() might have detected that the peer terminated the
        connection.  */
-    if (gdbserver_state.fd < 0) {
+    if (!gdb_is_connected()) {
         return sig;
     }
 
     sig = 0;
     gdbserver_state.state = RS_IDLE;
     gdbserver_state.running_state = 0;
-    while (gdbserver_state.running_state == 0) {
+    while (gdbserver_state.running_state == 0 && gdb_is_connected()) {
         n = read(gdbserver_state.fd, buf, 256);
         if (n > 0) {
             int i;
@@ -3104,10 +3123,7 @@ gdb_handlesig(CPUState *cpu, int sig)
         } else {
             /* XXX: Connection closed.  Should probably wait for another
                connection before continuing.  */
-            if (n == 0) {
-                close(gdbserver_state.fd);
-            }
-            gdbserver_state.fd = -1;
+            gdb_disconnect();
             return sig;
         }
     }
@@ -3121,7 +3137,7 @@ void gdb_signalled(CPUArchState *env, int sig)
 {
     char buf[4];
 
-    if (!gdbserver_state.init || gdbserver_state.fd < 0) {
+    if (!gdb_is_connected()) {
         return;
     }
 
@@ -3280,11 +3296,10 @@ int gdbserver_start(const char *port_or_path)
 /* Disable gdb stub for child processes.  */
 void gdbserver_fork(CPUState *cpu)
 {
-    if (!gdbserver_state.init || gdbserver_state.fd < 0) {
+    if (!gdb_is_connected()) {
         return;
     }
-    close(gdbserver_state.fd);
-    gdbserver_state.fd = -1;
+    gdb_disconnect();
     cpu_breakpoint_remove_all(cpu, BP_GDB);
     cpu_watchpoint_remove_all(cpu, BP_GDB);
 }
-- 
2.30.0