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.
This commit is contained in:
parent
ff55e1b548
commit
860789c7d5
|
@ -1,3 +1,16 @@
|
||||||
|
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>
|
2014-08-29 Gary Benson <gbenson@redhat.com>
|
||||||
|
|
||||||
* Makefile.in (SFILES): Add common/common-exceptions.c.
|
* Makefile.in (SFILES): Add common/common-exceptions.c.
|
||||||
|
|
|
@ -76,8 +76,6 @@ int pass_signals[GDB_SIGNAL_LAST];
|
||||||
int program_signals[GDB_SIGNAL_LAST];
|
int program_signals[GDB_SIGNAL_LAST];
|
||||||
int program_signals_p;
|
int program_signals_p;
|
||||||
|
|
||||||
jmp_buf toplevel;
|
|
||||||
|
|
||||||
/* The PID of the originally created or attached inferior. Used to
|
/* The PID of the originally created or attached inferior. Used to
|
||||||
send signals to the process when GDB sends us an asynchronous interrupt
|
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
|
(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);
|
for_each_inferior (&all_processes, detach_or_kill_inferior_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
/* Value that will be passed to exit(3) when gdbserver exits. */
|
||||||
main (int argc, char *argv[])
|
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 bad_attach;
|
||||||
int pid;
|
int pid;
|
||||||
|
@ -3138,12 +3162,6 @@ main (int argc, char *argv[])
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setjmp (toplevel))
|
|
||||||
{
|
|
||||||
fprintf (stderr, "Exiting\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
port = *next_arg;
|
port = *next_arg;
|
||||||
next_arg++;
|
next_arg++;
|
||||||
if (port == NULL || (!attach && !multi_mode && *next_arg == NULL))
|
if (port == NULL || (!attach && !multi_mode && *next_arg == NULL))
|
||||||
|
@ -3226,6 +3244,7 @@ main (int argc, char *argv[])
|
||||||
last_status.value.integer = 0;
|
last_status.value.integer = 0;
|
||||||
last_ptid = minus_one_ptid;
|
last_ptid = minus_one_ptid;
|
||||||
}
|
}
|
||||||
|
make_cleanup (detach_or_kill_for_exit_cleanup, NULL);
|
||||||
|
|
||||||
initialize_notif ();
|
initialize_notif ();
|
||||||
|
|
||||||
|
@ -3234,19 +3253,6 @@ main (int argc, char *argv[])
|
||||||
shared library event" notice on gdb side. */
|
shared library event" notice on gdb side. */
|
||||||
dlls_changed = 0;
|
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
|
if (last_status.kind == TARGET_WAITKIND_EXITED
|
||||||
|| last_status.kind == TARGET_WAITKIND_SIGNALLED)
|
|| last_status.kind == TARGET_WAITKIND_SIGNALLED)
|
||||||
was_running = 0;
|
was_running = 0;
|
||||||
|
@ -3254,13 +3260,12 @@ main (int argc, char *argv[])
|
||||||
was_running = 1;
|
was_running = 1;
|
||||||
|
|
||||||
if (!was_running && !multi_mode)
|
if (!was_running && !multi_mode)
|
||||||
{
|
error ("No program to debug");
|
||||||
fprintf (stderr, "No program to debug. GDBserver exiting.\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
volatile struct gdb_exception exception;
|
||||||
|
|
||||||
noack_mode = 0;
|
noack_mode = 0;
|
||||||
multi_process = 0;
|
multi_process = 0;
|
||||||
/* Be sure we're out of tfind mode. */
|
/* Be sure we're out of tfind mode. */
|
||||||
|
@ -3268,82 +3273,97 @@ main (int argc, char *argv[])
|
||||||
|
|
||||||
remote_open (port);
|
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)
|
if (response_needed)
|
||||||
{
|
{
|
||||||
write_enn (own_buf);
|
write_enn (own_buf);
|
||||||
putpkt (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). */
|
/* Skip PACKET until the next semi-colon (or end of string). */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -29,8 +29,6 @@ gdb_static_assert (sizeof (CORE_ADDR) >= sizeof (void *));
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include <setjmp.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_ALLOCA_H
|
#ifdef HAVE_ALLOCA_H
|
||||||
#include <alloca.h>
|
#include <alloca.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -89,8 +87,6 @@ extern int pass_signals[];
|
||||||
extern int program_signals[];
|
extern int program_signals[];
|
||||||
extern int program_signals_p;
|
extern int program_signals_p;
|
||||||
|
|
||||||
extern jmp_buf toplevel;
|
|
||||||
|
|
||||||
extern int disable_packet_vCont;
|
extern int disable_packet_vCont;
|
||||||
extern int disable_packet_Tthread;
|
extern int disable_packet_Tthread;
|
||||||
extern int disable_packet_qC;
|
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. */
|
as large as the largest register set supported by gdbserver. */
|
||||||
#define PBUFSIZ 16384
|
#define PBUFSIZ 16384
|
||||||
|
|
||||||
|
#include "common-exceptions.h"
|
||||||
|
#include "cleanups.h"
|
||||||
|
|
||||||
#endif /* SERVER_H */
|
#endif /* SERVER_H */
|
||||||
|
|
|
@ -76,17 +76,13 @@ perror_with_name (const char *string)
|
||||||
void
|
void
|
||||||
verror (const char *string, va_list args)
|
verror (const char *string, va_list args)
|
||||||
{
|
{
|
||||||
#ifndef IN_PROCESS_AGENT
|
#ifdef IN_PROCESS_AGENT
|
||||||
extern jmp_buf toplevel;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fflush (stdout);
|
fflush (stdout);
|
||||||
vfprintf (stderr, string, args);
|
vfprintf (stderr, string, args);
|
||||||
fprintf (stderr, "\n");
|
fprintf (stderr, "\n");
|
||||||
#ifndef IN_PROCESS_AGENT
|
|
||||||
longjmp (toplevel, 1);
|
|
||||||
#else
|
|
||||||
exit (1);
|
exit (1);
|
||||||
|
#else
|
||||||
|
throw_verror (GENERIC_ERROR, string, args);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue