diff options
Diffstat (limited to 'mysys')
-rw-r--r-- | mysys/CMakeLists.txt | 5 | ||||
-rw-r--r-- | mysys/my_addr_resolve.c | 131 | ||||
-rw-r--r-- | mysys/stacktrace.c | 37 |
3 files changed, 168 insertions, 5 deletions
diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index 5b611601955..f258fd7bb7f 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -33,6 +33,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c rijndael.c sha1.c string.c thr_alarm.c thr_lock.c thr_mutex.c thr_rwlock.c tree.c typelib.c base64.c my_memmem.c my_getpagesize.c lf_alloc-pin.c lf_dynarray.c lf_hash.c + my_addr_resolve.c my_atomic.c my_getncpus.c my_safehash.c my_chmod.c my_rnd.c my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c my_rdtsc.c) @@ -70,6 +71,10 @@ TARGET_LINK_LIBRARIES(mysys dbug strings ${ZLIB_LIBRARY} ${LIBNSL} ${LIBM} ${LIBRT}) DTRACE_INSTRUMENT(mysys) +IF(HAVE_BFD_H) + TARGET_LINK_LIBRARIES(mysys bfd) +ENDIF(HAVE_BFD_H) + IF (WIN32) TARGET_LINK_LIBRARIES(mysys IPHLPAPI) ENDIF(WIN32) diff --git a/mysys/my_addr_resolve.c b/mysys/my_addr_resolve.c new file mode 100644 index 00000000000..c9c9212eb7d --- /dev/null +++ b/mysys/my_addr_resolve.c @@ -0,0 +1,131 @@ +/* Copyright (C) 2011 Monty Program Ab + + 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; version 2 of the License. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "mysys_priv.h" +#include <m_string.h> +#include <my_sys.h> +#include <my_stacktrace.h> + +/** + strip the path, leave the file name and the last dirname +*/ +static const char *strip_path(const char *s) +{ + const char *prev, *last; + for(prev= last= s; *s; s++) + if (*s == '/' || *s == '\\') + { + prev= last; + last= s + 1; + } + return prev; +} + +/* + The following is very much single-threaded code and it's only supposed + to be used on shutdown or for a crash report + Or the caller should take care and use mutexes. + + Also it does not free any its memory. For the same reason - + it's only used for crash reports or on shutdown when we already + have a memory leak. +*/ + +#ifdef HAVE_BFD_H +#include <bfd.h> +static bfd *bfdh= 0; +static asymbol **symtable= 0; + +/** + finds a file name, a line number, and a function name corresponding to addr. + + the function name is demangled. + the file name is stripped of its path, only the two last components are kept + the resolving logic is mostly based on addr2line of binutils-2.17 + + @return 0 on success, 1 on failure +*/ +int my_addr_resolve(void *ptr, my_addr_loc *loc) +{ + bfd_vma addr= (intptr)ptr; + asection *sec; + + for (sec= bfdh->sections; sec; sec= sec->next) + { + bfd_vma start; + + if ((bfd_get_section_flags(bfdh, sec) & SEC_ALLOC) == 0) + continue; + + start = bfd_get_section_vma(bfdh, sec); + if (addr < start || addr >= start + bfd_get_section_size(sec)) + continue; + + if (bfd_find_nearest_line(bfdh, sec, symtable, addr - start, + &loc->file, &loc->func, &loc->line)) + { + if (loc->file) + loc->file= strip_path(loc->file); + else + loc->file= ""; + + if (loc->func) + { + const char *str= bfd_demangle(bfdh, loc->func, 3); + if (str) + loc->func= str; + } + + return 0; + } + } + + return 1; +} + +const char *my_addr_resolve_init() +{ + if (!bfdh) + { + uint unused; + char **matching; + + bfdh= bfd_openr(my_progname, NULL); + if (!bfdh) + goto err; + + if (bfd_check_format(bfdh, bfd_archive)) + goto err; + if (!bfd_check_format_matches (bfdh, bfd_object, &matching)) + goto err; + + if (bfd_read_minisymbols(bfdh, FALSE, (void *)&symtable, &unused) < 0) + goto err; + } + return 0; + +err: + return bfd_errmsg(bfd_get_error()); +} +#elif defined(HAVE_LIBELF_H) +/* + another possible implementation. +*/ +#elif defined(MY_ADDR_RESOLVE_FORK) +/* + yet another - just execute addr2line or eu-addr2line, whatever available, + pipe the addresses to it, and parse the output +*/ +#endif diff --git a/mysys/stacktrace.c b/mysys/stacktrace.c index d0897fabb52..a7b71a0bca6 100644 --- a/mysys/stacktrace.c +++ b/mysys/stacktrace.c @@ -210,25 +210,52 @@ static void my_demangle_symbols(char **addrs, int n) #endif /* BACKTRACE_DEMANGLE */ +#if HAVE_MY_ADDR_RESOLVE +static int print_with_addr_resolve(void **addrs, int n) +{ + int i; + const char *err; + + if ((err= my_addr_resolve_init())) + { + fprintf(stderr, "(my_addr_resolve failure: %s)\n", err); + return 0; + } + + for (i= 0; i < n; i++) + { + my_addr_loc loc; + if (my_addr_resolve(addrs[i], &loc)) + backtrace_symbols_fd(addrs+i, 1, fileno(stderr)); + else + fprintf(stderr, "%s:%u(%s)[%p]\n", + loc.file, loc.line, loc.func, addrs[i]); + } + return 1; +} +#endif + void my_print_stacktrace(uchar* stack_bottom, ulong thread_stack) { void *addrs[128]; - char **strings= NULL; + char **strings __attribute__((unused)) = NULL; int n = backtrace(addrs, array_elements(addrs)); fprintf(stderr, "stack_bottom = %p thread_stack 0x%lx\n", stack_bottom, thread_stack); +#if HAVE_MY_ADDR_RESOLVE + if (print_with_addr_resolve(addrs, n)) + return; +#endif #if BACKTRACE_DEMANGLE if ((strings= backtrace_symbols(addrs, n))) { my_demangle_symbols(strings, n); free(strings); + return; } #endif #if HAVE_BACKTRACE_SYMBOLS_FD - if (!strings) - { - backtrace_symbols_fd(addrs, n, fileno(stderr)); - } + backtrace_symbols_fd(addrs, n, fileno(stderr)); #endif } |