Use exceptions and cleanups in gdbserver
authorGary Benson <gbenson@redhat.com>
Fri, 8 Aug 2014 14:37:41 +0000 (15:37 +0100)
committerGary Benson <gbenson@redhat.com>
Fri, 29 Aug 2014 09:53:56 +0000 (10:53 +0100)
This commit replaces the hacky "exception" system in gdbserver with
the exceptions and cleanups subsystem from GDB.

Only the catch/cleanup code in what was "main" has been updated to
use the new system.  Other parts of gdbserver can now be converted
to use TRY_CATCH and cleanups on an as-needed basis.

A side-effect of this commit is that some error messages will change
slightly, and in cases with multiple errors the error messages will
be printed in a different order.

gdb/gdbserver/ChangeLog:

* server.h (setjmp.h): Do not include.
(toplevel): Do not declare.
(common-exceptions.h): Include.
(cleanups.h): Likewise.
* server.c (toplevel): Do not define.
(exit_code): New static global.
(detach_or_kill_for_exit_cleanup): New function.
(main): New function.  Original main renamed to...
(captured_main): New function.
* utils.c (verror) [!IN_PROCESS_AGENT]: Use throw_verror.

gdb/gdbserver/ChangeLog
gdb/gdbserver/server.c
gdb/gdbserver/server.h
gdb/gdbserver/utils.c

index 89cafa6..caa243d 100644 (file)
@@ -1,5 +1,18 @@
 2014-08-29  Gary Benson  <gbenson@redhat.com>
 
+       * server.h (setjmp.h): Do not include.
+       (toplevel): Do not declare.
+       (common-exceptions.h): Include.
+       (cleanups.h): Likewise.
+       * server.c (toplevel): Do not define.
+       (exit_code): New static global.
+       (detach_or_kill_for_exit_cleanup): New function.
+       (main): New function.  Original main renamed to...
+       (captured_main): New function.
+       * utils.c (verror) [!IN_PROCESS_AGENT]: Use throw_verror.
+
+2014-08-29  Gary Benson  <gbenson@redhat.com>
+
        * Makefile.in (SFILES): Add common/common-exceptions.c.
        (OBS): Add common-exceptions.o.
        (common-exceptions.o): New rule.
index cf1dffe..81cb2e1 100644 (file)
@@ -76,8 +76,6 @@ int pass_signals[GDB_SIGNAL_LAST];
 int program_signals[GDB_SIGNAL_LAST];
 int program_signals_p;
 
-jmp_buf toplevel;
-
 /* The PID of the originally created or attached inferior.  Used to
    send signals to the process when GDB sends us an asynchronous interrupt
    (user hitting Control-C in the client), and to wait for the child to exit
@@ -3013,8 +3011,34 @@ detach_or_kill_for_exit (void)
   for_each_inferior (&all_processes, detach_or_kill_inferior_callback);
 }
 
-int
-main (int argc, char *argv[])
+/* Value that will be passed to exit(3) when gdbserver exits.  */
+static int exit_code;
+
+/* Cleanup version of detach_or_kill_for_exit.  */
+
+static void
+detach_or_kill_for_exit_cleanup (void *ignore)
+{
+  volatile struct gdb_exception exception;
+
+  TRY_CATCH (exception, RETURN_MASK_ALL)
+    {
+      detach_or_kill_for_exit ();
+    }
+
+  if (exception.reason < 0)
+    {
+      fflush (stdout);
+      fprintf (stderr, "Detach or kill failed: %s\n", exception.message);
+      exit_code = 1;
+    }
+}
+
+/* Main function.  This is called by the real "main" function,
+   wrapped in a TRY_CATCH that handles any uncaught exceptions.  */
+
+static void ATTRIBUTE_NORETURN
+captured_main (int argc, char *argv[])
 {
   int bad_attach;
   int pid;
@@ -3138,12 +3162,6 @@ main (int argc, char *argv[])
       continue;
     }
 
-  if (setjmp (toplevel))
-    {
-      fprintf (stderr, "Exiting\n");
-      exit (1);
-    }
-
   port = *next_arg;
   next_arg++;
   if (port == NULL || (!attach && !multi_mode && *next_arg == NULL))
@@ -3226,6 +3244,7 @@ main (int argc, char *argv[])
       last_status.value.integer = 0;
       last_ptid = minus_one_ptid;
     }
+  make_cleanup (detach_or_kill_for_exit_cleanup, NULL);
 
   initialize_notif ();
 
@@ -3234,19 +3253,6 @@ main (int argc, char *argv[])
      shared library event" notice on gdb side.  */
   dlls_changed = 0;
 
-  if (setjmp (toplevel))
-    {
-      /* If something fails and longjmps while detaching or killing
-        inferiors, we'd end up here again, stuck in an infinite loop
-        trap.  Be sure that if that happens, we exit immediately
-        instead.  */
-      if (setjmp (toplevel) == 0)
-       detach_or_kill_for_exit ();
-      else
-       fprintf (stderr, "Detach or kill failed.  Exiting\n");
-      exit (1);
-    }
-
   if (last_status.kind == TARGET_WAITKIND_EXITED
       || last_status.kind == TARGET_WAITKIND_SIGNALLED)
     was_running = 0;
@@ -3254,13 +3260,12 @@ main (int argc, char *argv[])
     was_running = 1;
 
   if (!was_running && !multi_mode)
-    {
-      fprintf (stderr, "No program to debug.  GDBserver exiting.\n");
-      exit (1);
-    }
+    error ("No program to debug");
 
   while (1)
     {
+      volatile struct gdb_exception exception;
+
       noack_mode = 0;
       multi_process = 0;
       /* Be sure we're out of tfind mode.  */
@@ -3268,82 +3273,97 @@ main (int argc, char *argv[])
 
       remote_open (port);
 
-      if (setjmp (toplevel) != 0)
+      TRY_CATCH (exception, RETURN_MASK_ERROR)
        {
-         /* An error occurred.  */
-         if (response_needed)
-           {
-             write_enn (own_buf);
-             putpkt (own_buf);
-           }
-       }
-
-      /* Wait for events.  This will return when all event sources are
-        removed from the event loop.  */
-      start_event_loop ();
+         /* Wait for events.  This will return when all event sources
+            are removed from the event loop.  */
+         start_event_loop ();
 
-      /* If an exit was requested (using the "monitor exit" command),
-        terminate now.  The only other way to get here is for
-        getpkt to fail; close the connection and reopen it at the
-        top of the loop.  */
+         /* If an exit was requested (using the "monitor exit"
+            command), terminate now.  The only other way to get
+            here is for getpkt to fail; close the connection
+            and reopen it at the top of the loop.  */
 
-      if (exit_requested || run_once)
-       {
-         /* If something fails and longjmps while detaching or
-            killing inferiors, we'd end up here again, stuck in an
-            infinite loop trap.  Be sure that if that happens, we
-            exit immediately instead.  */
-         if (setjmp (toplevel) == 0)
-           {
-             detach_or_kill_for_exit ();
-             exit (0);
-           }
-         else
-           {
-             fprintf (stderr, "Detach or kill failed.  Exiting\n");
-             exit (1);
-           }
-       }
+         if (exit_requested || run_once)
+           throw_quit ("Quit");
 
-      fprintf (stderr,
-              "Remote side has terminated connection.  "
-              "GDBserver will reopen the connection.\n");
+         fprintf (stderr,
+                  "Remote side has terminated connection.  "
+                  "GDBserver will reopen the connection.\n");
 
-      /* Get rid of any pending statuses.  An eventual reconnection
-        (by the same GDB instance or another) will refresh all its
-        state from scratch.  */
-      discard_queued_stop_replies (-1);
-      for_each_inferior (&all_threads, clear_pending_status_callback);
+         /* Get rid of any pending statuses.  An eventual reconnection
+            (by the same GDB instance or another) will refresh all its
+            state from scratch.  */
+         discard_queued_stop_replies (-1);
+         for_each_inferior (&all_threads,
+                            clear_pending_status_callback);
 
-      if (tracing)
-       {
-         if (disconnected_tracing)
+         if (tracing)
            {
-             /* Try to enable non-stop/async mode, so we we can both
-                wait for an async socket accept, and handle async
-                target events simultaneously.  There's also no point
-                either in having the target always stop all threads,
-                when we're going to pass signals down without
-                informing GDB.  */
-             if (!non_stop)
+             if (disconnected_tracing)
                {
-                 if (start_non_stop (1))
-                   non_stop = 1;
+                 /* Try to enable non-stop/async mode, so we we can
+                    both wait for an async socket accept, and handle
+                    async target events simultaneously.  There's also
+                    no point either in having the target always stop
+                    all threads, when we're going to pass signals
+                    down without informing GDB.  */
+                 if (!non_stop)
+                   {
+                     if (start_non_stop (1))
+                       non_stop = 1;
 
-                 /* Detaching implicitly resumes all threads; simply
-                    disconnecting does not.  */
+                     /* Detaching implicitly resumes all threads;
+                        simply disconnecting does not.  */
+                   }
+               }
+             else
+               {
+                 fprintf (stderr,
+                          "Disconnected tracing disabled; "
+                          "stopping trace run.\n");
+                 stop_tracing ();
                }
            }
-         else
+       }
+
+      if (exception.reason == RETURN_ERROR)
+       {
+         if (response_needed)
            {
-             fprintf (stderr,
-                      "Disconnected tracing disabled; stopping trace run.\n");
-             stop_tracing ();
+             write_enn (own_buf);
+             putpkt (own_buf);
            }
        }
     }
 }
 
+/* Main function.  */
+
+int
+main (int argc, char *argv[])
+{
+  volatile struct gdb_exception exception;
+
+  TRY_CATCH (exception, RETURN_MASK_ALL)
+    {
+      captured_main (argc, argv);
+    }
+
+  /* captured_main should never return.  */
+  gdb_assert (exception.reason < 0);
+
+  if (exception.reason == RETURN_ERROR)
+    {
+      fflush (stdout);
+      fprintf (stderr, "%s\n", exception.message);
+      fprintf (stderr, "Exiting\n");
+      exit_code = 1;
+    }
+
+  exit (exit_code);
+}
+
 /* Skip PACKET until the next semi-colon (or end of string).  */
 
 static void
index e6b2277..812f533 100644 (file)
@@ -29,8 +29,6 @@ gdb_static_assert (sizeof (CORE_ADDR) >= sizeof (void *));
 
 #include "version.h"
 
-#include <setjmp.h>
-
 #ifdef HAVE_ALLOCA_H
 #include <alloca.h>
 #endif
@@ -89,8 +87,6 @@ extern int pass_signals[];
 extern int program_signals[];
 extern int program_signals_p;
 
-extern jmp_buf toplevel;
-
 extern int disable_packet_vCont;
 extern int disable_packet_Tthread;
 extern int disable_packet_qC;
@@ -129,4 +125,7 @@ extern int handle_target_event (int err, gdb_client_data client_data);
    as large as the largest register set supported by gdbserver.  */
 #define PBUFSIZ 16384
 
+#include "common-exceptions.h"
+#include "cleanups.h"
+
 #endif /* SERVER_H */
index e310b9e..2351a46 100644 (file)
@@ -76,17 +76,13 @@ perror_with_name (const char *string)
 void
 verror (const char *string, va_list args)
 {
-#ifndef IN_PROCESS_AGENT
-  extern jmp_buf toplevel;
-#endif
-
+#ifdef IN_PROCESS_AGENT
   fflush (stdout);
   vfprintf (stderr, string, args);
   fprintf (stderr, "\n");
-#ifndef IN_PROCESS_AGENT
-  longjmp (toplevel, 1);
-#else
   exit (1);
+#else
+  throw_verror (GENERIC_ERROR, string, args);
 #endif
 }