From ff55e1b54898ca4e2a90c005749505e33e32215c Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Thu, 7 Aug 2014 16:29:19 +0100 Subject: 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/common/common-exceptions.c | 308 +++++++++++++++++++++++++++++++++++++++++ gdb/common/common-exceptions.h | 185 +++++++++++++++++++++++++ 2 files changed, 493 insertions(+) create mode 100644 gdb/common/common-exceptions.c create mode 100644 gdb/common/common-exceptions.h (limited to 'gdb/common') diff --git a/gdb/common/common-exceptions.c b/gdb/common/common-exceptions.c new file mode 100644 index 0000000..c382d86 --- /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 0000000..5f750c3 --- /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 */ -- cgit v1.1