diff --git a/kmtrace/README b/kmtrace/README index 6084b2b7..c3e7ed5c 100644 --- a/kmtrace/README +++ b/kmtrace/README @@ -1,8 +1,6 @@ This is a KDE tool to assist with malloc debugging using glibc's "mtrace" functionality. Unfortunately the mtrace that is part of current (9/9/2000) glibc versions only logs the return-address of the malloc/free call. -The file mtrace.c in this directory logs a complete backtrace upon malloc/ -free. THIS PROGRAM DEPENDS ON GLIBC! It does not pretend to be portable. diff --git a/kmtrace/ktrace.c b/kmtrace/ktrace.c index 8b9eb2f4..e7eee47b 100644 --- a/kmtrace/ktrace.c +++ b/kmtrace/ktrace.c @@ -112,27 +112,36 @@ void kuntrace(void); static void addAllocationToTree(void); -static void tr_freehook __P ((void*, const void*)); -static void* tr_reallochook __P ((void*, size_t, - const void*)); -static void* tr_mallochook __P ((size_t, const void*)); -/* Old hook values. */ -static void (*tr_old_free_hook) __P ((void* ptr, const void*)); -static void* (*tr_old_malloc_hook) __P ((size_t size, - const void*)); -static void* (*tr_old_realloc_hook) __P ((void* ptr, - size_t size, - const void*)); +// Pointers to real calls +static void* (*real_malloc_ptr) (size_t size) = NULL; +static void* (*real_realloc_ptr) (void *ptr, size_t size) = NULL; +static void (*real_free_ptr) (void *ptr) = NULL; + +// This is used to return "allocated" memory until the pointer +// to the real malloc function becomes known. +// It seems about 75k of memory are required before that happens, +// but the buffer provides more space to cater for more if needed +// in other OS. +#define MALLOC_INIT_SIZE (1024 * 256) +static char malloc_init_buf[MALLOC_INIT_SIZE]; +static size_t malloc_init_pos = 0; +static pthread_mutex_t malloc_init_lock = PTHREAD_MUTEX_INITIALIZER; static FILE* mallstream; static char malloc_trace_buffer[TRACE_BUFFER_SIZE]; +static pthread_mutex_t malloc_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t free_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t realloc_lock = PTHREAD_MUTEX_INITIALIZER; -/* Address to breakpoint on accesses to... */ -void* mallwatch; - -static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static int tr_malloc_hook_enabled = 0; +static int tr_realloc_hook_enabled = 0; +static int tr_free_hook_enabled = 0; +// Hooks active in this thread +static __thread int tr_malloc_hook_active = 0; +static __thread int tr_realloc_hook_active = 0; +static __thread int tr_free_hook_active = 0; static int bt_size; static void *bt[TR_BT_SIZE + 1]; @@ -159,14 +168,15 @@ static char* mallTreeFile = NULL; static FILE* mallTreeStream = NULL; static long mallThreshold = 2000; -/* This function is called when the block being alloc'd, realloc'd, or - * freed has an address matching the variable "mallwatch". In a - * debugger, set "mallwatch" to the address of interest, then put a - * breakpoint on tr_break. */ -void tr_break __P ((void)); -void -tr_break() +// Get real function pointers on loading +static void __attribute__((constructor)) init(void) { + if (!real_malloc_ptr) + { + real_malloc_ptr = (void* (*)(size_t))(dlsym(RTLD_NEXT, "malloc")); + real_realloc_ptr = (void* (*)(void *, size_t))(dlsym(RTLD_NEXT, "realloc")); + real_free_ptr = (void (*)(void *))(dlsym(RTLD_NEXT, "free")); + } } __inline__ static void @@ -373,104 +383,125 @@ tr_log(const void* caller, void* ptr, void* old, } } -static void -tr_freehook (ptr, caller) - void* ptr; - const void* caller; +void free(void *ptr) { - if (ptr == NULL) + if (ptr == NULL || + (ptr >= (void*)malloc_init_buf && ptr < (void*)(malloc_init_buf + malloc_init_pos))) + { return; - if (ptr == mallwatch) - tr_break (); + } + + if (!real_free_ptr) + { + // Should never happen! + return; + } + + if (!tr_free_hook_enabled || tr_free_hook_active) + { + // Real function is called if hooks are not enabled or if + // free is called recursively while logging data (should + // probably never happen!) + return (*real_free_ptr)(ptr); + } + + pthread_mutex_lock(&free_lock); + tr_free_hook_active = 1; - pthread_mutex_lock(&lock); #ifdef PROFILE tr_frees++; tr_current_mallocs--; #endif - __free_hook = tr_old_free_hook; - - if (tr_old_free_hook != NULL) - (*tr_old_free_hook) (ptr, caller); - else - free(ptr); + void *caller = __builtin_return_address(0); + (*real_free_ptr)(ptr); tr_log(caller, ptr, 0, 0, TR_FREE); - __free_hook = tr_freehook; - pthread_mutex_unlock(&lock); + tr_free_hook_active = 0; + pthread_mutex_unlock(&free_lock); } -static void* -tr_mallochook (size, caller) - size_t size; - const void* caller; +void *malloc(size_t size) { - void* hdr; + if (!real_malloc_ptr) + { + // Return memory from static buffer if available + pthread_mutex_lock(&malloc_init_lock); + if ((malloc_init_pos + size) > MALLOC_INIT_SIZE) + { + pthread_mutex_unlock(&malloc_init_lock); + return NULL; + } + else + { + void *ptr = (void*)(malloc_init_buf + malloc_init_pos); + malloc_init_pos += size; + pthread_mutex_unlock(&malloc_init_lock); + return ptr; + } + } - pthread_mutex_lock(&lock); + if (!tr_malloc_hook_enabled || tr_malloc_hook_active) + { + // Real function is called if hooks are not enabled or if + // malloc is called recursively while logging data + return (*real_malloc_ptr)(size); + } - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; - __free_hook = tr_old_free_hook; + pthread_mutex_lock(&malloc_lock); + tr_malloc_hook_active = 1; - if (tr_old_malloc_hook != NULL) - hdr = (void*) (*tr_old_malloc_hook) (size, caller); - else - hdr = (void*) malloc(size); + void *caller = __builtin_return_address(0); + void *hdr = (*real_malloc_ptr)(size); tr_log(caller, hdr, 0, size, TR_MALLOC); + /* We only build the allocation tree if mallTreeFile has been set. */ if (mallTreeFile) addAllocationToTree(); - __malloc_hook = tr_mallochook; - __realloc_hook = tr_reallochook; - __free_hook = tr_freehook; - #ifdef PROFILE tr_mallocs++; tr_current_mallocs++; if (tr_current_mallocs > tr_max_mallocs) tr_max_mallocs = tr_current_mallocs; #endif - pthread_mutex_unlock(&lock); - if (hdr == mallwatch) - tr_break (); + tr_malloc_hook_active = 0; + pthread_mutex_unlock(&malloc_lock); return hdr; } -static void* -tr_reallochook (ptr, size, caller) - void* ptr; - size_t size; - const void* caller; +void* realloc(void *ptr, size_t size) { - void* hdr; - - if (ptr == mallwatch) - tr_break (); + if (ptr >= (void*)malloc_init_buf && ptr < (void*)(malloc_init_buf + malloc_init_pos)) + { + // Reallocating pointer from initial static buffer, nothing to free. Just call malloc + return malloc(size); + } - pthread_mutex_lock(&lock); + if (!real_realloc_ptr) + { + // Should never happen + return NULL; + } - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; + if (!tr_realloc_hook_enabled || tr_realloc_hook_active) + { + // Real function is called if hooks are not enabled or if + // realloc is called recursively while logging data + return (*real_realloc_ptr)(ptr, size); + } - if (tr_old_realloc_hook != NULL) - hdr = (void*) (*tr_old_realloc_hook) (ptr, size, caller); - else - hdr = (void*) realloc (ptr, size); + pthread_mutex_lock(&realloc_lock); + tr_realloc_hook_active = 1; + void *caller = __builtin_return_address(0); + void *hdr = (*real_realloc_ptr)(ptr, size); tr_log(caller, hdr, ptr, size, TR_REALLOC); - __free_hook = tr_freehook; - __malloc_hook = tr_mallochook; - __realloc_hook = tr_reallochook; - #ifdef PROFILE - /* If ptr is 0 there was no previos malloc of this location */ + // If ptr is 0 there was no previos malloc of this location if (ptr == NULL) { tr_mallocs++; @@ -480,10 +511,8 @@ tr_reallochook (ptr, size, caller) } #endif - pthread_mutex_unlock(&lock); - - if (hdr == mallwatch) - tr_break (); + tr_realloc_hook_active = 0; + pthread_mutex_unlock(&realloc_lock); return hdr; } @@ -671,10 +700,7 @@ release_libc_mem (void) #endif /* We enable tracing if either the environment variable MALLOC_TRACE - * or the variable MALLOC_TREE are set, or if the variable mallwatch - * has been patched to an address that the debugging user wants us to - * stop on. When patching mallwatch, don't forget to set a breakpoint - * on tr_break! */ + * or the variable MALLOC_TREE are set */ void ktrace() { @@ -701,7 +727,7 @@ ktrace() if( getenv("MALLOC_THRESHOLD") != NULL ) mallThreshold = atol(getenv("MALLOC_THRESHOLD")); #endif - if (mallfile != NULL || mallTreeFile != NULL || mallwatch != NULL) + if (mallfile != NULL || mallTreeFile != NULL) { mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w"); if (mallstream != NULL) @@ -717,14 +743,10 @@ ktrace() if(*buf) fprintf (mallstream, "#%s\n", buf); - /* Save old hooks and hook in our own functions for all - * malloc, realloc and free calls */ - tr_old_free_hook = __free_hook; - __free_hook = tr_freehook; - tr_old_malloc_hook = __malloc_hook; - __malloc_hook = tr_mallochook; - tr_old_realloc_hook = __realloc_hook; - __realloc_hook = tr_reallochook; + // Enable hooks + tr_malloc_hook_enabled = 1; + tr_realloc_hook_enabled = 1; + tr_free_hook_enabled = 1; tr_cache_pos = TR_CACHE_SIZE; do @@ -748,14 +770,14 @@ ktrace() void kuntrace() { + // Disable hooks + tr_malloc_hook_enabled = 0; + tr_realloc_hook_enabled = 0; + tr_free_hook_enabled = 0; + if (mallstream == NULL) return; - /* restore hooks to original values */ - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; - if (removeBranchesBelowThreshold(CallTree)) CallTree = 0; if (mallTreeFile) @@ -802,9 +824,6 @@ int fork() { close(fileno(mallstream)); mallstream = NULL; - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; } } return result; diff --git a/kmtrace/mtrace.c b/kmtrace/mtrace.c deleted file mode 100644 index cef74783..00000000 --- a/kmtrace/mtrace.c +++ /dev/null @@ -1,383 +0,0 @@ -/* More debugging hooks for `malloc'. - Copyright (C) 1991,92,93,94,96,97,98,99,2000 Free Software Foundation, Inc. - Written April 2, 1991 by John Gilmore of Cygnus Support. - Based on mcheck.c by Mike Haertel. - Hacked by AK - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - - The author may be reached (Email) at the address mike@ai.mit.edu, - or (US mail) as Mike Haertel c/o Free Software Foundation. */ - -#define _LIBC -#define MALLOC_HOOKS - -#ifndef _MALLOC_INTERNAL -#define _MALLOC_INTERNAL -#include -#include -#include -#endif - -#include - -#include -#include -#include - -#include -#include - -#ifdef USE_IN_LIBIO -# include -# define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l) -#endif - -#define TRACE_BUFFER_SIZE 512 - -static FILE *mallstream; -static const char mallenv[]= "MALLOC_TRACE"; -static char malloc_trace_buffer[TRACE_BUFFER_SIZE]; - -__libc_lock_define_initialized (static, lock) - -/* Address to breakpoint on accesses to... */ -__ptr_t mallwatch; - -/* File name and line number information, for callers that had - the foresight to call through a macro. */ -char *_mtrace_file; -int _mtrace_line; - -/* Old hook values. */ -static void (*tr_old_free_hook) __P ((__ptr_t ptr, const __ptr_t)); -static __ptr_t (*tr_old_malloc_hook) __P ((__malloc_size_t size, - const __ptr_t)); -static __ptr_t (*tr_old_realloc_hook) __P ((__ptr_t ptr, - __malloc_size_t size, - const __ptr_t)); - -#define TR_PIPELINE_SIZE 16 -#define TR_BT_SIZE 80 -#define TR_HASHTABLE_SIZE 9973 - -#define TR_NONE 0 -#define TR_MALLOC 1 -#define TR_REALLOC 2 -#define TR_FREE 3 - -typedef struct { - int op; - __ptr_t ptr; - __ptr_t old; - __malloc_size_t size; - int bt_size; - void *bt[TR_BT_SIZE+1]; -} tr_entry; - -static tr_entry tr_pipeline[TR_PIPELINE_SIZE]; -static int tr_pipeline_pos; -static void *tr_hashtable[TR_HASHTABLE_SIZE]; - - -/* This function is called when the block being alloc'd, realloc'd, or - freed has an address matching the variable "mallwatch". In a debugger, - set "mallwatch" to the address of interest, then put a breakpoint on - tr_break. */ - -void tr_break __P ((void)); -void -tr_break () -{ -} - -static void -tr_backtrace(void **bt, int size) -{ - char buf[20]; - int i; - Dl_info info; - for(i = 0; i < size; i++) - { - long hash = (((unsigned long)bt[i]) / 4) % TR_HASHTABLE_SIZE; - if ((tr_hashtable[hash]!= bt[i]) && _dl_addr(bt[i], &info) && info.dli_fname && *info.dli_fname) - { - if (bt[i] >= (void *) info.dli_saddr) - sprintf(buf, "+%#lx", (unsigned long)(bt[i] - info.dli_saddr)); - else - sprintf(buf, "-%#lx", (unsigned long)(info.dli_saddr - bt[i])); - fprintf(mallstream, "%s%s%s%s%s[%p]\n", - info.dli_fname ?: "", - info.dli_sname ? "(" : "", - info.dli_sname ?: "", - info.dli_sname ? buf : "", - info.dli_sname ? ")" : "", - bt[i]); - tr_hashtable[hash] = bt[i]; - } - else { - fprintf(mallstream, "[%p]\n", bt[i]); - } - } -} - -static void -inline -tr_log(const __ptr_t caller, __ptr_t ptr, __ptr_t old, __malloc_size_t size, int op) -{ - switch(op) - { - case TR_REALLOC: - break; - case TR_MALLOC: - break; - case TR_FREE: - { - int i = tr_pipeline_pos; - do { - if (i) i--; else i = TR_PIPELINE_SIZE-1; - if (tr_pipeline[i].ptr == ptr) - { - if (tr_pipeline[i].op == TR_MALLOC) - { - tr_pipeline[i].op = TR_NONE; - tr_pipeline[i].ptr = NULL; - return; - } - break; - } - } while (i != tr_pipeline_pos); - } - } - - if (tr_pipeline[tr_pipeline_pos].op) - { - putc('@', mallstream); - putc('\n', mallstream); - /* Generate backtrace... - * We throw out the first frame (tr_mallochook) - * end the last one (_start) - */ - tr_backtrace(&(tr_pipeline[tr_pipeline_pos].bt[1]), - tr_pipeline[tr_pipeline_pos].bt_size-2); - - switch(tr_pipeline[tr_pipeline_pos].op) - { - case TR_MALLOC: - fprintf (mallstream, "+ %p %#lx\n", - tr_pipeline[tr_pipeline_pos].ptr, - (unsigned long int) tr_pipeline[tr_pipeline_pos].size); - break; - case TR_FREE: - fprintf (mallstream, "- %p\n", - tr_pipeline[tr_pipeline_pos].ptr); - break; - case TR_REALLOC: - fprintf (mallstream, "< %p\n", - tr_pipeline[tr_pipeline_pos].old); - fprintf (mallstream, "> %p %#lx\n", - tr_pipeline[tr_pipeline_pos].ptr, - (unsigned long int) tr_pipeline[tr_pipeline_pos].size); - break; - } - } - - tr_pipeline[tr_pipeline_pos].op = op; - tr_pipeline[tr_pipeline_pos].ptr = ptr; - tr_pipeline[tr_pipeline_pos].old = old; - tr_pipeline[tr_pipeline_pos].size = size; - tr_pipeline[tr_pipeline_pos].bt_size = backtrace( - tr_pipeline[tr_pipeline_pos].bt, TR_BT_SIZE); - tr_pipeline_pos++; - if (tr_pipeline_pos == TR_PIPELINE_SIZE) - tr_pipeline_pos = 0; -} - -static void tr_freehook __P ((__ptr_t, const __ptr_t)); -static void -tr_freehook (ptr, caller) - __ptr_t ptr; - const __ptr_t caller; -{ - if (ptr == NULL) - return; - __libc_lock_lock (lock); - tr_log(caller, ptr, 0, 0, TR_FREE ); - __libc_lock_unlock (lock); - if (ptr == mallwatch) - tr_break (); - __libc_lock_lock (lock); - __free_hook = tr_old_free_hook; - if (tr_old_free_hook != NULL) - (*tr_old_free_hook) (ptr, caller); - else - free (ptr); - __free_hook = tr_freehook; - __libc_lock_unlock (lock); -} - -static __ptr_t tr_mallochook __P ((__malloc_size_t, const __ptr_t)); -static __ptr_t -tr_mallochook (size, caller) - __malloc_size_t size; - const __ptr_t caller; -{ - __ptr_t hdr; - - __libc_lock_lock (lock); - - __malloc_hook = tr_old_malloc_hook; - if (tr_old_malloc_hook != NULL) - hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller); - else - hdr = (__ptr_t) malloc (size); - __malloc_hook = tr_mallochook; - - tr_log(caller, hdr, 0, size, TR_MALLOC); - - __libc_lock_unlock (lock); - - if (hdr == mallwatch) - tr_break (); - - return hdr; -} - -static __ptr_t tr_reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t)); -static __ptr_t -tr_reallochook (ptr, size, caller) - __ptr_t ptr; - __malloc_size_t size; - const __ptr_t caller; -{ - __ptr_t hdr; - - if (ptr == mallwatch) - tr_break (); - - __libc_lock_lock (lock); - - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; - if (tr_old_realloc_hook != NULL) - hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller); - else - hdr = (__ptr_t) realloc (ptr, size); - __free_hook = tr_freehook; - __malloc_hook = tr_mallochook; - __realloc_hook = tr_reallochook; - - tr_log(caller, hdr, ptr, size, TR_REALLOC); - - __libc_lock_unlock (lock); - - if (hdr == mallwatch) - tr_break (); - - return hdr; -} - - -#ifdef _LIBC -extern void __libc_freeres (void); - -/* This function gets called to make sure all memory the library - allocates get freed and so does not irritate the user when studying - the mtrace output. */ -static void -release_libc_mem (void) -{ - /* Only call the free function if we still are running in mtrace mode. */ - if (mallstream != NULL) - __libc_freeres (); -} -#endif - - -/* We enable tracing if either the environment variable MALLOC_TRACE - is set, or if the variable mallwatch has been patched to an address - that the debugging user wants us to stop on. When patching mallwatch, - don't forget to set a breakpoint on tr_break! */ - -void -mtrace () -{ -#ifdef _LIBC - static int added_atexit_handler; -#endif - char *mallfile; - - /* Don't panic if we're called more than once. */ - if (mallstream != NULL) - return; - -#ifdef _LIBC - /* When compiling the GNU libc we use the secure getenv function - which prevents the misuse in case of SUID or SGID enabled - programs. */ - mallfile = __secure_getenv (mallenv); -#else - mallfile = getenv (mallenv); -#endif - if (mallfile != NULL || mallwatch != NULL) - { - mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w"); - if (mallstream != NULL) - { - /* Be sure it doesn't malloc its buffer! */ - setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE); - fprintf (mallstream, "= Start\n"); - tr_old_free_hook = __free_hook; - __free_hook = tr_freehook; - tr_old_malloc_hook = __malloc_hook; - __malloc_hook = tr_mallochook; - tr_old_realloc_hook = __realloc_hook; - __realloc_hook = tr_reallochook; - - tr_pipeline_pos = TR_PIPELINE_SIZE; - for(;tr_pipeline_pos--;) - { - tr_pipeline[tr_pipeline_pos].op = TR_NONE; - tr_pipeline[tr_pipeline_pos].ptr = NULL; - } - memset(tr_hashtable, 0, sizeof(void *)*TR_HASHTABLE_SIZE); -#ifdef _LIBC - if (!added_atexit_handler) - { - added_atexit_handler = 1; - atexit (release_libc_mem); - } -#endif - } - } -} - -void -muntrace () -{ - if (mallstream == NULL) - return; - - fprintf (mallstream, "= End\n"); - fclose (mallstream); - mallstream = NULL; - __free_hook = tr_old_free_hook; - __malloc_hook = tr_old_malloc_hook; - __realloc_hook = tr_old_realloc_hook; -} - -