From f7237cf37abae7253a3ef24c38d7d6835224c76b Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 5 Nov 2008 13:58:19 +0000 Subject: [PATCH] separated backtrace functionality from leak_detective, used in leak_detective mutex profiling signal handler --- src/charon/daemon.c | 56 ++----- src/libstrongswan/Makefile.am | 1 + src/libstrongswan/utils/backtrace.c | 179 +++++++++++++++++++++++ src/libstrongswan/utils/backtrace.h | 63 ++++++++ src/libstrongswan/utils/leak_detective.c | 152 ++++--------------- src/libstrongswan/utils/leak_detective.h | 5 - src/libstrongswan/utils/mutex.c | 45 +++--- 7 files changed, 303 insertions(+), 198 deletions(-) create mode 100644 src/libstrongswan/utils/backtrace.c create mode 100644 src/libstrongswan/utils/backtrace.h diff --git a/src/charon/daemon.c b/src/charon/daemon.c index ea6766627..d628248d2 100644 --- a/src/charon/daemon.c +++ b/src/charon/daemon.c @@ -16,11 +16,6 @@ * for more details. */ -#ifdef HAVE_DLADDR -# define _GNU_SOURCE -# include -#endif /* HAVE_DLADDR */ - #include #include #include @@ -34,9 +29,6 @@ #include #include #include -#ifdef HAVE_BACKTRACE -# include -#endif /* HAVE_BACKTRACE */ #ifdef CAPABILITIES #include #endif /* CAPABILITIES */ @@ -44,6 +36,7 @@ #include "daemon.h" #include +#include #include #include @@ -433,45 +426,13 @@ static bool initialize(private_daemon_t *this, bool syslog, level_t levels[]) */ static void segv_handler(int signal) { -#ifdef HAVE_BACKTRACE - void *array[20]; - size_t size; - char **strings; - size_t i; - - size = backtrace(array, 20); - strings = backtrace_symbols(array, size); - - DBG1(DBG_JOB, "thread %u received %s. Dumping %d frames from stack:", - pthread_self(), signal == SIGSEGV ? "SIGSEGV" : "SIGILL", size); - - for (i = 0; i < size; i++) - { -#ifdef HAVE_DLADDR - Dl_info info; - - if (dladdr(array[i], &info)) - { - void *ptr = array[i]; - if (strstr(info.dli_fname, ".so")) - { - ptr = (void*)(array[i] - info.dli_fbase); - } - DBG1(DBG_DMN, " %s [%p]", info.dli_fname, ptr); - } - else - { -#endif /* HAVE_DLADDR */ - DBG1(DBG_DMN, " %s", strings[i]); -#ifdef HAVE_DLADDR - } -#endif /* HAVE_DLADDR */ - } - free (strings); -#else /* !HAVE_BACKTRACE */ - DBG1(DBG_DMN, "thread %u received %s", - pthread_self(), signal == SIGSEGV ? "SIGSEGV" : "SIGILL"); -#endif /* HAVE_BACKTRACE */ + backtrace_t *backtrace; + + DBG1(DBG_DMN, "thread %u received %d", pthread_self(), signal); + backtrace = backtrace_create(2); + backtrace->log(backtrace, stderr); + backtrace->destroy(backtrace); + DBG1(DBG_DMN, "killing ourself, received critical signal"); raise(SIGKILL); } @@ -534,6 +495,7 @@ private_daemon_t *daemon_create(void) sigaddset(&action.sa_mask, SIGHUP); sigaction(SIGSEGV, &action, NULL); sigaction(SIGILL, &action, NULL); + sigaction(SIGBUS, &action, NULL); action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 70c8b7a1e..52f2f1892 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -50,6 +50,7 @@ utils/linked_list.c utils/linked_list.h \ utils/enumerator.c utils/enumerator.h \ utils/optionsfrom.c utils/optionsfrom.h \ utils/mutex.c utils/mutex.h \ +utils/backtrace.c utils/backtrace.h \ plugins/plugin_loader.c plugins/plugin_loader.h plugins/plugin.h libstrongswan_la_LIBADD = -lpthread -ldl diff --git a/src/libstrongswan/utils/backtrace.c b/src/libstrongswan/utils/backtrace.c new file mode 100644 index 000000000..be3857248 --- /dev/null +++ b/src/libstrongswan/utils/backtrace.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2006-2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 2 of the License, or (at your + * option) any later version. See . + * + * 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. + * + * $Id$ + */ + +#define _GNU_SOURCE + +#ifdef HAVE_DLADDR +# include +#endif /* HAVE_DLADDR */ + +#ifdef HAVE_BACKTRACE +# include +#endif /* HAVE_BACKTRACE */ + +#include "backtrace.h" + +typedef struct private_backtrace_t private_backtrace_t; + +/** + * Private data of an backtrace_t object. + */ +struct private_backtrace_t { + + /** + * Public backtrace_t interface. + */ + backtrace_t public; + + /** + * Number of stacks frames obtained in stack_frames + */ + int frame_count; + + /** + * Recorded stack frames. + */ + void *frames[]; +}; + +/** + * Implementation of backtrace_t.log + */ +static void log_(private_backtrace_t *this, FILE *file) +{ +#ifdef HAVE_BACKTRACE + size_t i; + char **strings; + + strings = backtrace_symbols(this->frames, this->frame_count); + + fprintf(file, " dumping %d stack frame addresses:\n", this->frame_count); + for (i = 0; i < this->frame_count; i++) + { +#ifdef HAVE_DLADDR + Dl_info info; + + if (dladdr(this->frames[i], &info)) + { + char cmd[1024]; + FILE *output; + char c; + void *ptr = this->frames[i]; + + if (strstr(info.dli_fname, ".so")) + { + ptr = (void*)(this->frames[i] - info.dli_fbase); + } + snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", info.dli_fname, ptr); + if (info.dli_sname) + { + fprintf(file, " \e[33m%s\e[0m @ %p (\e[31m%s\e[0m+0x%x) [%p]\n", + info.dli_fname, info.dli_fbase, info.dli_sname, + this->frames[i] - info.dli_saddr, this->frames[i]); + } + else + { + fprintf(file, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname, + info.dli_fbase, this->frames[i]); + } + fprintf(file, " -> \e[32m"); + output = popen(cmd, "r"); + if (output) + { + while (TRUE) + { + c = getc(output); + if (c == '\n' || c == EOF) + { + break; + } + fputc(c, file); + } + } + else + { +#endif /* HAVE_DLADDR */ + fprintf(file, " %s\n", strings[i]); +#ifdef HAVE_DLADDR + } + fprintf(file, "\n\e[0m"); + } + else + { + fprintf(file, " %s\n", strings[i]); + } +#endif /* HAVE_DLADDR */ + } + free (strings); +#else /* !HAVE_BACKTRACE */ + fprintf(file, "C library does not support backtrace().\n"); +#endif /* HAVE_BACKTRACE */ +} + +/** + * Implementation of backtrace_t.contains_function + */ +static bool contains_function(private_backtrace_t *this, char *function) +{ +#ifdef HAVE_DLADDR + int i; + + for (i = 0; i< this->frame_count; i++) + { + Dl_info info; + + if (dladdr(this->frames[i], &info) && info.dli_sname) + { + if (streq(info.dli_sname, function)) + { + return TRUE; + } + } + } +#endif /* HAVE_DLADDR */ + return FALSE; +} + +/** + * Implementation of backtrace_t.destroy. + */ +static void destroy(private_backtrace_t *this) +{ + free(this); +} + +/** + * See header + */ +backtrace_t *backtrace_create(int skip) +{ + private_backtrace_t *this; + void *frames[50]; + int frame_count; + + frame_count = backtrace(frames, countof(frames)); + this = malloc(sizeof(private_backtrace_t) + frame_count * sizeof(void*)); + this->frame_count = frame_count - skip; + memcpy(this->frames, frames + skip, this->frame_count * sizeof(void*)); + + this->public.log = (void(*)(backtrace_t*,FILE*))log_; + this->public.contains_function = (bool(*)(backtrace_t*, char *function))contains_function; + this->public.destroy = (void(*)(backtrace_t*))destroy; + + return &this->public; +} + diff --git a/src/libstrongswan/utils/backtrace.h b/src/libstrongswan/utils/backtrace.h new file mode 100644 index 000000000..72effd01e --- /dev/null +++ b/src/libstrongswan/utils/backtrace.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 Martin Willi + * Hochschule fuer Technik Rapperswil + * + * 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 2 of the License, or (at your + * option) any later version. See . + * + * 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. + */ + +/** + * @defgroup backtrace backtrace + * @{ @ingroup utils + */ + +#ifndef BACKTRACE_H_ +#define BACKTRACE_H_ + +#include + +#include + +typedef struct backtrace_t backtrace_t; + +/** + * A backtrace registers the frames on the stack during creation. + */ +struct backtrace_t { + + /** + * Log the backtrace to a FILE stream. + */ + void (*log)(backtrace_t *this, FILE *file); + + /** + * Check if the backtrace contains a frame in a specific function. + * + * @param function name + * @return TRUE if function is in the stack + */ + bool (*contains_function)(backtrace_t *this, char *function); + + /** + * Destroy a backtrace instance. + */ + void (*destroy)(backtrace_t *this); +}; + +/** + * Create a backtrace of the current stack. + * + * @param skip how many of the innerst frames to skip + * @return backtrace + */ +backtrace_t *backtrace_create(int skip); + +#endif /* BACKTRACE_H_ @}*/ + diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 70726da3b..bb33259ff 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -14,11 +14,6 @@ * * $Id$ */ - -#ifdef HAVE_DLADDR -# define _GNU_SOURCE -# include -#endif /* HAVE_DLADDR */ #include #include @@ -28,21 +23,18 @@ #include #include #include -#include #include #include #include #include #include #include -#ifdef HAVE_BACKTRACE -# include -#endif /* HAVE_BACKTRACE */ #include "leak_detective.h" #include #include +#include typedef struct private_leak_detective_t private_leak_detective_t; @@ -105,16 +97,6 @@ struct memory_header_t { */ u_int bytes; - /** - * Stack frames at the time of allocation - */ - void *stack_frames[STACK_FRAMES_COUNT]; - - /** - * Number of stacks frames obtained in stack_frames - */ - int stack_frame_count; - /** * Pointer to previous entry in linked list */ @@ -125,6 +107,11 @@ struct memory_header_t { */ memory_header_t *next; + /** + * backtrace taken during (re-)allocation + */ + backtrace_t *backtrace; + /** * magic bytes to detect bad free or heap underflow, MEMORY_HEADER_MAGIC */ @@ -151,7 +138,7 @@ struct memory_tail_t { static memory_header_t first_header = { magic: MEMORY_HEADER_MAGIC, bytes: 0, - stack_frame_count: 0, + backtrace: NULL, previous: NULL, next: NULL }; @@ -161,76 +148,6 @@ static memory_header_t first_header = { */ static bool installed = FALSE; -/** - * log stack frames queried by backtrace() - * TODO: Dump symbols of static functions. This could be done with - * the addr2line utility or the GNU BFD Library... - */ -static void log_stack_frames(void **stack_frames, int stack_frame_count) -{ -#ifdef HAVE_BACKTRACE - size_t i; - char **strings; - - strings = backtrace_symbols(stack_frames, stack_frame_count); - - fprintf(stderr, " dumping %d stack frame addresses:\n", stack_frame_count); - for (i = 0; i < stack_frame_count; i++) - { -#ifdef HAVE_DLADDR - Dl_info info; - - if (dladdr(stack_frames[i], &info)) - { - char cmd[1024]; - FILE *output; - char c; - void *ptr = stack_frames[i]; - - if (strstr(info.dli_fname, ".so")) - { - ptr = (void*)(stack_frames[i] - info.dli_fbase); - } - snprintf(cmd, sizeof(cmd), "addr2line -e %s %p", info.dli_fname, ptr); - if (info.dli_sname) - { - fprintf(stderr, " \e[33m%s\e[0m @ %p (\e[31m%s\e[0m+0x%x) [%p]\n", - info.dli_fname, info.dli_fbase, info.dli_sname, - stack_frames[i] - info.dli_saddr, stack_frames[i]); - } - else - { - fprintf(stderr, " \e[33m%s\e[0m @ %p [%p]\n", info.dli_fname, - info.dli_fbase, stack_frames[i]); - } - fprintf(stderr, " -> \e[32m"); - output = popen(cmd, "r"); - if (output) - { - while (TRUE) - { - c = getc(output); - if (c == '\n' || c == EOF) - { - break; - } - fputc(c, stderr); - } - } - else - { -#endif /* HAVE_DLADDR */ - fprintf(stderr, " %s\n", strings[i]); -#ifdef HAVE_DLADDR - } - fprintf(stderr, "\n\e[0m"); - } -#endif /* HAVE_DLADDR */ - } - free (strings); -#endif /* HAVE_BACKTRACE */ -} - /** * Leak report white list * @@ -238,6 +155,8 @@ static void log_stack_frames(void **stack_frames, int stack_frame_count) * otherwise on leak report. */ char *whitelist[] = { + /* backtraces, including own */ + "backtrace_create", /* pthread stuff */ "pthread_create", "pthread_setspecific", @@ -284,27 +203,16 @@ char *whitelist[] = { /** * check if a stack frame contains functions listed above */ -static bool is_whitelisted(void **stack_frames, int stack_frame_count) +static bool is_whitelisted(backtrace_t *backtrace) { - int i, j; - -#ifdef HAVE_DLADDR - for (i=0; i< stack_frame_count; i++) + int i; + for (i = 0; i < sizeof(whitelist)/sizeof(char*); i++) { - Dl_info info; - - if (dladdr(stack_frames[i], &info) && info.dli_sname) - { - for (j = 0; j < sizeof(whitelist)/sizeof(char*); j++) - { - if (streq(info.dli_sname, whitelist[j])) - { - return TRUE; - } - } + if (backtrace->contains_function(backtrace, whitelist[i])) + { + return TRUE; } } -#endif /* HAVE_DLADDR */ return FALSE; } @@ -318,7 +226,7 @@ void report_leaks() for (hdr = first_header.next; hdr != NULL; hdr = hdr->next) { - if (is_whitelisted(hdr->stack_frames, hdr->stack_frame_count)) + if (is_whitelisted(hdr->backtrace)) { whitelisted++; } @@ -326,7 +234,7 @@ void report_leaks() { fprintf(stderr, "Leak (%d bytes at %p):\n", hdr->bytes, hdr + 1); /* skip the first frame, contains leak detective logic */ - log_stack_frames(hdr->stack_frames + 1, hdr->stack_frame_count - 1); + hdr->backtrace->log(hdr->backtrace, stderr); leaks++; } } @@ -403,7 +311,7 @@ void *malloc_hook(size_t bytes, const void *caller) hdr->magic = MEMORY_HEADER_MAGIC; hdr->bytes = bytes; - hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT); + hdr->backtrace = backtrace_create(3); tail->magic = MEMORY_TAIL_MAGIC; install_hooks(); @@ -426,10 +334,9 @@ void *malloc_hook(size_t bytes, const void *caller) */ void free_hook(void *ptr, const void *caller) { - void *stack_frames[STACK_FRAMES_COUNT]; - int stack_frame_count; memory_header_t *hdr; memory_tail_t *tail; + backtrace_t *backtrace; pthread_t thread_id = pthread_self(); int oldpolicy; struct sched_param oldparams, params; @@ -455,8 +362,9 @@ void free_hook(void *ptr, const void *caller) fprintf(stderr, "freeing invalid memory (%p): " "header magic 0x%x, tail magic 0x%x:\n", ptr, hdr->magic, tail->magic); - stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT); - log_stack_frames(stack_frames, stack_frame_count); + backtrace = backtrace_create(3); + backtrace->log(backtrace, stderr); + backtrace->destroy(backtrace); } else { @@ -466,10 +374,11 @@ void free_hook(void *ptr, const void *caller) hdr->next->previous = hdr->previous; } hdr->previous->next = hdr->next; - + hdr->backtrace->destroy(hdr->backtrace); + /* clear MAGIC, set mem to something remarkable */ memset(hdr, MEMORY_FREE_PATTERN, hdr->bytes + sizeof(memory_header_t)); - + free(hdr); } @@ -483,9 +392,8 @@ void free_hook(void *ptr, const void *caller) void *realloc_hook(void *old, size_t bytes, const void *caller) { memory_header_t *hdr; - void *stack_frames[STACK_FRAMES_COUNT]; - int stack_frame_count; memory_tail_t *tail; + backtrace_t *backtrace; pthread_t thread_id = pthread_self(); int oldpolicy; struct sched_param oldparams, params; @@ -512,8 +420,9 @@ void *realloc_hook(void *old, size_t bytes, const void *caller) fprintf(stderr, "reallocating invalid memory (%p): " "header magic 0x%x, tail magic 0x%x:\n", old, hdr->magic, tail->magic); - stack_frame_count = backtrace(stack_frames, STACK_FRAMES_COUNT); - log_stack_frames(stack_frames, stack_frame_count); + backtrace = backtrace_create(3); + backtrace->log(backtrace, stderr); + backtrace->destroy(backtrace); } /* clear tail magic, allocate, set tail magic */ memset(&tail->magic, MEMORY_ALLOC_PATTERN, sizeof(tail->magic)); @@ -523,7 +432,8 @@ void *realloc_hook(void *old, size_t bytes, const void *caller) /* update statistics */ hdr->bytes = bytes; - hdr->stack_frame_count = backtrace(hdr->stack_frames, STACK_FRAMES_COUNT); + hdr->backtrace->destroy(hdr->backtrace); + hdr->backtrace = backtrace_create(3); /* update header of linked list neighbours */ if (hdr->next) diff --git a/src/libstrongswan/utils/leak_detective.h b/src/libstrongswan/utils/leak_detective.h index 763814726..3773fb8e5 100644 --- a/src/libstrongswan/utils/leak_detective.h +++ b/src/libstrongswan/utils/leak_detective.h @@ -21,11 +21,6 @@ #ifndef LEAK_DETECTIVE_H_ #define LEAK_DETECTIVE_H_ -/** - * Maximum depth stack frames to register - */ -#define STACK_FRAMES_COUNT 20 - typedef struct leak_detective_t leak_detective_t; /** diff --git a/src/libstrongswan/utils/mutex.c b/src/libstrongswan/utils/mutex.c index 71d59ca42..25d2788aa 100644 --- a/src/libstrongswan/utils/mutex.c +++ b/src/libstrongswan/utils/mutex.c @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -53,14 +54,9 @@ struct private_mutex_t { struct timeval waited; /** - * creator of the mutex + * backtrace where mutex has been created */ - void *stack[10]; - - /** - * number of pointers in stack - */ - int stack_size; + backtrace_t *backtrace; #endif /* LOCK_PROFILER */ /** @@ -111,23 +107,22 @@ struct private_condvar_t { #include /** - * print mutex locking statistics + * Print and cleanup mutex profiler */ -static void print_stats(private_mutex_t *this) +static void profiler_cleanup(private_mutex_t *this) { - int i; - - DBG1("waited %d.%06ds in mutex, created at:", - this->waited.tv_sec, this->waited.tv_usec); - for (i = 0; i < this->stack_size; i++) - { - DBG1(" %p", this->stack[i]); - } + fprintf(stderr, "waited %d.%06ds in mutex, created at:", + this->waited.tv_sec, this->waited.tv_usec); + this->backtrace->log(this->backtrace, stderr); + this->backtrace->destroy(this->backtrace); } -static void init_stats(private_mutex_t *this) +/** + * Initialize mutex profiler + */ +static void profiler_init(private_mutex_t *this) { - this->stack_size = backtrace(this->stack, countof(this->stack)); + this->backtrace = backtrace_create(3); timerclear(&this->waited); } @@ -151,8 +146,8 @@ static void lock(private_mutex_t *this) #else /* !LOCK_PROFILER */ /** dummy implementations */ -static void print_stats(private_mutex_t *this) {} -static void init_stats(private_mutex_t *this) {} +static void profiler_cleanup(private_mutex_t *this) {} +static void profiler_init(private_mutex_t *this) {} /** * Implementation of mutex_t.lock. @@ -224,7 +219,7 @@ static void unlock_r(private_r_mutex_t *this) */ static void mutex_destroy(private_mutex_t *this) { - print_stats(this); + profiler_cleanup(this); pthread_mutex_destroy(&this->mutex); free(this); } @@ -234,7 +229,7 @@ static void mutex_destroy(private_mutex_t *this) */ static void mutex_destroy_r(private_r_mutex_t *this) { - print_stats(&this->generic); + profiler_cleanup(&this->generic); pthread_mutex_destroy(&this->generic.mutex); pthread_key_delete(this->times); free(this); @@ -258,7 +253,7 @@ mutex_t *mutex_create(mutex_type_t type) pthread_mutex_init(&this->generic.mutex, NULL); pthread_key_create(&this->times, NULL); this->generic.recursive = TRUE; - init_stats(&this->generic); + profiler_init(&this->generic); this->thread = 0; return &this->generic.public; @@ -274,7 +269,7 @@ mutex_t *mutex_create(mutex_type_t type) pthread_mutex_init(&this->mutex, NULL); this->recursive = FALSE; - init_stats(this); + profiler_init(this); return &this->public; }