From ff55e1b54898ca4e2a90c005749505e33e32215c Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Thu, 7 Aug 2014 16:29:19 +0100 Subject: [PATCH] Introduce common/common-exceptions.[ch] This commit moves the exception throwing and catching code into gdb/common/. All exception printing code remains in gdb/exceptions.[ch]. gdb/ChangeLog: * common/common-exceptions.h: New file. * common/common-exceptions.c: Likewise. * Makefile.in (SFILES): Add common/common-exceptions.c. (HFILES_NO_SRCDIR): Add common/common-exceptions.h. (COMMON_OBS): Add common-exceptions.o. (common-exceptions.o): New rule. * exceptions.h (common-exceptions.h): Include. (gdb_setjmp.h): Do not include. (return_reason): Moved to common-exceptions.h. (enum return_reason): Likewise. (RETURN_MASK): Likewise. (typedef return_mask): Likewise. (enum errors): Likewise. (struct gdb_exception): Likewise. (exceptions_state_mc_init): Likewise. (exceptions_state_mc_action_iter): Likewise. (exceptions_state_mc_action_iter_1): Likewise. (TRY_CATCH): Likewise. (throw_exception): Likewise. (throw_verror): Likewise. (throw_vquit): Likewise. (throw_error): Likewise. (throw_quit): Likewise. * exceptions.c (enum catcher_state): Moved to common-exceptions.c. (enum catcher_action): Likewise. (struct catcher): Likewise. (current_catcher): Likewise. (catcher_list_size): Likewise. (exceptions_state_mc_init): Likewise. (catcher_pop): Likewise. (exceptions_state_mc): Likewise. (exceptions_state_mc_action_iter): Likewise. (exceptions_state_mc_action_iter_1): Likewise. (throw_exception): Likewise. (exception_messages): Likewise. (exception_messages_size): Likewise. (throw_it): Likewise. (throw_verror): Likewise. (throw_vquit): Likewise. (throw_error): Likewise. (throw_quit): Likewise. (prepare_to_throw_exception): New function. gdb/gdbserver/ChangeLog: * Makefile.in (SFILES): Add common/common-exceptions.c. (OBS): Add common-exceptions.o. (common-exceptions.o): New rule. * utils.c (prepare_to_throw_exception): New function. --- gdb/ChangeLog | 45 +++++ gdb/Makefile.in | 12 +- gdb/common/common-exceptions.c | 308 +++++++++++++++++++++++++++++++++ gdb/common/common-exceptions.h | 185 ++++++++++++++++++++ gdb/exceptions.c | 282 +----------------------------- gdb/exceptions.h | 154 +---------------- gdb/gdbserver/ChangeLog | 7 + gdb/gdbserver/Makefile.in | 7 +- gdb/gdbserver/utils.c | 8 + 9 files changed, 570 insertions(+), 438 deletions(-) create mode 100644 gdb/common/common-exceptions.c create mode 100644 gdb/common/common-exceptions.h diff --git a/gdb/ChangeLog b/gdb/ChangeLog index fbe8e87a41..07f0c5e74a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,48 @@ +2014-08-29 Gary Benson + + * common/common-exceptions.h: New file. + * common/common-exceptions.c: Likewise. + * Makefile.in (SFILES): Add common/common-exceptions.c. + (HFILES_NO_SRCDIR): Add common/common-exceptions.h. + (COMMON_OBS): Add common-exceptions.o. + (common-exceptions.o): New rule. + * exceptions.h (common-exceptions.h): Include. + (gdb_setjmp.h): Do not include. + (return_reason): Moved to common-exceptions.h. + (enum return_reason): Likewise. + (RETURN_MASK): Likewise. + (typedef return_mask): Likewise. + (enum errors): Likewise. + (struct gdb_exception): Likewise. + (exceptions_state_mc_init): Likewise. + (exceptions_state_mc_action_iter): Likewise. + (exceptions_state_mc_action_iter_1): Likewise. + (TRY_CATCH): Likewise. + (throw_exception): Likewise. + (throw_verror): Likewise. + (throw_vquit): Likewise. + (throw_error): Likewise. + (throw_quit): Likewise. + * exceptions.c (enum catcher_state): Moved to common-exceptions.c. + (enum catcher_action): Likewise. + (struct catcher): Likewise. + (current_catcher): Likewise. + (catcher_list_size): Likewise. + (exceptions_state_mc_init): Likewise. + (catcher_pop): Likewise. + (exceptions_state_mc): Likewise. + (exceptions_state_mc_action_iter): Likewise. + (exceptions_state_mc_action_iter_1): Likewise. + (throw_exception): Likewise. + (exception_messages): Likewise. + (exception_messages_size): Likewise. + (throw_it): Likewise. + (throw_verror): Likewise. + (throw_vquit): Likewise. + (throw_error): Likewise. + (throw_quit): Likewise. + (prepare_to_throw_exception): New function. + 2014-08-29 Gary Benson * common/gdb_setjmp.h: New file. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index c886c5d0ba..fbcdfb3e3c 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -851,7 +851,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \ common/format.c common/filestuff.c btrace.c record-btrace.c ctf.c \ target/waitstatus.c common/print-utils.c common/rsp-low.c \ - common/errors.c common/common-debug.c + common/errors.c common/common-debug.c common/common-exceptions.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -937,7 +937,8 @@ ctf.h nat/i386-cpuid.h nat/i386-gcc-cpuid.h target/resume.h \ target/wait.h target/waitstatus.h nat/linux-nat.h nat/linux-waitpid.h \ common/print-utils.h common/rsp-low.h nat/i386-dregs.h x86-linux-nat.h \ i386-linux-nat.h common/common-defs.h common/errors.h common/common-types.h \ -common/common-debug.h common/cleanups.h common/gdb_setjmp.h +common/common-debug.h common/cleanups.h common/gdb_setjmp.h \ +common/common-exceptions.h # Header files that already have srcdir in them, or which are in objdir. @@ -1036,7 +1037,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ gdb_vecs.o jit.o progspace.o skip.o probe.o \ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \ format.o registry.o btrace.o record-btrace.o waitstatus.o \ - print-utils.o rsp-low.o errors.o common-debug.o debug.o + print-utils.o rsp-low.o errors.o common-debug.o debug.o \ + common-exceptions.o TSOBS = inflow.o @@ -2166,6 +2168,10 @@ cleanups.o: ${srcdir}/common/cleanups.c $(COMPILE) $(srcdir)/common/cleanups.c $(POSTCOMPILE) +common-exceptions.o: ${srcdir}/common/common-exceptions.c + $(COMPILE) $(srcdir)/common/common-exceptions.c + $(POSTCOMPILE) + # # gdb/target/ dependencies # diff --git a/gdb/common/common-exceptions.c b/gdb/common/common-exceptions.c new file mode 100644 index 0000000000..c382d8630b --- /dev/null +++ b/gdb/common/common-exceptions.c @@ -0,0 +1,308 @@ +/* Exception (throw catch) mechanism, for GDB, the GNU debugger. + + Copyright (C) 1986-2014 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +#include "common-defs.h" +#include "common-exceptions.h" +#include "cleanups.h" + +/* Possible catcher states. */ +enum catcher_state { + /* Initial state, a new catcher has just been created. */ + CATCHER_CREATED, + /* The catch code is running. */ + CATCHER_RUNNING, + CATCHER_RUNNING_1, + /* The catch code threw an exception. */ + CATCHER_ABORTING +}; + +/* Possible catcher actions. */ +enum catcher_action { + CATCH_ITER, + CATCH_ITER_1, + CATCH_THROWING +}; + +struct catcher +{ + enum catcher_state state; + /* Jump buffer pointing back at the exception handler. */ + SIGJMP_BUF buf; + /* Status buffer belonging to the exception handler. */ + volatile struct gdb_exception *exception; + /* Saved/current state. */ + int mask; + struct cleanup *saved_cleanup_chain; + /* Back link. */ + struct catcher *prev; +}; + +/* Where to go for throw_exception(). */ +static struct catcher *current_catcher; + +/* Return length of current_catcher list. */ + +static int +catcher_list_size (void) +{ + int size; + struct catcher *catcher; + + for (size = 0, catcher = current_catcher; + catcher != NULL; + catcher = catcher->prev) + ++size; + + return size; +} + +SIGJMP_BUF * +exceptions_state_mc_init (volatile struct gdb_exception *exception, + return_mask mask) +{ + struct catcher *new_catcher = XCNEW (struct catcher); + + /* Start with no exception, save it's address. */ + exception->reason = 0; + exception->error = GDB_NO_ERROR; + exception->message = NULL; + new_catcher->exception = exception; + + new_catcher->mask = mask; + + /* Prevent error/quit during FUNC from calling cleanups established + prior to here. */ + new_catcher->saved_cleanup_chain = save_cleanups (); + + /* Push this new catcher on the top. */ + new_catcher->prev = current_catcher; + current_catcher = new_catcher; + new_catcher->state = CATCHER_CREATED; + + return &new_catcher->buf; +} + +static void +catcher_pop (void) +{ + struct catcher *old_catcher = current_catcher; + + current_catcher = old_catcher->prev; + + /* Restore the cleanup chain, the error/quit messages, and the uiout + builder, to their original states. */ + + restore_cleanups (old_catcher->saved_cleanup_chain); + + xfree (old_catcher); +} + +/* Catcher state machine. Returns non-zero if the m/c should be run + again, zero if it should abort. */ + +static int +exceptions_state_mc (enum catcher_action action) +{ + switch (current_catcher->state) + { + case CATCHER_CREATED: + switch (action) + { + case CATCH_ITER: + /* Allow the code to run the catcher. */ + current_catcher->state = CATCHER_RUNNING; + return 1; + default: + internal_error (__FILE__, __LINE__, _("bad state")); + } + case CATCHER_RUNNING: + switch (action) + { + case CATCH_ITER: + /* No error/quit has occured. Just clean up. */ + catcher_pop (); + return 0; + case CATCH_ITER_1: + current_catcher->state = CATCHER_RUNNING_1; + return 1; + case CATCH_THROWING: + current_catcher->state = CATCHER_ABORTING; + /* See also throw_exception. */ + return 1; + default: + internal_error (__FILE__, __LINE__, _("bad switch")); + } + case CATCHER_RUNNING_1: + switch (action) + { + case CATCH_ITER: + /* The did a "break" from the inner while loop. */ + catcher_pop (); + return 0; + case CATCH_ITER_1: + current_catcher->state = CATCHER_RUNNING; + return 0; + case CATCH_THROWING: + current_catcher->state = CATCHER_ABORTING; + /* See also throw_exception. */ + return 1; + default: + internal_error (__FILE__, __LINE__, _("bad switch")); + } + case CATCHER_ABORTING: + switch (action) + { + case CATCH_ITER: + { + struct gdb_exception exception = *current_catcher->exception; + + if (current_catcher->mask & RETURN_MASK (exception.reason)) + { + /* Exit normally if this catcher can handle this + exception. The caller analyses the func return + values. */ + catcher_pop (); + return 0; + } + /* The caller didn't request that the event be caught, + relay the event to the next containing + catch_errors(). */ + catcher_pop (); + throw_exception (exception); + } + default: + internal_error (__FILE__, __LINE__, _("bad state")); + } + default: + internal_error (__FILE__, __LINE__, _("bad switch")); + } +} + +int +exceptions_state_mc_action_iter (void) +{ + return exceptions_state_mc (CATCH_ITER); +} + +int +exceptions_state_mc_action_iter_1 (void) +{ + return exceptions_state_mc (CATCH_ITER_1); +} + +/* Return EXCEPTION to the nearest containing catch_errors(). */ + +void +throw_exception (struct gdb_exception exception) +{ + prepare_to_throw_exception (); + + do_cleanups (all_cleanups ()); + + /* Jump to the containing catch_errors() call, communicating REASON + to that call via setjmp's return value. Note that REASON can't + be zero, by definition in defs.h. */ + exceptions_state_mc (CATCH_THROWING); + *current_catcher->exception = exception; + SIGLONGJMP (current_catcher->buf, exception.reason); +} + +/* A stack of exception messages. + This is needed to handle nested calls to throw_it: we don't want to + xfree space for a message before it's used. + This can happen if we throw an exception during a cleanup: + An outer TRY_CATCH may have an exception message it wants to print, + but while doing cleanups further calls to throw_it are made. + + This is indexed by the size of the current_catcher list. + It is a dynamically allocated array so that we don't care how deeply + GDB nests its TRY_CATCHs. */ +static char **exception_messages; + +/* The number of currently allocated entries in exception_messages. */ +static int exception_messages_size; + +static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0) +throw_it (enum return_reason reason, enum errors error, const char *fmt, + va_list ap) +{ + struct gdb_exception e; + char *new_message; + int depth = catcher_list_size (); + + gdb_assert (depth > 0); + + /* Note: The new message may use an old message's text. */ + new_message = xstrvprintf (fmt, ap); + + if (depth > exception_messages_size) + { + int old_size = exception_messages_size; + + exception_messages_size = depth + 10; + exception_messages = (char **) xrealloc (exception_messages, + exception_messages_size + * sizeof (char *)); + memset (exception_messages + old_size, 0, + (exception_messages_size - old_size) * sizeof (char *)); + } + + xfree (exception_messages[depth - 1]); + exception_messages[depth - 1] = new_message; + + /* Create the exception. */ + e.reason = reason; + e.error = error; + e.message = new_message; + + /* Throw the exception. */ + throw_exception (e); +} + +void +throw_verror (enum errors error, const char *fmt, va_list ap) +{ + throw_it (RETURN_ERROR, error, fmt, ap); +} + +void +throw_vquit (const char *fmt, va_list ap) +{ + throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap); +} + +void +throw_error (enum errors error, const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + throw_verror (error, fmt, args); + va_end (args); +} + +void +throw_quit (const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + throw_vquit (fmt, args); + va_end (args); +} diff --git a/gdb/common/common-exceptions.h b/gdb/common/common-exceptions.h new file mode 100644 index 0000000000..5f750c3a63 --- /dev/null +++ b/gdb/common/common-exceptions.h @@ -0,0 +1,185 @@ +/* Exception (throw catch) mechanism, for GDB, the GNU debugger. + + Copyright (C) 1986-2014 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +#ifndef COMMON_EXCEPTIONS_H +#define COMMON_EXCEPTIONS_H + +#include "gdb_setjmp.h" + +/* Reasons for calling throw_exceptions(). NOTE: all reason values + must be less than zero. enum value 0 is reserved for internal use + as the return value from an initial setjmp(). The function + catch_exceptions() reserves values >= 0 as legal results from its + wrapped function. */ + +enum return_reason + { + /* User interrupt. */ + RETURN_QUIT = -2, + /* Any other error. */ + RETURN_ERROR + }; + +#define RETURN_MASK(reason) (1 << (int)(-reason)) + +typedef enum +{ + RETURN_MASK_QUIT = RETURN_MASK (RETURN_QUIT), + RETURN_MASK_ERROR = RETURN_MASK (RETURN_ERROR), + RETURN_MASK_ALL = (RETURN_MASK_QUIT | RETURN_MASK_ERROR) +} return_mask; + +/* Describe all exceptions. */ + +enum errors { + GDB_NO_ERROR, + + /* Any generic error, the corresponding text is in + exception.message. */ + GENERIC_ERROR, + + /* Something requested was not found. */ + NOT_FOUND_ERROR, + + /* Thread library lacks support necessary for finding thread local + storage. */ + TLS_NO_LIBRARY_SUPPORT_ERROR, + + /* Load module not found while attempting to find thread local storage. */ + TLS_LOAD_MODULE_NOT_FOUND_ERROR, + + /* Thread local storage has not been allocated yet. */ + TLS_NOT_ALLOCATED_YET_ERROR, + + /* Something else went wrong while attempting to find thread local + storage. The ``struct gdb_exception'' message field provides + more detail. */ + TLS_GENERIC_ERROR, + + /* Problem parsing an XML document. */ + XML_PARSE_ERROR, + + /* Error accessing memory. */ + MEMORY_ERROR, + + /* Value not available. E.g., a register was not collected in a + traceframe. */ + NOT_AVAILABLE_ERROR, + + /* Value was optimized out. Note: if the value was a register, this + means the register was not saved in the frame. */ + OPTIMIZED_OUT_ERROR, + + /* DW_OP_GNU_entry_value resolving failed. */ + NO_ENTRY_VALUE_ERROR, + + /* Target throwing an error has been closed. Current command should be + aborted as the inferior state is no longer valid. */ + TARGET_CLOSE_ERROR, + + /* An undefined command was executed. */ + UNDEFINED_COMMAND_ERROR, + + /* Requested feature, method, mechanism, etc. is not supported. */ + NOT_SUPPORTED_ERROR, + + /* Add more errors here. */ + NR_ERRORS +}; + +struct gdb_exception +{ + enum return_reason reason; + enum errors error; + const char *message; +}; + +/* Functions to drive the exceptions state machine. Though declared + here by necessity, these functions should be considered internal to + the exceptions subsystem and not used other than via the TRY_CATCH + macro defined below. */ + +extern SIGJMP_BUF *exceptions_state_mc_init (volatile struct + gdb_exception *exception, + return_mask mask); +extern int exceptions_state_mc_action_iter (void); +extern int exceptions_state_mc_action_iter_1 (void); + +/* Macro to wrap up standard try/catch behavior. + + The double loop lets us correctly handle code "break"ing out of the + try catch block. (It works as the "break" only exits the inner + "while" loop, the outer for loop detects this handling it + correctly.) Of course "return" and "goto" are not so lucky. + + For instance: + + *INDENT-OFF* + + volatile struct gdb_exception e; + TRY_CATCH (e, RETURN_MASK_ERROR) + { + } + switch (e.reason) + { + case RETURN_ERROR: ... + } + + */ + +#define TRY_CATCH(EXCEPTION,MASK) \ + { \ + SIGJMP_BUF *buf = \ + exceptions_state_mc_init (&(EXCEPTION), (MASK)); \ + SIGSETJMP (*buf); \ + } \ + while (exceptions_state_mc_action_iter ()) \ + while (exceptions_state_mc_action_iter_1 ()) + +/* *INDENT-ON* */ + +/* Hook to allow client-specific actions to be performed prior to + throwing an exception. This function must be provided by the + client, and will be called before any cleanups are run. */ + +extern void prepare_to_throw_exception (void); + +/* Throw an exception (as described by "struct gdb_exception"). Will + execute a LONG JUMP to the inner most containing exception handler + established using catch_exceptions() (or similar). + + Code normally throws an exception using error() et.al. For various + reaons, GDB also contains code that throws an exception directly. + For instance, the remote*.c targets contain CNTRL-C signal handlers + that propogate the QUIT event up the exception chain. ``This could + be a good thing or a dangerous thing.'' -- the Existential + Wombat. */ + +extern void throw_exception (struct gdb_exception exception) + ATTRIBUTE_NORETURN; +extern void throw_verror (enum errors, const char *fmt, va_list ap) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 0); +extern void throw_vquit (const char *fmt, va_list ap) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 0); +extern void throw_error (enum errors error, const char *fmt, ...) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3); +extern void throw_quit (const char *fmt, ...) + ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2); + +#endif /* COMMON_EXCEPTIONS_H */ diff --git a/gdb/exceptions.c b/gdb/exceptions.c index 48843eae75..28b8dd1bce 100644 --- a/gdb/exceptions.c +++ b/gdb/exceptions.c @@ -29,207 +29,11 @@ const struct gdb_exception exception_none = { 0, GDB_NO_ERROR, NULL }; -/* Possible catcher states. */ -enum catcher_state { - /* Initial state, a new catcher has just been created. */ - CATCHER_CREATED, - /* The catch code is running. */ - CATCHER_RUNNING, - CATCHER_RUNNING_1, - /* The catch code threw an exception. */ - CATCHER_ABORTING -}; - -/* Possible catcher actions. */ -enum catcher_action { - CATCH_ITER, - CATCH_ITER_1, - CATCH_THROWING -}; - -struct catcher -{ - enum catcher_state state; - /* Jump buffer pointing back at the exception handler. */ - SIGJMP_BUF buf; - /* Status buffer belonging to the exception handler. */ - volatile struct gdb_exception *exception; - /* Saved/current state. */ - int mask; - struct cleanup *saved_cleanup_chain; - /* Back link. */ - struct catcher *prev; -}; - -/* Where to go for throw_exception(). */ -static struct catcher *current_catcher; - -/* Return length of current_catcher list. */ - -static int -catcher_list_size (void) -{ - int size; - struct catcher *catcher; - - for (size = 0, catcher = current_catcher; - catcher != NULL; - catcher = catcher->prev) - ++size; - - return size; -} - -SIGJMP_BUF * -exceptions_state_mc_init (volatile struct gdb_exception *exception, - return_mask mask) -{ - struct catcher *new_catcher = XCNEW (struct catcher); - - /* Start with no exception, save it's address. */ - exception->reason = 0; - exception->error = GDB_NO_ERROR; - exception->message = NULL; - new_catcher->exception = exception; - - new_catcher->mask = mask; - - /* Prevent error/quit during FUNC from calling cleanups established - prior to here. */ - new_catcher->saved_cleanup_chain = save_cleanups (); - - /* Push this new catcher on the top. */ - new_catcher->prev = current_catcher; - current_catcher = new_catcher; - new_catcher->state = CATCHER_CREATED; - - return &new_catcher->buf; -} - -static void -catcher_pop (void) -{ - struct catcher *old_catcher = current_catcher; - - current_catcher = old_catcher->prev; - - /* Restore the cleanup chain, the error/quit messages, and the uiout - builder, to their original states. */ - - restore_cleanups (old_catcher->saved_cleanup_chain); - - xfree (old_catcher); -} - -/* Catcher state machine. Returns non-zero if the m/c should be run - again, zero if it should abort. */ - -static int -exceptions_state_mc (enum catcher_action action) -{ - switch (current_catcher->state) - { - case CATCHER_CREATED: - switch (action) - { - case CATCH_ITER: - /* Allow the code to run the catcher. */ - current_catcher->state = CATCHER_RUNNING; - return 1; - default: - internal_error (__FILE__, __LINE__, _("bad state")); - } - case CATCHER_RUNNING: - switch (action) - { - case CATCH_ITER: - /* No error/quit has occured. Just clean up. */ - catcher_pop (); - return 0; - case CATCH_ITER_1: - current_catcher->state = CATCHER_RUNNING_1; - return 1; - case CATCH_THROWING: - current_catcher->state = CATCHER_ABORTING; - /* See also throw_exception. */ - return 1; - default: - internal_error (__FILE__, __LINE__, _("bad switch")); - } - case CATCHER_RUNNING_1: - switch (action) - { - case CATCH_ITER: - /* The did a "break" from the inner while loop. */ - catcher_pop (); - return 0; - case CATCH_ITER_1: - current_catcher->state = CATCHER_RUNNING; - return 0; - case CATCH_THROWING: - current_catcher->state = CATCHER_ABORTING; - /* See also throw_exception. */ - return 1; - default: - internal_error (__FILE__, __LINE__, _("bad switch")); - } - case CATCHER_ABORTING: - switch (action) - { - case CATCH_ITER: - { - struct gdb_exception exception = *current_catcher->exception; - - if (current_catcher->mask & RETURN_MASK (exception.reason)) - { - /* Exit normally if this catcher can handle this - exception. The caller analyses the func return - values. */ - catcher_pop (); - return 0; - } - /* The caller didn't request that the event be caught, - relay the event to the next containing - catch_errors(). */ - catcher_pop (); - throw_exception (exception); - } - default: - internal_error (__FILE__, __LINE__, _("bad state")); - } - default: - internal_error (__FILE__, __LINE__, _("bad switch")); - } -} - -int -exceptions_state_mc_action_iter (void) -{ - return exceptions_state_mc (CATCH_ITER); -} - -int -exceptions_state_mc_action_iter_1 (void) -{ - return exceptions_state_mc (CATCH_ITER_1); -} - -/* Return EXCEPTION to the nearest containing catch_errors(). */ - void -throw_exception (struct gdb_exception exception) +prepare_to_throw_exception (void) { clear_quit_flag (); immediate_quit = 0; - - do_cleanups (all_cleanups ()); - - /* Jump to the containing catch_errors() call, communicating REASON - to that call via setjmp's return value. Note that REASON can't - be zero, by definition in defs.h. */ - exceptions_state_mc (CATCH_THROWING); - *current_catcher->exception = exception; - SIGLONGJMP (current_catcher->buf, exception.reason); } static void @@ -332,90 +136,6 @@ exception_fprintf (struct ui_file *file, struct gdb_exception e, } } -/* A stack of exception messages. - This is needed to handle nested calls to throw_it: we don't want to - xfree space for a message before it's used. - This can happen if we throw an exception during a cleanup: - An outer TRY_CATCH may have an exception message it wants to print, - but while doing cleanups further calls to throw_it are made. - - This is indexed by the size of the current_catcher list. - It is a dynamically allocated array so that we don't care how deeply - GDB nests its TRY_CATCHs. */ -static char **exception_messages; - -/* The number of currently allocated entries in exception_messages. */ -static int exception_messages_size; - -static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0) -throw_it (enum return_reason reason, enum errors error, const char *fmt, - va_list ap) -{ - struct gdb_exception e; - char *new_message; - int depth = catcher_list_size (); - - gdb_assert (depth > 0); - - /* Note: The new message may use an old message's text. */ - new_message = xstrvprintf (fmt, ap); - - if (depth > exception_messages_size) - { - int old_size = exception_messages_size; - - exception_messages_size = depth + 10; - exception_messages = (char **) xrealloc (exception_messages, - exception_messages_size - * sizeof (char *)); - memset (exception_messages + old_size, 0, - (exception_messages_size - old_size) * sizeof (char *)); - } - - xfree (exception_messages[depth - 1]); - exception_messages[depth - 1] = new_message; - - /* Create the exception. */ - e.reason = reason; - e.error = error; - e.message = new_message; - - /* Throw the exception. */ - throw_exception (e); -} - -void -throw_verror (enum errors error, const char *fmt, va_list ap) -{ - throw_it (RETURN_ERROR, error, fmt, ap); -} - -void -throw_vquit (const char *fmt, va_list ap) -{ - throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap); -} - -void -throw_error (enum errors error, const char *fmt, ...) -{ - va_list args; - - va_start (args, fmt); - throw_verror (error, fmt, args); - va_end (args); -} - -void -throw_quit (const char *fmt, ...) -{ - va_list args; - - va_start (args, fmt); - throw_vquit (fmt, args); - va_end (args); -} - /* Call FUNC(UIOUT, FUNC_ARGS) but wrapped within an exception handler. If an exception (enum return_reason) is thrown using throw_exception() than all cleanups installed since diff --git a/gdb/exceptions.h b/gdb/exceptions.h index e3ff672f26..00d2b6126c 100644 --- a/gdb/exceptions.h +++ b/gdb/exceptions.h @@ -20,142 +20,12 @@ #ifndef EXCEPTIONS_H #define EXCEPTIONS_H +#include "common-exceptions.h" #include "ui-out.h" -#include "gdb_setjmp.h" - -/* Reasons for calling throw_exceptions(). NOTE: all reason values - must be less than zero. enum value 0 is reserved for internal use - as the return value from an initial setjmp(). The function - catch_exceptions() reserves values >= 0 as legal results from its - wrapped function. */ - -enum return_reason - { - /* User interrupt. */ - RETURN_QUIT = -2, - /* Any other error. */ - RETURN_ERROR - }; - -#define RETURN_MASK(reason) (1 << (int)(-reason)) - -typedef enum -{ - RETURN_MASK_QUIT = RETURN_MASK (RETURN_QUIT), - RETURN_MASK_ERROR = RETURN_MASK (RETURN_ERROR), - RETURN_MASK_ALL = (RETURN_MASK_QUIT | RETURN_MASK_ERROR) -} return_mask; - -/* Describe all exceptions. */ - -enum errors { - GDB_NO_ERROR, - - /* Any generic error, the corresponding text is in - exception.message. */ - GENERIC_ERROR, - - /* Something requested was not found. */ - NOT_FOUND_ERROR, - - /* Thread library lacks support necessary for finding thread local - storage. */ - TLS_NO_LIBRARY_SUPPORT_ERROR, - - /* Load module not found while attempting to find thread local storage. */ - TLS_LOAD_MODULE_NOT_FOUND_ERROR, - - /* Thread local storage has not been allocated yet. */ - TLS_NOT_ALLOCATED_YET_ERROR, - - /* Something else went wrong while attempting to find thread local - storage. The ``struct gdb_exception'' message field provides - more detail. */ - TLS_GENERIC_ERROR, - - /* Problem parsing an XML document. */ - XML_PARSE_ERROR, - - /* Error accessing memory. */ - MEMORY_ERROR, - - /* Value not available. E.g., a register was not collected in a - traceframe. */ - NOT_AVAILABLE_ERROR, - - /* Value was optimized out. Note: if the value was a register, this - means the register was not saved in the frame. */ - OPTIMIZED_OUT_ERROR, - - /* DW_OP_GNU_entry_value resolving failed. */ - NO_ENTRY_VALUE_ERROR, - - /* Target throwing an error has been closed. Current command should be - aborted as the inferior state is no longer valid. */ - TARGET_CLOSE_ERROR, - - /* An undefined command was executed. */ - UNDEFINED_COMMAND_ERROR, - - /* Requested feature, method, mechanism, etc. is not supported. */ - NOT_SUPPORTED_ERROR, - - /* Add more errors here. */ - NR_ERRORS -}; - -struct gdb_exception -{ - enum return_reason reason; - enum errors error; - const char *message; -}; /* A pre-defined non-exception. */ extern const struct gdb_exception exception_none; -/* Functions to drive the exceptions state m/c (internal to - exceptions). */ -SIGJMP_BUF *exceptions_state_mc_init (volatile struct - gdb_exception *exception, - return_mask mask); -int exceptions_state_mc_action_iter (void); -int exceptions_state_mc_action_iter_1 (void); - -/* Macro to wrap up standard try/catch behavior. - - The double loop lets us correctly handle code "break"ing out of the - try catch block. (It works as the "break" only exits the inner - "while" loop, the outer for loop detects this handling it - correctly.) Of course "return" and "goto" are not so lucky. - - For instance: - - *INDENT-OFF* - - volatile struct gdb_exception e; - TRY_CATCH (e, RETURN_MASK_ERROR) - { - } - switch (e.reason) - { - case RETURN_ERROR: ... - } - - */ - -#define TRY_CATCH(EXCEPTION,MASK) \ - { \ - SIGJMP_BUF *buf = \ - exceptions_state_mc_init (&(EXCEPTION), (MASK)); \ - SIGSETJMP (*buf); \ - } \ - while (exceptions_state_mc_action_iter ()) \ - while (exceptions_state_mc_action_iter_1 ()) - -/* *INDENT-ON* */ - - /* If E is an exception, print it's error message on the specified stream. For _fprintf, prefix the message with PREFIX... */ extern void exception_print (struct ui_file *file, struct gdb_exception e); @@ -163,28 +33,6 @@ extern void exception_fprintf (struct ui_file *file, struct gdb_exception e, const char *prefix, ...) ATTRIBUTE_PRINTF (3, 4); -/* Throw an exception (as described by "struct gdb_exception"). Will - execute a LONG JUMP to the inner most containing exception handler - established using catch_exceptions() (or similar). - - Code normally throws an exception using error() et.al. For various - reaons, GDB also contains code that throws an exception directly. - For instance, the remote*.c targets contain CNTRL-C signal handlers - that propogate the QUIT event up the exception chain. ``This could - be a good thing or a dangerous thing.'' -- the Existential - Wombat. */ - -extern void throw_exception (struct gdb_exception exception) - ATTRIBUTE_NORETURN; -extern void throw_verror (enum errors, const char *fmt, va_list ap) - ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 0); -extern void throw_vquit (const char *fmt, va_list ap) - ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 0); -extern void throw_error (enum errors error, const char *fmt, ...) - ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3); -extern void throw_quit (const char *fmt, ...) - ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2); - /* Call FUNC(UIOUT, FUNC_ARGS) but wrapped within an exception handler. If an exception (enum return_reason) is thrown using throw_exception() than all cleanups installed since diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 453854ef19..89cafa69a2 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,10 @@ +2014-08-29 Gary Benson + + * Makefile.in (SFILES): Add common/common-exceptions.c. + (OBS): Add common-exceptions.o. + (common-exceptions.o): New rule. + * utils.c (prepare_to_throw_exception): New function. + 2014-08-29 Gary Benson * config.in: Regenerate. diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index f130082693..21ac6f2a51 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -170,7 +170,8 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \ $(srcdir)/common/filestuff.c $(srcdir)/target/waitstatus.c \ $(srcdir)/nat/mips-linux-watch.c $(srcdir)/common/print-utils.c \ $(srcdir)/common/rsp-low.c $(srcdir)/common/errors.c \ - $(srcdir)/common/common-debug.c $(srcdir)/common/cleanups.c + $(srcdir)/common/common-debug.c $(srcdir)/common/cleanups.c \ + $(srcdir)/common/common-exceptions.c DEPFILES = @GDBSERVER_DEPFILES@ @@ -184,6 +185,7 @@ OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o \ mem-break.o hostio.o event-loop.o tracepoint.o xml-utils.o \ common-utils.o ptid.o buffer.o format.o filestuff.o dll.o notif.o \ tdesc.o print-utils.o rsp-low.o errors.o common-debug.o cleanups.o \ + common-exceptions.o \ $(XML_BUILTIN) $(DEPFILES) $(LIBOBJS) GDBREPLAY_OBS = gdbreplay.o version.o GDBSERVER_LIBS = @GDBSERVER_LIBS@ @@ -547,6 +549,9 @@ common-debug.o: ../common/common-debug.c cleanups.o: ../common/cleanups.c $(COMPILE) $< $(POSTCOMPILE) +common-exceptions.o: ../common/common-exceptions.c + $(COMPILE) $< + $(POSTCOMPILE) waitstatus.o: ../target/waitstatus.c $(COMPILE) $< $(POSTCOMPILE) diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c index 244e114c70..e310b9e140 100644 --- a/gdb/gdbserver/utils.c +++ b/gdb/gdbserver/utils.c @@ -141,3 +141,11 @@ pfildes (gdb_fildes_t fd) return plongest (fd); #endif } + +/* See common/common-exceptions.h. */ + +void +prepare_to_throw_exception (void) +{ + /* No client-specific actions required. */ +}