From 860789c7d5d6ef2f60e757feb21845bb230582a8 Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Fri, 8 Aug 2014 15:37:41 +0100 Subject: [PATCH] Use exceptions and cleanups in gdbserver 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 | 13 +++ gdb/gdbserver/server.c | 206 ++++++++++++++++++++++------------------ gdb/gdbserver/server.h | 7 +- gdb/gdbserver/utils.c | 10 +- 4 files changed, 132 insertions(+), 104 deletions(-) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 89cafa69a2..caa243da5d 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,16 @@ +2014-08-29 Gary Benson + + * 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 * Makefile.in (SFILES): Add common/common-exceptions.c. diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index cf1dffee12..81cb2e1ca5 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -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) + { + /* 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 (exit_requested || run_once) + throw_quit ("Quit"); + + 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); + + if (tracing) + { + if (disconnected_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 (start_non_stop (1)) + non_stop = 1; + + /* Detaching implicitly resumes all threads; + simply disconnecting does not. */ + } + } + else + { + fprintf (stderr, + "Disconnected tracing disabled; " + "stopping trace run.\n"); + stop_tracing (); + } + } + } + + if (exception.reason == RETURN_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 (); - - /* 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); - } - } - - 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); - - if (tracing) - { - if (disconnected_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 (start_non_stop (1)) - non_stop = 1; - - /* Detaching implicitly resumes all threads; simply - disconnecting does not. */ - } - } - else - { - fprintf (stderr, - "Disconnected tracing disabled; stopping trace run.\n"); - stop_tracing (); - } - } } } +/* 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 diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index e6b2277450..812f5338fd 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -29,8 +29,6 @@ gdb_static_assert (sizeof (CORE_ADDR) >= sizeof (void *)); #include "version.h" -#include - #ifdef HAVE_ALLOCA_H #include #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 */ diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c index e310b9e140..2351a46266 100644 --- a/gdb/gdbserver/utils.c +++ b/gdb/gdbserver/utils.c @@ -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 }