From 26605a32204c414be6d30690c9196a3c640f0142 Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Wed, 27 Jul 2011 22:34:53 -0400 Subject: [PATCH] Forgot to do '-a' on the last commit 'cause git is silly like that... --- Makefile | 6 +- exception_test.c | 4 +- exception_test_simple.c | 33 +++++--- exclib.c | 162 +++++++++++++++------------------------- exclib.h | 17 ++--- 5 files changed, 93 insertions(+), 129 deletions(-) diff --git a/Makefile b/Makefile index 3e26f74..69441d2 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,9 @@ CC=gcc LD=gcc EXECOBJ= -OBJECTS=exclib.o exception_test.o backtrace.o +OBJECTS=exclib.o exception_test.o LIBS= -ifeq "$(OS)" "win32" - LIBS=-lbfd -lintl -liberty -imagehlp -endif +CFLAGS= all: exception_test diff --git a/exception_test.c b/exception_test.c index be5b92c..7cc6e24 100644 --- a/exception_test.c +++ b/exception_test.c @@ -25,7 +25,6 @@ int main(void) { int x = 2; - exclib_register_signals(); printf("Stack frames are %d bytes in size\n\n", sizeof(struct exc_status)); TRY { @@ -50,8 +49,7 @@ int main(void) printf("Using THROW_NONZERO\n"); THROW_NONZERO(strcmp("a", "b"), 0, "String Compare Exception"); } FINALLY { - printf("In finally clause.\n"); - exclib_print_exception_stack(__FILE__, (char *)__func__, __LINE__); + exclib_print_exception_stack("In finally clause", __FILE__, (char *)__func__, __LINE__); } ETRY; TRY { diff --git a/exception_test_simple.c b/exception_test_simple.c index c4fd7f6..768e824 100644 --- a/exception_test_simple.c +++ b/exception_test_simple.c @@ -2,17 +2,30 @@ int main(void) { - exclib_register_signals(); + EXCLIB_TRACE("No stack to print?"); + + TRY { + EXCLIB_TRACE("Inside TRY"); + THROW(3, NULL); + } CATCH(3) { + EXCLIB_TRACE("Caught 3"); + THROW(5, NULL); + } CATCH(5) { + EXCLIB_TRACE("Caught 5"); TRY { - exclib_print_exception_stack(__FILE__, (char *)__func__, __LINE__); - THROW(2, NULL); - } CATCH(2) { - exclib_print_exception_stack(__FILE__, (char *)__func__, __LINE__); - THROW(3, NULL); - } CATCH(3) { - } FINALLY { - exclib_print_exception_stack(__FILE__, (char *)__func__, __LINE__); - return 0; + THROW(6, NULL); + } CATCH (6) { + EXCLIB_TRACE("Caught 6"); + THROW(7, NULL); } ETRY; + } CATCH(7) { + EXCLIB_TRACE("Caught 7"); + } FINALLY { + EXCLIB_TRACE("In finally clause"); + } ETRY; + + EXCLIB_TRACE("Exiting program"); + + return 0; } diff --git a/exclib.c b/exclib.c index 1e103ba..2264735 100644 --- a/exclib.c +++ b/exclib.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -18,38 +17,60 @@ struct exc_name_data __exclib_exc_names[EXC_PREDEFINED_EXCEPTIONS] = { {EXC_OUTOFBOUNDS, "Array Index Out of Bounds", SIGTERM} }; -void exclib_print_exception_stack(char *file, char *func, int line) +void exclib_print_exception_stack(char *mbuf, char *file, char *func, int line) { char buf[256]; char flagbuf[256]; + char *excname = NULL; struct exc_status *cur = EXC_STATUS_LIST; int idx = 0; fprintf(stderr, + "EXCLIB: %s:%d:%s: %s\n", + file, + line, + func, + mbuf); + + /*fprintf(stderr, "%s:%d:%s: Contents of the Exception Stack at this time:\n", file, line, - func); + func);*/ if ( ! cur ) - fprintf(stderr, "#0: No exception stack\n"); + fprintf(stderr, "EXCLIB: #0: No exception stack\n"); while ( cur != NULL ) { memset((char *)&buf, 0, 256); memset((char *)&flagbuf, 0, 256); - if ( cur->catching == 1) - strcat((char *)&flagbuf, "catching "); - if ( cur->caught == 1) - strcat((char *)&flagbuf, "caught "); - if ( cur->tried == 1) - strcat((char *)&flagbuf, "tried "); + if ( cur->catching == 1) { + if ( strlen((char *)&flagbuf) > 0 ) + strcat((char *)&flagbuf, ","); + strcat((char *)&flagbuf, "catching"); + } + if ( cur->caught == 1) { + if ( strlen((char *)&flagbuf) > 0 ) + strcat((char *)&flagbuf, ","); + strcat((char *)&flagbuf, "caught"); + } + if ( cur->tried == 1) { + if ( strlen((char *)&flagbuf) > 0 ) + strcat((char *)&flagbuf, ","); + strcat((char *)&flagbuf, "tried"); + } + + if ( cur->name ) + excname = cur->name; + else + excname = "NULL"; sprintf((char *)&buf, - "#%d[0x%x] %s:%d:%s: %s (%d) ( %s): %s\n", + "EXCLIB: #%d[0x%x] %s:%d:%s:%s:%d:%s: %s\n", idx, cur, cur->file, cur->line, cur->function, - cur->name, + excname, cur->value, (char *)&flagbuf, cur->description); @@ -59,72 +80,6 @@ void exclib_print_exception_stack(char *file, char *func, int line) } } -void exclib_print_stacktrace(char *msg, char *file, char *func, int line) -{ - size_t bt_size; - void *bt_frames[EXC_BT_FRAMES]; - char **strings; - size_t i; - - if (!msg) - msg = "Unknown error occured"; - - fprintf(stderr, - "%s:%d:%s: %s\n", - file, - line, - func, - msg); - - bt_size = backtrace(bt_frames, EXC_BT_FRAMES); - strings = backtrace_symbols (bt_frames, bt_size); - for (i = 0; i < bt_size; i++) - fprintf(stderr, "#%d: %s\n", i, strings[i]); - free (strings); -} - -void exclib_print_exc_stacktrace (char *msg) -{ - char **strings; - size_t i; - - if ( !msg ) - msg = "Unhandled Exception"; - - fprintf(stderr, - "%s:%d:%s: %s '%s' (%d) : %s\n", - EXC_STATUS_LIST->file, - EXC_STATUS_LIST->line, - EXC_STATUS_LIST->function, - msg, - EXC_STATUS_LIST->name, - EXC_STATUS_LIST->value, - EXC_STATUS_LIST->description); - - strings = backtrace_symbols (EXC_STATUS_LIST->bt_frames, EXC_STATUS_LIST->bt_size); - for (i = 0; i < EXC_STATUS_LIST->bt_size; i++) { - fprintf(stderr, "#%d: %s\n", i, strings[i]); - } - - free (strings); -} - -void __exclib_sigsegv_handler(int signal) -{ - exclib_print_stacktrace("SIGSEGV - Segmentation Fault.", __FILE__, (char *)__func__, __LINE__); -#ifdef DEBUG_LOOPONSEGFAULT - fprintf(stderr, "Entering infinite loop; please connect a debugger to this process or kill it.\n"); - fflush(stderr); - while ( 1 ) { }; -#endif - exit(signal); -} - -void exclib_register_signals(void) -{ - signal(SIGSEGV, &__exclib_sigsegv_handler); -} - void exclib_bulk_name_exceptions(struct exc_name_data *exclib_exc_names, int size) { struct exc_name_data *ptr; @@ -171,25 +126,30 @@ void exclib_prep_throw(int value, char *msg, char *file, char *func, int line) if ( EXC_STATUS_LIST && EXC_STATUS_LIST->tried == 1) { sprintf((char *)&stbuf, "Tried to THROW %d but couldn't create new exception frame", value); if ( EXC_STATUS_LIST->catching == 1 && EXC_STATUS_LIST->prev ) { - if ( exclib_new_exc_frame(EXC_STATUS_LIST, file, func, line) ) - exclib_print_stacktrace((char *)&stbuf, file, func, line); - memcpy(EXC_STATUS_LIST->buf, EXC_STATUS_LIST->prev->buf, sizeof(jmp_buf)); - } else if ( EXC_STATUS_LIST->catching && (EXC_STATUS_LIST->prev == NULL) ) { - if ( exclib_new_exc_frame(&__exc_statuses[__exc_curidx++], file, func, line) ) - exclib_print_stacktrace((char *)&stbuf, file, func, line); - exclib_clear_exc_frame(); - __exc_curidx--; - } else { - if ( exclib_new_exc_frame(EXC_STATUS_LIST, file, func, line) ) - exclib_print_stacktrace((char *)&stbuf, file, func, line); + if ( exclib_new_exc_frame(EXC_STATUS_LIST, file, func, line) ) { + exclib_print_exception_stack((char *)&stbuf, file, func, line); + exit(value); + } + memcpy(EXC_STATUS_LIST->buf, EXC_STATUS_LIST->prev->buf, sizeof(jmp_buf)); + } /*else if ( EXC_STATUS_LIST->catching == 1 && (EXC_STATUS_LIST->prev == NULL) ) { + if ( exclib_new_exc_frame(&__exc_statuses[__exc_curidx++], file, func, line) ) { + exclib_print_exception_stack((char *)&stbuf, file, func, line); + exit(value); + } + EXCLIB_TRACE("Condition 2"); + exclib_clear_exc_frame(); + __exc_curidx--; + } */ else { + if ( exclib_new_exc_frame(EXC_STATUS_LIST, file, func, line) ) + exclib_print_exception_stack((char *)&stbuf, file, func, line); } EXC_STATUS_LIST->value = value; EXC_STATUS_LIST->name = __exc_names[value]; EXC_STATUS_LIST->description = msg; } else { sprintf((char *)&stbuf, "Tried to THROW Exception %d but had no exception context. (Called outside of TRY block, or thrown while TRY was setting up?)", value); - exclib_print_stacktrace("", file, func, line); - kill(getpid(), __exc_signals[value]); + exclib_print_exception_stack((char *)&stbuf, file, func, line); + exit(__exc_signals[value]); } } @@ -216,7 +176,6 @@ int exclib_new_exc_frame(struct exc_status *es, char *file, char *function, int es->caught = 0; es->tried = 0; es->catching = 0; - es->bt_size = backtrace(es->bt_frames, EXC_BT_FRAMES); EXC_STATUS_LIST = es; return 0; } @@ -225,27 +184,24 @@ int exclib_clear_exc_frame() { struct exc_status *es = EXC_STATUS_LIST; if ( !es ) { - exclib_print_stacktrace("exclib_clear_exc_frame was called but there were no exception frames to clear!", + exclib_print_exception_stack("exclib_clear_exc_frame was called but there were no exception frames to clear!", __FILE__, (char *)__func__, __LINE__); - kill(getpid(), SIGTERM); + exit(1); } if ( es->prev ) es->prev->next = NULL; - if ( !es->caught ) { - // exception was unhandled - do we have anywhere else to go? + if ( es->value && !es->caught ) { + // thrown exception was unhandled - do we have anywhere else to go? if ( !es->prev ) { - // No frame above us to propagate this into, stacktrace and kill ourselves - // we do kill here instead of just exit() to try and let any custom signal - // handlers clean things up for us - exclib_print_exc_stacktrace(NULL); - kill(getpid(), __exc_signals[es->value]); - //kill(getpid(), SIGKILL); + // No frame above us to propagate this into, stacktrace and kill ourselves + exclib_print_exception_stack("Uncaught exception", __FILE__, (char*)__func__, __LINE__); + exit(es->value); + //kill(getpid(), SIGKILL); } else if ( es->tried && es->prev && (es->catching == 0)) { // copy this exception up into the upper frame and siglongjmp back to that EXC_STATUS_LIST = es->prev; EXC_STATUS_LIST->caught = 0; memcpy(&EXC_STATUS_LIST->bt_frames, &es->bt_frames, (sizeof(void *)*EXC_BT_FRAMES)); - EXC_STATUS_LIST->bt_size = es->bt_size; EXC_STATUS_LIST->file = es->file; EXC_STATUS_LIST->function = es->function; EXC_STATUS_LIST->line = es->line; diff --git a/exclib.h b/exclib.h index 4e78cfa..b0d9f1d 100644 --- a/exclib.h +++ b/exclib.h @@ -26,12 +26,12 @@ * 9- Each member of the exception stack is currently ~700 bytes, so beware of making EXC_BT_FRAMES too large (the default, 50, is already unimaginably deep and adds ~35kB to your memory usage automatically). Don't be afraid to make EXC_BT_FRAMES smaller; most programs won't need more than 10 or 15, because this only tracks where TRY/THROW have been used, and each TRY/THROW pair use only one entry. Stacktraces aren't stored here, so this doesn't limit the possible size of a stacktrace. * 10- Because this library allows you to name your exceptions, you will automatically use an additional (1 * EXC_MAX_EXCEPTIONS) bytes of memory for the array of character pointers to store linkage to your exception strings, not counting whatever memory is used up by the actual string table for your strings. By default, EXC_MAX_EXCEPTIONS is set to 65535. You should probably leave it there, since you have no way of knowing how many exceptions a dependent library might use (and remember, compiler array bounds checking may not always save you here)... The 64k memory hit is, in this day and age, a pretty small price to pay for the verbosity provided. * 11- You get an additional (sizeof(int) * EXC_MAX_EXCEPTIONS) in memory usage from the table of signals to match exceptions. When an uncaught exception rises to the top, it generates a signal action. By default, that signal is SIGKILL, but you can override it in the call to exclib_name_exception. - * 12- The underlying mechanism behind this is sigsetjmp / siglongjmp, two "arcane and slightly dangerous" functions. Essentially this is used to treat your *entire* codebase as something we can GOTO between when there's an exception. I don't think it will, but if this does funny things to your code, I'm sorry. + * 12- The underlying mechanism behind this is setjmp / longjmp, two "arcane and slightly dangerous" functions. Essentially this is used to treat your *entire* codebase as something we can GOTO between when there's an exception. I don't think it will, but if this does funny things to your code, I'm sorry. * 13- You should limit your involvement with this library to the all-capital #defines. The functions themselves are undocumented and may change without warning, and they were never meant for human consumption anyways. * 14- There are exceptions to every rule, and the exception to #11 is that you can safely call "exclib_print_stacktrace", and that you MUST call "exclib_name_exception" if you want your exceptions to have pretty names in tracebacks, and not just numbers. * 15- I haven't tried it, but this should be safe for C++ as well. Beware of mangled symbols in tracebacks. (C++ already has exception handling, you shouldn't need this there, I'm just saying it should work.) * 16- It is impossible to THROW(0). A compile time error will occur complaining about duplicate case value. If by some miracle you do get it to compile, you will enter an infinite loop, as there is no exit path for this. - * 17- These are done as defines for a reason; at the time of TRY/THROW, a lot of stack information is saved to show the user (via traceback) where the exception handling occured. And besides, sigsetjmp/siglongjmp won't work if we use functions for this instead of defines, because the call stack will be different. + * 17- These are done as defines for a reason; at the time of TRY/THROW, a lot of stack information is saved to show the user (via traceback) where the exception handling occured. And besides, setjmp/longjmp won't work if we use functions for this instead of defines, because the call stack will be different. * * Model usage: * TRY { @@ -102,10 +102,10 @@ #define TRY \ if (__exc_curidx >= EXC_BT_FRAMES) \ - exclib_print_stacktrace("No available exception stack context", __FILE__, (char *)__func__, __LINE__); \ + exclib_print_exception_stack("No available exception stack context", __FILE__, (char *)__func__, __LINE__); \ if ( exclib_new_exc_frame(&__exc_statuses[__exc_curidx++], __FILE__, (char *)__func__, __LINE__) != 0) \ - exclib_print_stacktrace("Tried to TRY but couldn't create new exception frame", __FILE__, (char *)__func__, __LINE__); \ -EXC_STATUS_LIST->value = sigsetjmp(EXC_STATUS_LIST->buf, 1); \ + exclib_print_exception_stack("Tried to TRY but couldn't create new exception frame", __FILE__, (char *)__func__, __LINE__); \ +EXC_STATUS_LIST->value = setjmp(EXC_STATUS_LIST->buf); \ EXC_STATUS_LIST->tried = 1;\ switch( EXC_STATUS_LIST->value ) { \ case 0: @@ -139,8 +139,9 @@ if ( EXC_STATUS_LIST->caught ) { #define THROW_NONZERO(x, y, z) int rc = (x); if ( rc != 0 ) THROW(rc + y, z); #define THROW_ZERO(x, y, z) if ( (x) == 0 ) THROW(y, z); -#define THROW(x, y) exclib_prep_throw(x, y, __FILE__, (char *)__func__, __LINE__); if ( EXC_STATUS_LIST ) { siglongjmp(EXC_STATUS_LIST->buf, x); } else { exclib_print_stacktrace("Unhandled Exception "#x"", __FILE__, (char *)__func__, __LINE__); }; +#define THROW(x, y) exclib_prep_throw(x, y, __FILE__, (char *)__func__, __LINE__); if ( EXC_STATUS_LIST ) { longjmp(EXC_STATUS_LIST->buf, x); } else { exclib_print_exception_stack("Unhandled Exception "#x"", __FILE__, (char *)__func__, __LINE__); }; +#define EXCLIB_TRACE(x) exclib_print_exception_stack(x, __FILE__, (char *)__func__, __LINE__) struct exc_name_data { int exc; @@ -183,9 +184,7 @@ extern void exclib_register_signals(void); extern void exclib_prep_throw(int value, char *msg, char *file, char *func, int line); extern void exclib_name_exception(int value, char *name, int signal); extern void exclib_bulk_name_exceptions(struct exc_name_data *exclib_exc_names, int size); -extern void exclib_print_stacktrace(char *msg, char *file, char *func, int line); -extern void exclib_print_exc_stacktrace(char *msg); -extern void exclib_print_exception_stack(char *file, char *func, int line); +extern void exclib_print_exception_stack(char *mbuf, char *file, char *func, int line); extern int exclib_new_exc_frame(struct exc_status *es, char *file, char *function, int line); extern int exclib_clear_exc_frame();