Registers a signal handler to catch SIGSEGV in order to display the stack where the program crashed.
Adds the 'stack' helper module with generic stack trace routines that can be used to implement better debugging messages. Signed-off-by: Zachary T Welch <z...@superlucidity.net> --- src/helper/Makefile.am | 2 + src/helper/stack.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++ src/helper/stack.h | 101 ++++++++++++++++++++++++++++ src/openocd.c | 10 ++-- 4 files changed, 281 insertions(+), 5 deletions(-) create mode 100644 src/helper/stack.c create mode 100644 src/helper/stack.h diff --git a/src/helper/Makefile.am b/src/helper/Makefile.am index 13bcbd1..51d5550 100644 --- a/src/helper/Makefile.am +++ b/src/helper/Makefile.am @@ -15,6 +15,7 @@ endif libhelper_la_SOURCES = \ + stack.c \ binarybuffer.c \ $(CONFIGFILES) \ configuration.c \ @@ -49,6 +50,7 @@ noinst_HEADERS = \ fileio.h \ jim.h \ jim-eventloop.h \ + stack.h \ system.h \ bin2char.c diff --git a/src/helper/stack.c b/src/helper/stack.c new file mode 100644 index 0000000..7f31358 --- /dev/null +++ b/src/helper/stack.c @@ -0,0 +1,173 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "stack.h" +#include "module.h" +#include "log.h" + +#include <signal.h> +#include <execinfo.h> + + +static void stack_dump_symbols_free(struct stack_dump_state *dump) +{ + if (NULL == dump->symbols) + return; + + unsigned count = dump->available; + for (unsigned i = 0; NULL != dump->symbols[i] && i < count; i++) + module_symbol_free(dump->symbols[i]); + free((void *)dump->symbols); + dump->symbols = NULL; +} +int stack_dump_symbols_fill(struct stack_dump_state *dump) +{ + if (NULL != dump->symbols) + return 0; + + unsigned count = dump->available; + struct module_symbol **symbols = calloc(count, sizeof(*symbols)); + if (NULL == symbols) + return -ENOMEM; + + dump->symbols = symbols; + + for (unsigned i = 0; i < count; i++) + { + void *addr = dump->raw[i]; + symbols[i] = module_symbol_by_addr(addr); + if (NULL == symbols[i]) + return -ENOMEM; + } + + return 0; +} + +//#define STACK_SIMPLE_STRINGS + +static void stack_dump_strings_free(struct stack_dump_state *dump) +{ + if (NULL == dump->stack) + return; + +#ifndef STACK_SIMPLE_STRINGS + unsigned count = dump->available; + for (unsigned i = 0; i < count; i++) + free(dump->stack[i]); +#endif + + free((void *)dump->stack); +} +int stack_dump_strings_fill(struct stack_dump_state *dump) +{ + if (NULL != dump->stack) + return 0; + +#ifdef STACK_SIMPLE_STRINGS + dump->stack = backtrace_symbols(dump->raw, dump->available); + return dump->stack ? 0 : -ENOMEM; +#else + int retval = stack_dump_symbols_fill(dump); + if (0 != retval) + return retval; + + unsigned count = dump->available; + char **stack = calloc(count, sizeof(char *)); + if (NULL == stack) + return -ENOMEM; + + dump->stack = stack; + + for (unsigned i = 0; i < count; i++) + { + struct module_symbol *sym = dump->symbols[i]; + stack[i] = alloc_printf("[0x%8.8x] %s+0x%2.2zu " + "soname=%s (base=%8.8x)", + sym->sym_addr, sym->sym_name, sym->sym_offset, + sym->so_name, sym->so_addr); + if (NULL == stack[i]) + return -ENOMEM; + } + + return 0; +#endif +} + +void stack_dump_to_fd(struct stack_dump_state *dump, int fd) +{ + backtrace_symbols_fd(dump->raw, dump->available, fd); +} + +int stack_fd_dumper(struct stack_dump_state *dump, intptr_t data) +{ + stack_dump_to_fd(dump, (int)data); + return 0; +} + +int stack_dump(unsigned depth, stack_dumper_t dumper, intptr_t data) +{ + void *stack[depth]; + unsigned nsyms = backtrace(stack, depth); + + struct stack_dump_state state = { + .raw = stack, + .available = nsyms, + .requested = depth, + }; + int retval = (*dumper)(&state, data); + + // stack_dump_fill_symbols() list may need tear-down + stack_dump_strings_free(&state); + stack_dump_symbols_free(&state); + + return retval; +} + +static void stack_segfault_sighandler(int sig) +{ + stack_dump(10, &stack_fd_dumper, 2); + exit(1); +} + +int stack_trace_segfaults(void) +{ + sighandler_t old = signal(SIGSEGV, &stack_segfault_sighandler); + if (SIG_ERR == old) + return -errno; + return 0; +} + + +struct stack_walk_dump_state { + stack_walker_t walker; + intptr_t data; +}; + +static int stack_walk_dumper(struct stack_dump_state *dump, intptr_t data) +{ + int retval = stack_dump_strings_fill(dump); + if (0 != retval) + return retval; + + struct stack_walk_dump_state *walk; + walk = (struct stack_walk_dump_state *)data; + + struct stack_walk_state state = { .dump = dump }; + for (unsigned i = 0; i < dump->available; i++) + { + state.level = i; + state.frame = dump->stack[i]; + retval = (*walk->walker)(&state, walk->data); + if (0 != retval) + return retval; + } + return 0; +} +int stack_walk(unsigned depth, stack_walker_t walker, intptr_t data) +{ + struct stack_walk_dump_state walk = { + .walker = walker, + .data = data, + }; + return stack_dump(depth, &stack_walk_dumper, (intptr_t)&walk); +} diff --git a/src/helper/stack.h b/src/helper/stack.h new file mode 100644 index 0000000..e1668d9 --- /dev/null +++ b/src/helper/stack.h @@ -0,0 +1,101 @@ +#ifndef HELPER_STACK_H +#define HELPER_STACK_H + +#include "types.h" + +struct module_symbol; + +struct stack_dump_state { + /** + * The list of raw stack frames, which may be dumped to a file + * descriptor with stack_dump_to_fd(). The list will contain + * @c available entries, each is the address of one stack frame. + */ + void *const *raw; + /** + * The list of stack frames as module_symbol structures, + * produced by calling stack_dump_symbols(). + */ + struct module_symbol *const *symbols; + /** + * The list of stack frames as strings, produced by calling + * stack_dump_fill(). Calls stack_dump_symbols(), if that + * routine has not called prior to being called for this dump. + */ + char *const *stack; + + /// number of frames produced in @c stack + unsigned available; + + /// number of frames requested + unsigned requested; +}; + +/// The callback used with stack_dump(). +typedef int (*stack_dumper_t)(struct stack_dump_state *dump, intptr_t data); + +struct stack_walk_state { + /// the stack dump + struct stack_dump_state *dump; + + /// current level (out of @c available) + unsigned level; + /// string with current frame information + const char *frame; +}; + +/// The callback used with stack_walk(). +typedef int (*stack_walker_t)(struct stack_walk_state *walk, intptr_t data); + +/** + * Provide stack dumps when a segfault occurs. + * @return On success, returns 0; otherwise, an error code. + */ +int stack_trace_segfaults(void); + +/** + * Produces a backtraces for debugging or crashs, passing a new + * stack_dump_state to the provided callback function. + * + * See http://www.gnu.org/software/libc/manual/html_node/Backtraces.html + * for more information about these glibc functions. + * + * @param depth The depth of the stack trace to produce. + * @param dumper The callback function that will receive the stack_dump_state. + * @param data Private data that can be used by the dumper callback. + * @returns Upon success, returns 0; otherwise, the error. + */ +int stack_dump(unsigned depth, stack_dumper_t dumper, intptr_t data); + +/** + * Writes a stack dump to the given file descriptor. + * @param dump The stack dump provided to a stack_dumper_t call back. + * @param fd The file descriptor to which the stack frame should be written. + */ +void stack_dump_to_fd(struct stack_dump_state *dump, int fd); + +/** + * Fill a stack dump with module_symbol instances, which allows + * callbacks to provide more information that available through other + * means. + */ +int stack_dump_fill_symbols(struct stack_dump_state *dump); + +/** + * Fill a stack dump with string versions. This uses malloc, so it is + * not safe to call in some contexts (e.g. signal handlers). + * @returns Upon success, returns 0; otherwise, the error. + */ +int stack_dump_fill_strings(struct stack_dump_state *dump); + + +/** + * Pass a stack_walk_state for each level of the stack to the @c walker. + * @param depth The depth of the stack trace to produce. + * @param walker The callback function that will receive the stack_walk_state. + * @param data Private data that can be used by the walker callback. + * @returns Upon success, returns 0; otherwise, the error. + */ +int stack_walk(unsigned depth, stack_walker_t walker, intptr_t data); + +#endif // HELPER_STACK_H diff --git a/src/openocd.c b/src/openocd.c index 73a24dd..870d905 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -29,6 +29,7 @@ #endif #include "openocd.h" +#include "stack.h" #include "jtag.h" #include "configuration.h" #include "xsvf.h" @@ -246,12 +247,11 @@ void openocd_sleep_postlude(void) * application will have it's own implementation of main(). */ int openocd_main(int argc, char *argv[]) { - int ret; - - /* initialize commandline interface */ - struct command_context *cmd_ctx; + int ret = stack_trace_segfaults(); + if (ERROR_OK != ret) + return EXIT_FAILURE; - cmd_ctx = setup_command_handler(); + struct command_context *cmd_ctx = setup_command_handler(); #if BUILD_IOUTIL if (ioutil_init(cmd_ctx) != ERROR_OK) -- 1.6.4.4 _______________________________________________ Openocd-development mailing list Openocd-development@lists.berlios.de https://lists.berlios.de/mailman/listinfo/openocd-development