/* * Copyright 1996, Silicon Graphics, Inc. * ALL RIGHTS RESERVED * * UNPUBLISHED -- Rights reserved under the copyright laws of the United * States. Use of a copyright notice is precautionary only and does not * imply publication or disclosure. * * U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND: * Use, duplication or disclosure by the Government is subject to restrictions * as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights * in Technical Data and Computer Software clause at DFARS 252.227-7013 and/or * in similar or successor clauses in the FAR, or the DOD or NASA FAR * Supplement. Contractor/manufacturer is Silicon Graphics, Inc., * 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that (i) the above copyright notices and this * permission notice appear in all copies of the software and related * documentation, and (ii) the name of Silicon Graphics may not be * used in any advertising or publicity relating to the software * without the specific, prior written permission of Silicon Graphics. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, * INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY * THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE * OR PERFORMANCE OF THIS SOFTWARE. * */ /* * This source has been customized for NSD static linking! * * MALLOC_BAD_ASSET - hardwired message (always assert) * MALLOC_CHECK_ATEXEC - defaults to 0 * */ /* dmalloc.c Front-end to malloc that does counting and stuff. Expects there to be a "real" malloc and free and realloc. */ #undef NDEBUG /* don't even think about it!! */ #include "dmalloc.h" #include "stacktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include /* sproc creates three spinlocks: __malloclock, __monlock, and __randlock. We can't use __malloclock because we call the real malloc which uses it, and that would cause deadlock. */ static pid_t lock_owner;/* XXXassume shared--this may not be a good assumption*/ static int lock_depth; /* number of times this process holds it */ #define THELOCK __randlock extern ulock_t THELOCK; #ifdef LOCK_DEBUG #define lock_debug getenv("MALLOC_LOCK_DEBUG") #else #define lock_debug 0 #endif static void LOCK_MALLOC(int inc) { if (lock_debug) printf("%d(%d): LOCKING %d+%d (owner=%d)\n", get_pid(), getpid(), lock_depth,inc,lock_owner); if (!THELOCK) { lock_depth += inc; if (lock_debug) printf("%d(%d): locked %d\n", get_pid(), getpid(), lock_depth); return; /* no sprocs occured yet */ } if (lock_owner != get_pid()) { /* someone else has it, or no one does */ ussetlock(THELOCK); lock_owner = get_pid(); assert(lock_depth == 0); } else { /* this process already has it */ assert(lock_depth > 0); } lock_depth += inc; /* we have the lock */ if (lock_debug) printf("%d(%d): LOCKED %d\n", get_pid(), getpid(), lock_depth); } static void UNLOCK_MALLOC(int inc) { if (lock_debug) printf("%d(%d): UNLOCKING %d-%d (owner=%d)\n", get_pid(), getpid(), lock_depth,inc,lock_owner); if (!THELOCK) { lock_depth -= inc; if (lock_debug) printf("%d(%d): unlocked %d\n", get_pid(), getpid(), lock_depth); return; /* no sprocs occured yet */ } assert(lock_owner == get_pid()); assert(lock_depth > 0); if ((lock_depth -= inc) == 0) { lock_owner = 0; usunsetlock(THELOCK); } if (lock_debug) printf("%d(%d): UNLOCKED %d\n", get_pid(), getpid(), lock_depth); } #define CREATE_MALLOC_LOCK() /* not needed, since we use monlock */ #undef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #undef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) #define INRANGE(foo,bar,baz) ((foo(bar))&&((bar)baz)) #define streq(s,t) (strcmp(s, t) == 0) #if 0 /* screw it, just use the strong symbols */ #ifndef real_malloc #define real_malloc mAlLoC #endif #ifndef real_free #define real_free fReE #endif #ifndef real_realloc #define real_realloc rEaLlOc #endif #ifndef real__execve #define real__execve _eXeCvE #endif #ifndef real_amalloc #define real_amalloc aMaLlOc #endif #ifndef real_afree #define real_afree aFrEe #endif #ifndef real_arealloc #define real_arealloc aReAlLoC #endif #endif /* 0 */ #ifndef real_malloc #define real_malloc _malloc #endif #ifndef real_free #define real_free _free #endif #ifndef real_realloc #define real_realloc _realloc #endif #ifndef real__execve #define real__execve _execve #endif #ifndef real_amalloc #define real_amalloc _amalloc #endif #ifndef real_afree #define real_afree _afree #endif #ifndef real_arealloc #define real_arealloc _arealloc #endif #ifndef real_acreate #define real_acreate _acreate #endif #ifndef real_adelete #define real_adelete _adelete #endif # ifdef __cplusplus extern "C" { # endif extern void *real_malloc(size_t); extern void real_free(void *); extern void *real_realloc(void *, size_t); extern int real__execve(const char *file, char*const*argv, char*const*envp); extern void *real_amalloc(size_t, void *); extern void real_afree(void *, void *); extern void *real_arealloc(void *, size_t, void *); extern void *real_acreate(void *, size_t, int, void *, void *(*)(size_t, void *)); extern void real_adelete(void *); # ifdef __cplusplus }; # endif #define type_MALLOC ((void *)0) #define type_FREE ((void *)1) #ifndef MAX_MALLOCS #define MAX_MALLOCS 10000 /* max number of occurrances of malloc&free */ #endif #ifndef MAX_STACKTRACE_DEPTH #define MAX_STACKTRACE_DEPTH 10 #endif #define MAX_SUM_OF_STACKTRACES (MAX_MALLOCS * MAX_STACKTRACE_DEPTH) /* XXX for now, until we do better bounds checking */ #define SYMBUFSIZ 1024 struct hist { void *type; /* type_MALLOC or type_FREE */ int ncalls, nbytes; int ncalls_freed, nbytes_freed; /* if it's a malloc */ int stacktrace_depth; void **stacktrace; /* pointer into stacktraces buffer */ }; /* * Stuff that gets prepended and appended to each malloc block. * Wow, that's a lot of stuff. */ static struct header { int magic0, magic1; int n; struct hist *hist; struct tailer *tail; #define LINKED_LIST #ifdef LINKED_LIST struct header *prev, *next; /* linked list */ #endif /* LINKED_LIST */ void *ap; /* arena pointer */ int magic2, magic3; } aheader = { 0xdeadbeef, 0xfedcba98, 0,0,0, #ifdef LINKED_LIST 0,0, #endif /* LINKED_LIST */ NULL, 0x3456789a, 0xabbaabba}; static struct tailer { int magic4, magic5; struct header *head; void *real_mem; /* same as head unless memaligned */ int magic6, magic7; } atailer = { 0xcacacaca, 0x89898989, NULL, NULL, 0xface6969, 0x81818181 }; #ifdef LINKED_LIST /* XXX relies on the assumption that the first small block allocated from an arena is always ap + 0x130 */ #define FIRSTHEADER_ADDR(ap) ((ap) ? (struct header **)(((char *)(ap))+0x130) : &firstheader) #define THE_ARENA_LOCK(ap) (*(ulock_t *)(((char *)(ap))+39*sizeof(void*))) #define LOCK_ARENA(ap) ((ap) && THE_ARENA_LOCK(ap) ? ussetlock(THE_ARENA_LOCK(ap)) : 0) #define UNLOCK_ARENA(ap) ((ap) && THE_ARENA_LOCK(ap) ? usunsetlock(THE_ARENA_LOCK(ap)) : 0) static struct header *firstheader = NULL; static void link_header(struct header *hdr, void *ap) { struct header **firstheader_addr = FIRSTHEADER_ADDR(ap); LOCK_ARENA(ap); hdr->next = *firstheader_addr; if (*firstheader_addr) (*firstheader_addr)->prev = hdr; hdr->prev = NULL; *firstheader_addr = hdr; UNLOCK_ARENA(ap); } static void unlink_header(struct header *hdr, void *ap) { LOCK_ARENA(ap); if (hdr->next) hdr->next->prev = hdr->prev; if (hdr->prev) hdr->prev->next = hdr->next; else *FIRSTHEADER_ADDR(ap) = hdr->next; UNLOCK_ARENA(ap); } #define MAXARENAS 100 static void *arenas[MAXARENAS]; static int narenas = 0; #endif /* LINKED_LIST */ static struct header *lowest_head_ever = NULL, *highest_head_ever = NULL; /* big static variables */ static struct hist hists[MAX_MALLOCS]; static int nhists = 0; static void *hists_stacktrace_buffer[MAX_SUM_OF_STACKTRACES]; static int hists_stacktrace_buffer_size = 0; static int ncalls_malloc = 0; static int ncalls_free = 0; static int nbytes_malloced = 0; static int nbytes_freed = 0; static int ncalls_malloc_really = 0; /* Not touched by reset */ static int ncalls_free_really = 0; /* Not touched by reset */ static int nbytes_malloced_really = 0; /* Not touched by reset */ static int nbytes_freed_really = 0; /* Not touched by reset */ static int max_diff_bytes = 0; /* Not touched by reset */ static void *malloc_block_of_interest = NULL; static int malloc_call_of_interest = -1; static int depth_of_trace_of_interest = -1; static void *trace_of_interest[MAX_STACKTRACE_DEPTH]; static int malloc_size_of_interest = -1; static int malloc_trace = 0; /* If set, print every operation */ #define MALLOC_FILL '\001' #define FREE_FILL '\002' static void fdprintf(int fd, char *fmt, ...) { char buf[MAXPATHLEN*2]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); write(fd, buf, strlen(buf)); va_end(args); } /* external parameters user can mess with -- maybe should be mallopt option */ int malloc_stacktrace_get_depth = 0; /* default is don't trace */ int malloc_fillarea = 1; /* fill area by default */ extern void malloc_init_function(); static void _atexit() { int do_info = 0, do_free = 0, do_check = 1; /* note default values */ char *p; if (p = getenv("MALLOC_INFO_ATEXIT")) do_info = (*p ? atoi(p) : 1); if (p = getenv("FREE_ATEXIT")) do_free = (*p ? atoi(p) : 1); if (p = getenv("MALLOC_CHECK_ATEXIT")) do_check = (*p ? atoi(p) : 1); if (do_info) malloc_info(0, -1); #ifdef LINKED_LIST if (do_free) { if (do_free >= 2) { fdprintf(2, "%s(%d): Freeing everything...", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid()); } LOCK_MALLOC(1); while (firstheader) free((void *)(firstheader+1)); UNLOCK_MALLOC(1); if (do_free >= 2) fdprintf(2, "done.\n"); } if (do_check) { if (do_check >= 2) { fdprintf(2, "%s(%d): Checking malloc chain (+%d amalloc arena%s) at exit...", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), narenas, narenas==1 ? "" : "s"); } malloc_check_during("exit"); if (do_check >= 2) fdprintf(2, "done.\n"); } #endif /* LINKED_LIST */ } /* From looking at the libc source: execl is weak symbol for _execl which calls _execv execle is weak symbol for _execle calls _execve execv is weak for _execv which calls _execve execve is weak for _execve So it looks like the simplest way to trap all of them is to redefine _execve. Also, to keep other modules from randomly latching on to the weak symbol execve in libc, we also define execve as a strong symbol here. */ #include /* NOT syscall.h-- it gives wrong value for SYS_execve! */ extern int _execve(const char *file, char *const*argv, char *const*envp) { int do_check = 0; char *p; if (p = getenv("MALLOC_CHECK_ATEXEC")) do_check = (*p ? atoi(p) : 0); #ifdef LINKED_LIST if (do_check) { if (do_check >= 2) { /* * Various levels of verbosity * depending on the value of MALLOC_CHECK_ATEXEC... * >= 1 (default): do the check * >= 2: also print a message with the name of the executable * >= 3: print the args too * >= 4: quote the args */ fdprintf(2, "%s(%d): Checking malloc chain (+%d amalloc arena%s) on execve(\"%s\"", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), narenas, narenas==1 ? "" : "s", file); if (do_check >= 3) { int i; fdprintf(2, ", {"); for (i = 0; argv[i] != NULL; ++i) { fdprintf(2, do_check >= 4 ? "%s\"%s\"" : "%s%s", i==0 ? "" : " ", argv[i]); } fdprintf(2, "}"); } fdprintf(2, ")..."); } malloc_check_during("execve"); if (do_check >= 2) fdprintf(2, "done.\n"); } #endif /* LINKED_LIST */ /* return real__execve(file, argv, envp); */ /* argh... having trouble getting the real one because they don't supply libc.a any more... so fake it by calling syscall instead */ return syscall(SYS_execve, file, argv, envp); } extern int execve(const char *file, char *const*argv, char *const*envp) { return _execve(file, argv, envp); } extern void * acreate(void *addr, size_t len, int flags, void *ushdr, void *(*grow)(size_t, void *)) { void *ap = real_acreate(addr, len, flags, ushdr, grow); struct header **firstheader_addr = real_amalloc(sizeof(struct header *), ap); ulock_t arena_lock = THE_ARENA_LOCK(ap); assert(firstheader_addr == FIRSTHEADER_ADDR(ap)); *firstheader_addr = NULL; arenas[narenas++] = ap; if (getenv("MALLOC_VERBOSE")) /* XXX overhead */ fdprintf(2, "%s(%d): acreate(addr=%#x, len=%#x, flags=%d, ushdr=%#x, grow=%#x) called, returning %#x, firstheader_addr = %#x, arena_lock = %#x\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), addr, len, flags, ushdr, grow, ap, firstheader_addr, arena_lock); return ap; } extern void adelete(void *ap) { fdprintf(2, "%s(%d): adelete(%#x) not implemented\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), ap); } #ifdef LINKED_LIST extern int amalloc_check_during(void *ap, char *during) { int is_bad = 0; struct header *p; LOCK_MALLOC(1); LOCK_ARENA(ap); for (p = *FIRSTHEADER_ADDR(ap); p; p = p->next) if (!amalloc_isgoodblock_during(p+1, ap, during)) is_bad = 1; UNLOCK_ARENA(ap); UNLOCK_MALLOC(1); return is_bad ? -1 : 0; } extern int malloc_check_during(char *during) { int i; int return_value = amalloc_check_during(NULL, during); for (i = 0; i < narenas; ++i) { return_value |= amalloc_check_during(arenas[i], during); } return return_value; } extern int malloc_check() { return malloc_check_during("malloc_check"); } #endif /* LINKED_LIST */ static int malloc_reset_signal = 0; static int malloc_info_signal = 0; static int malloc_check_signal = 0; static int malloc_trace_signal = 0; static void catch(int sig) { if (lock_depth > 0) { fdprintf(2, "%s(%d): In a malloc function already; try again in a moment.\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid()); return; } if (sig == malloc_check_signal) { fdprintf(2, "%s(%d): Checking malloc chain (+%d amalloc arena%s)...", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), narenas, narenas==1 ? "" : "s"); malloc_check(); fdprintf(2, "done.\n"); } if (sig == malloc_info_signal) { malloc_check(); malloc_info(1, -1); /* XXX for now, always print nonleaks too */ } if (sig == malloc_reset_signal) { malloc_check(); malloc_reset(); } if (sig == malloc_trace_signal) { malloc_check(); malloc_trace = !malloc_trace; fdprintf(2, "%s(%d): Setting malloc_trace to %d\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), malloc_trace); } } /* to be called only from _malloc_init() */ static void __malloc_init() { static int called_already = 0; if (!called_already) { char *p; /* try to snarf argv[0] as soon as possible, in case main clobbers it */ (void)stacktrace_get_argv0(); if (p = getenv("MALLOC_FILLAREA")) malloc_fillarea = (*p ? atoi(p) : 1); if (p = getenv("MALLOC_STACKTRACE_GET_DEPTH")) malloc_stacktrace_get_depth = (*p ? atoi(p) : 0); if (p = getenv("MALLOC_RESET_SIGNAL")) malloc_reset_signal = (*p ? atoi(p) : SIGUSR1); if (p = getenv("MALLOC_INFO_SIGNAL")) malloc_info_signal = (*p ? atoi(p) : SIGUSR2); if (p = getenv("MALLOC_CHECK_SIGNAL")) malloc_check_signal = (*p ? atoi(p) : SIGUSR2); if (p = getenv("MALLOC_TRACE_SIGNAL")) malloc_trace_signal = (*p ? atoi(p) : SIGUSR1); if (malloc_reset_signal != 0) sigset(malloc_reset_signal, catch); if (malloc_info_signal != 0) sigset(malloc_info_signal, catch); if (malloc_check_signal != 0) sigset(malloc_check_signal, catch); if (malloc_trace_signal != 0) sigset(malloc_trace_signal, catch); /* XXX it might be useful to make a nice callback interface... */ /* XXX and definitely want a gui */ if (p = getenv("MALLOC_STACKTRACE_OF_INTEREST")) { depth_of_trace_of_interest = sscanf(p, "%x %x %x %x %x %x %x %x %x %x", &trace_of_interest[0], &trace_of_interest[1], &trace_of_interest[2], &trace_of_interest[3], &trace_of_interest[4], &trace_of_interest[5], &trace_of_interest[6], &trace_of_interest[7], &trace_of_interest[8], &trace_of_interest[9]); if (depth_of_trace_of_interest == -1) depth_of_trace_of_interest = 0; /* dumbass sscanf */ } /* * Note: we must have some code in this file that * sets the value of malloc_call_of_interest, * otherwise we will not be able to set it properly in the debugger * (due to compiler optimization or something). */ /* XXX want a gui for this */ if (p = getenv("MALLOC_CALL_OF_INTEREST")) malloc_call_of_interest = (int) strtoul(p, (char **)NULL, 0); if (p = getenv("MALLOC_BLOCK_OF_INTEREST")) malloc_block_of_interest = (void *)strtoul(p, (char **)NULL, 0); if (p = getenv("MALLOC_SIZE_OF_INTEREST")) malloc_size_of_interest = (int)strtoul(p, (char **)NULL, 0); if (p = getenv("MALLOC_TRACE")) malloc_trace = (*p ? (int) strtoul(p, (char **)NULL, 0) : 1); if (p = getenv("MALLOC_PROMPT_ON_STARTUP")) { if (!*p || streq(p, stacktrace_get_argv0())) { int tty = open("/dev/tty", 2); char c; assert(tty >= 0); fdprintf(tty, "%s(%d): hit return to continue", stacktrace_get_argv0(), getpid()); read(tty, &c, 1); close(tty); } } malloc_init_function(); /* call application-defined function */ atexit(_atexit); called_already = 1; /* called_already must be true at this point, so that this block won't get re-entered if the us_ routines call malloc. */ CREATE_MALLOC_LOCK(); } } /* this should be static, but we want to make it visible to the linker so it can be made an init function... */ extern void _malloc_init() { static int called_already = 0; if (!called_already) { char *p; if (p = getenv("MALLOC_STACKTRASH")) { int length = 4096; char val = '\003'; extern void *_stacktrace_get_sp(); char *sp = _stacktrace_get_sp(); if (*p) length = (int) strtoul(p, (char **)NULL, 0); if (p = strchr(p, ',')) val = (char) strtoul(p+1, (char **)NULL, 0); while (length-- >= 0) *--sp = val; } /* * Could just put the body of __malloc_init here, * but we want to make this function as simple as possible * so that sp will be as high as possible... */ called_already = 1; __malloc_init(); } } #define MALLOC_STACKTRACE_GET_DEPTH (malloc_stacktrace_get_depth == -1 ? MAX_STACKTRACE_DEPTH : MIN(malloc_stacktrace_get_depth, MAX_STACKTRACE_DEPTH)) /* only compares up to the min of the two sizes */ static int stacktracecmp(int siz0, void *trace0[], int siz1, void *trace1[]) { int i; for (i = 0; i < siz0 && i < siz1; ++i) if (((unsigned long *)trace0)[i] != ((unsigned long *)trace1)[i]) { if (((unsigned long *)trace0)[i] < ((unsigned long *)trace1)[i]) return -1; else return 1; } return siz0 - siz1; } static int stacktracencmp(int siz0, void *trace0[], int siz1, void *trace1[], int n) { int i; for (i = 0; i < siz0 && i < siz1 && i < n; ++i) if (((unsigned long *)trace0)[i] != ((unsigned long *)trace1)[i]) { if (((unsigned long *)trace0)[i] < ((unsigned long *)trace1)[i]) return -1; else return 1; } if (i == n) return 0; return siz0 - siz1; } /* simple prime testing-- we only need it to initialize hashsiz */ static int isprime(int n) { int i; if (n % 2 == 0) return 0; for (i = 3; i*i <= n; ++i) if (n % i == 0) return 0; return 1; } static int hashfun(int depth, void *trace[], int hashsiz) { int i; /* not unsigned! or (i < depth) will be true when i==0 and depth<0 */ unsigned long sum = 0; for (i = 0; i < depth; ++i) sum += (((unsigned long)trace[i])/4); return sum % (hashsiz-1) + 1; /* in range 1..hashsiz-1 */ } #ifdef HASH_STATS static int n_collisions = 0; static int n_distinct_collisions = 0; static int n_pileups; static int n_distinct_pileups; static int max_pileup = 0; extern void _malloc_history_print_stats() { printf("%d max pileup\n", max_pileup); printf("%d distinct pileups\n", n_distinct_pileups); printf("%d total pileups\n", n_pileups); printf("%d distinct collisions\n", n_distinct_collisions); printf("%d total collisions\n", n_collisions); } #endif static struct hist * find_existing_hist(void *type, int stacktrace_depth, void *stacktrace[]) { #ifndef HASHSIZ #define HASHSIZ (MAX_MALLOCS * 3/2) #endif static struct hist *hashtable[HASHSIZ]; static int hashsiz = 0; int i, hash; #ifdef HASH_STATS int pileup = 0; #endif /* * Make the size of the hash table prime so that * for every hash, 0 < hash < hashsiz, the sequence * hash, 2*hash, 3*hash, ... (mod hashsiz) * is a unique path through 1..hashsize-1 (this allows a simple * rehashing mechanism that avoids pileups). * Note that 0 is not allowed-- make sure hashfun() can not return 0! * (hashtable[0] is not used, but subtracting 1 from everything is * not worth the trouble). */ if (!hashsiz) /* then hashsiz hasn't been initialized yet */ for (hashsiz = HASHSIZ; !isprime(hashsiz); hashsiz--) ; hash = hashfun(stacktrace_depth, stacktrace, hashsiz); for (i = hash; hashtable[i]; #ifdef HASH_STUPID i++ #else i = (i+hash) % hashsiz #endif ) { if (type == hashtable[i]->type && !stacktracecmp(stacktrace_depth, stacktrace, hashtable[i]->stacktrace_depth, hashtable[i]->stacktrace)) { return hashtable[i]; } #ifdef HASH_STATS pileup++; n_collisions++; n_pileups += (pileup == 1); #endif } if (nhists == MAX_MALLOCS) return NULL; #ifdef HASH_STATS n_distinct_collisions += pileup; n_distinct_pileups += (pileup > 0); max_pileup = MAX(pileup, max_pileup); #endif hashtable[i] = &hists[nhists]; return hashtable[i]; } /* just places for the debugger to stop... */ extern void malloc_of_interest() { printf(""); /* make sure this function doesn't get optimized out */ } static struct hist * findhist(void *type, int stacktrace_depth, void *stacktrace[]) { int i; struct hist *h; h = find_existing_hist(type, stacktrace_depth, stacktrace); if (h == NULL) { static int already_complained = 0; if (!already_complained) { fdprintf(2, "Turning off malloc tracing: more than %d distinct mallocs & frees\n", MAX_MALLOCS); already_complained = 1; } return NULL; } if (depth_of_trace_of_interest >= 0 && !stacktracecmp(stacktrace_depth, stacktrace, depth_of_trace_of_interest, trace_of_interest)) { malloc_of_interest(); /* make each one separate XXXthis should go with other bounds checking*/ if (nhists < MAX_MALLOCS) { h = hists+nhists; } } if (h-hists == nhists) { /* then it wasn't really existing */ nhists++; h->type = type; h->ncalls = 0; h->nbytes = 0; h->ncalls_freed = 0; h->nbytes_freed = 0; /* XXX need to do some bounds checking here */ h->stacktrace_depth = MIN(stacktrace_depth, MAX_STACKTRACE_DEPTH); h->stacktrace = hists_stacktrace_buffer + hists_stacktrace_buffer_size; hists_stacktrace_buffer_size += h->stacktrace_depth; for (i = 0; i < h->stacktrace_depth; ++i) h->stacktrace[i] = stacktrace[i]; } return h; } /* * Stuff to do on each malloc or realloc */ static void malloc_do_stuff(size_t n, struct header *head, void *ap) { struct hist *h; int stacktrace_depth; void *stacktrace[MAX_STACKTRACE_DEPTH]; if (malloc_block_of_interest == (void *)(head+1)) malloc_of_interest(); if (malloc_call_of_interest == ncalls_malloc) malloc_of_interest(); if (malloc_size_of_interest == n) malloc_of_interest(); if (!lowest_head_ever || head < lowest_head_ever) lowest_head_ever = head; if (!highest_head_ever || head > highest_head_ever) highest_head_ever = head; ncalls_malloc++; ncalls_malloc_really++; nbytes_malloced += n; nbytes_malloced_really += n; if (nbytes_malloced_really - nbytes_freed_really > max_diff_bytes) max_diff_bytes = nbytes_malloced_really - nbytes_freed_really; /* don't get any traces in recursive mallocs */ if (lock_depth >= 200) stacktrace_depth = 0; else stacktrace_depth = stacktrace_get(2, MALLOC_STACKTRACE_GET_DEPTH, stacktrace); h = findhist(type_MALLOC, stacktrace_depth, stacktrace); if (h) { h->ncalls++; h->nbytes += n; } head->n = n; head->hist = h; head->ap = ap; #ifdef LINKED_LIST link_header(head, ap); #endif } static int strcontains(char *S, char *s) /* XXX remove this when no longer used */ { if (!S || !s) return 0; for (; *S; S++) if (!strncmp(S, s, strlen(s))) return 1; return 0; } /* Are we suppressing messages about this block because of an environment variable? */ static int suppressing(void *block) { char *p = getenv("MALLOC_SUPPRESS"); if (!p) return 0; while (p && *p) { char argv0_to_suppress[4096]; long block_to_suppress = 0; while (*p && isspace(*p)) p++; if (sscanf(p, "%[^:]:%lx", argv0_to_suppress, &block_to_suppress) != 2){ argv0_to_suppress[0] = '\0'; if (sscanf(p, "%lx", &block_to_suppress) != 1) break; } if (streq(argv0_to_suppress, stacktrace_get_argv0()) && block_to_suppress == (long)block) return 1; /* found it; suppress it */ while (*p && !isspace(*p)) p++; while (*p && isspace(*p)) p++; } return 0; /* not found in suppress list; don't suppress */ } extern void /* can stop here for breakpoint debugging */ malloc_bad(void *block) { assert(!"nsd bad malloc/free assert. Notify mende@piecomputer.engr.sgi.com"); assert(!getenv("MALLOC_BAD_ASSERT")); } extern int amalloc_isgoodblock_during(void *block, void *ap, char *during)/* XXX think of a better name */ { int its_bad = 0; struct header *head = ((struct header *)block) - 1; struct tailer *tail; int nwrong_in_head, nwrong_in_tail; char *p; /* * Sanity check so we don't buserror ourself */ if (((long)head & 3L) || (ap == NULL && (head < lowest_head_ever || head > highest_head_ever))) { if (!suppressing(head+1)) fdprintf(2, "%s(%d): ERROR: %#x is not a valid malloced block,\n\tdetected during %s\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), head+1, during); malloc_bad(block); return 0; /* failure */ } /* * Heavy duty bounds checking */ nwrong_in_head = (head->magic0 != aheader.magic0) + (head->magic1 != aheader.magic1) + (head->magic2 != aheader.magic2) + (head->magic3 != aheader.magic3); if (nwrong_in_head > 1) { if (!suppressing(head+1)) fdprintf(2, "%s(%d): ERROR: %#x is not a valid malloced block (or more than 4 bytes of underflow corruption),\n\tdetected during %s\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), head+1, during); malloc_bad(block); return 0; /* failure */ } if (nwrong_in_head > 0) { if (!suppressing(head+1)) fdprintf(2, "%s(%d): ERROR: underflow corruption detected during %s\n\tat malloc block %#x\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), during, head+1); its_bad = 1; } tail = head->tail; nwrong_in_tail = (tail->magic4 != atailer.magic4) + (tail->magic5 != atailer.magic5) + (tail->head != head) + (tail->magic6 != atailer.magic6) + (tail->magic7 != atailer.magic7); /* check that the < 4 bytes of extra alignment space are still filled with MALLOC_FILL */ for (p = (char *)(head+1) + head->n; p < (char *)tail; ++p) if (*p != MALLOC_FILL) nwrong_in_tail++; if (nwrong_in_tail > 0) { if (!suppressing(head+1)) fdprintf(2, "%s(%d): ERROR: overflow corruption detected during %s\n\tat malloc block %#x (%s%d byte%s)\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), during, head+1, nwrong_in_head ? "maybe " : "", head->n, head->n==1 ? "" : "s"); its_bad = 1; } if (nwrong_in_head == 0 && head->ap != ap) { if (!suppressing(head+1)) fdprintf(2, "%s(%d): ERROR: malloc block %#x (%d byte%s) is from arena %#x, not %#x,\n\tdetected during %s\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), head+1, head->n, head->n==1 ? "" : "s", head->ap, ap, during); its_bad = 1; } if (!nwrong_in_head && !nwrong_in_tail && ((long)head->hist & 1)) { /* * When we free the block, we set the loworder bit of hist. * Of course the real free and malloc may clobber it, * but if that happened, the above never would have passed. */ if (!suppressing(head+1)) fdprintf(2, "%s(%d): ERROR: %#x (%d byte%s) has been freed already,\n\tdetected during %s\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), head+1, head->n, head->n==1 ? "" : "s", during); its_bad = 1; } if (its_bad) { char buf[100]; /* snarf the stacktrace away; since the block has been freed already, our printing may clobber it */ int stacktrace_depth; void *stacktrace[MAX_STACKTRACE_DEPTH]; stacktrace_depth = ((struct hist *)((long)head->hist&~1))->stacktrace_depth; bcopy(((struct hist *)((long)head->hist&~1))->stacktrace, stacktrace, stacktrace_depth * sizeof(*stacktrace)); malloc_bad(block); /* XXX Quick semi-accurate attempt to see whether we are in a DSO--, since we will probably core dump if we are and the main program is stripped and we try to get a stack trace. */ if (!suppressing(head+1) && (strcontains(getenv("_RLD_LIST"), "libdmalloc") && getenv("_MALLOC_TRY_TO_PRINT_STACKTRACES") ||!strcontains(getenv("_RLD_LIST"), "libdmalloc") && !getenv("_MALLOC_DONT_TRY_TO_PRINT_STACKTRACES"))) { simple_stacktrace_print(/*fd*/2, NULL, /*skip*/3, 100); } if (!suppressing(head+1) && (strcontains(getenv("_RLD_LIST"), "libdmalloc") && getenv("_MALLOC_TRY_TO_PRINT_STACKTRACES") ||!strcontains(getenv("_RLD_LIST"), "libdmalloc") && !getenv("_MALLOC_DONT_TRY_TO_PRINT_STACKTRACES"))) { fdprintf(2, "This block may have been allocated here:\n"); simple_stacktrace_write(/*fd*/2, /*fmt*/NULL, /*filename*/NULL, stacktrace_depth, stacktrace); } return 0; /* failure */ } return 1; /* success */ } extern int malloc_isgoodblock_during(void *block, char *during)/* XXX think of a better name */ { return amalloc_isgoodblock_during(block, NULL, during); } extern int amalloc_isgoodblock(void *block, void *ap) /* XXX think of a better name */ { return amalloc_isgoodblock_during(block, ap, "amalloc_isgoodblock"); } extern int malloc_isgoodblock(void *block) /* XXX think of a better name */ { return malloc_isgoodblock_during(block, "malloc_isgoodblock"); } /* * Stuff to do on each free or realloc. * Return 1 on success, 0 if corruption was detected. */ static int free_do_stuff(struct header *head, void *ap, char *during) { struct hist *h; int stacktrace_depth; void *stacktrace[MAX_STACKTRACE_DEPTH]; if (!amalloc_isgoodblock_during(head+1, ap, during)) return 0; /* failure */ ncalls_free++; ncalls_free_really++; nbytes_freed += head->n; nbytes_freed_really += head->n; if (lock_depth >= 200) stacktrace_depth = 0; else stacktrace_depth = stacktrace_get(2, MALLOC_STACKTRACE_GET_DEPTH, stacktrace); h = findhist(type_FREE, stacktrace_depth, stacktrace); if (h) { h->ncalls++; h->nbytes += head->n; } if (head->hist) { head->hist->ncalls_freed++; head->hist->nbytes_freed += head->n; } #ifdef LINKED_LIST unlink_header(head, ap); #endif return 1; /* success */ } /* Alignment necessary for our head or tail structure is 4. (This is not the same as the alignment we have to return from malloc). */ #define ALIGNUP(n) (((n)+3)&~3) extern void malloc_failed() { /* this function exists for breakpoint debugging. */ printf(""); /* make sure this function doesn't get optimized out */ /* XXX maybe should allow a callback here? */ } /* common code for malloc and amalloc... */ static void * a_malloc(size_t n, void *ap, size_t alignment) { void *real_mem; struct header *head; assert((alignment&(alignment-1))==0); alignment = MAX(alignment, 8); _malloc_init(); LOCK_MALLOC(100); head = 0; /* XXX suppress stupid compiler warning */ if (ap != NULL) real_mem = (struct header *) real_amalloc(ALIGNUP(n + sizeof(*head)) + sizeof(struct tailer) + alignment-8, ap); else real_mem = (struct header *) real_malloc(ALIGNUP(n + sizeof(*head)) + sizeof(struct tailer) + alignment-8); if (!real_mem) { /* XXX wrong message when it's only malloc! */ fdprintf(2, "%s(%d): amalloc(%d, 0x%p) returning NULL\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), (int)n, ap); malloc_failed(); malloc_check(); /* maybe it was corruption that caused it */ UNLOCK_MALLOC(100); return NULL; } head = (struct header *)((char *)((unsigned long)(((char *)real_mem+sizeof(*head)) + alignment-1) &~ (unsigned long)(alignment-1)) - sizeof(*head)); *head = aheader; head->tail = (struct tailer *)(((char *)head) + ALIGNUP(n + sizeof(*head))); *head->tail = atailer; head->tail->head = head; head->tail->real_mem = real_mem; malloc_do_stuff(n, head, ap); if (malloc_fillarea) memset((void *)(head+1), MALLOC_FILL, n); /* always fill any space after the buffer and before the tail, for overflow detection */ memset((void *)((char *)(head+1)+n), MALLOC_FILL, (char *)head->tail - ((char *)(head+1)+n)); UNLOCK_MALLOC(100); return (void *)(head+1); } /* * Front end to the real amalloc */ extern void * amalloc(size_t n, void *ap) { void *ret = a_malloc(n, ap, 1); if (malloc_trace) fdprintf(2, "%s(%d): amalloc(%d, 0x%p) returning 0x%p\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), n, ap, ret); return ret; } /* * Front end to the real malloc */ extern void * malloc(size_t n) { void *ret = a_malloc(n, NULL, 1); if (malloc_trace) fdprintf(2, "%s(%d): malloc(%d) returning 0x%p\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), n, ret); return ret; } /* common code for free and afree... */ static void a_free(void *p, void *ap) { struct header *vom; _malloc_init(); LOCK_MALLOC(100); if (!p) { UNLOCK_MALLOC(100); return; /* XXX do we want to save this information? */ } vom = ((struct header *)p) - 1; if (!free_do_stuff(vom, ap, ap ? "afree" : "free")) { UNLOCK_MALLOC(100); return; /* free_do_stuff prints its own error message */ } if (malloc_fillarea) memset((void *)(vom+1), FREE_FILL, vom->n); vom->hist = (struct hist *)((long)vom->hist | 1); if (ap != NULL) real_afree(vom->tail->real_mem, ap); else real_free(vom->tail->real_mem); UNLOCK_MALLOC(100); } /* * Front end to the real afree */ extern void afree(void *p, void *ap) { if (malloc_trace) fdprintf(2, "%s(%d): afree(0x%p, 0x%p) (%d bytes)\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), p, ap, amallocblksize(p, ap)); a_free(p, ap); } /* * Front end to the real free */ extern void free(void *p) { if (malloc_trace) fdprintf(2, "%s(%d): free(0x%p) (%d bytes)\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), p, mallocblksize(p)); a_free(p, NULL); } /* common code for realloc and arealloc... */ static void * a_realloc(void *p, size_t n, void *ap) { struct header *head; void *old_real_mem, *real_mem; int old_n; if (!p) return a_malloc(n, ap, 1); /* this is what the sgi man page says, anyway */ _malloc_init(); LOCK_MALLOC(100); head = ((struct header *)p) - 1; if (!free_do_stuff(head, ap, ap ? "arealloc" : "realloc")) { UNLOCK_MALLOC(100); return NULL; /* free_do_stuff prints its own error message */ } old_n = head->n; old_real_mem = head->tail->real_mem; if (malloc_fillarea && n < old_n) memset((void *)((char *)(head+1) + n), FREE_FILL, old_n - n); if ((void *)head != old_real_mem) { fdprintf(2, "%s(%d): WARNING: (a)realloc(0x%p, %d, 0x%p) (was %d bytes) but original memory was memaligned! Alignment unknown, assuming 8\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), p, (int)n, ap, old_n); } /* XXX will not preserve alignment if original was memaligned, */ /* XXX but will waste as much space... */ if (ap != NULL) real_mem = (struct header *) real_arealloc(old_real_mem, ALIGNUP(n + sizeof(*head)) + sizeof(struct tailer) + ((char *)head - (char *)old_real_mem), ap); else real_mem = (struct header *) real_realloc(old_real_mem, ALIGNUP(n + sizeof(*head)) + sizeof(struct tailer) + ((char *)head - (char *)old_real_mem)); if (!real_mem) { /* XXX wrong message when it's only realloc! */ fdprintf(2, "%s(%d): arealloc(0x%p, %d, 0x%p) (was %d bytes) returning returning NULL\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), p, (int)n, ap, old_n); malloc_failed(); malloc_check(); /* maybe it was corruption that caused it */ UNLOCK_MALLOC(100); return NULL; /* XXX do we want to save this information? */ } head = (struct header *)((char *)real_mem + ((char *)head - (char *)old_real_mem)); head->tail = (struct tailer *)(((char *)head) + ALIGNUP(n+sizeof(*head))); *head->tail = atailer; head->tail->head = head; head->tail->real_mem = head; malloc_do_stuff(n, head, ap); if (malloc_fillarea && n > old_n) memset((void *)((char *)(head+1) + old_n), MALLOC_FILL, n - old_n); /* always fill any space after the buffer and before the tail, for overflow detection */ memset((void *)((char *)(head+1)+n), MALLOC_FILL, (char *)head->tail - ((char *)(head+1)+n)); UNLOCK_MALLOC(100); return (void *)(head + 1); } /* * Front end to the real arealloc */ extern void * arealloc(void *p, size_t n, void *ap) { int oldsiz = p ? amallocblksize(p, ap) : 0; void *ret = a_realloc(p, n, ap); if (malloc_trace) fdprintf(2, "%s(%d): arealloc(0x%p, %d, 0x%p) (was %d bytes) returning 0x%p\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), p, (int)n, ap, oldsiz, ret); return ret; } /* * Front end to the real realloc */ extern void * realloc(void *p, size_t n) { int oldsiz = p ? mallocblksize(p) : 0; void *ret = a_realloc(p, n, NULL); if (malloc_trace) fdprintf(2, "%s(%d): realloc(0x%p, %d) (was %d bytes) returning 0x%p\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), p, (int)n, oldsiz, ret); return ret; } /* * Sorting utilities */ static void multiqsort(char *base, int n, int elsiz, int (*cmps[])(const void *, const void *)) /* cmps is a null-terminated array of comparison functions */ { int i, (*cmp)(const void *, const void *); if (!(cmp = cmps[0])) return; qsort(base, n, elsiz, cmp); if (cmps[1]) for (; n > 0; n -= i, base += i*elsiz) { for (i = 0; i < n; ++i) if ((*cmp)(base, base + i*elsiz)) break; if (i > 1) multiqsort(base, i, elsiz, cmps+1); } } static int cmp_leaks(struct hist **a, struct hist **b) { return ((*b)->nbytes - (*b)->nbytes_freed) - ((*a)->nbytes - (*a)->nbytes_freed); /* highest first */ } static int cmp_ncalls(struct hist **a, struct hist **b) { return (*b)->ncalls - (*a)->ncalls; /* highest first */ } static int cmp_stacktrace(struct hist **a, struct hist **b) { return stacktracecmp((*a)->stacktrace_depth, (*a)->stacktrace, (*b)->stacktrace_depth, (*b)->stacktrace); } #if 0 /* maybe someday we'll do this by filename */ static int cmp_lineno(struct hist *a, struct hist *b) { return (*a)->lineno - (*b)->lineno; } static int cmp_filename(struct hist **a, struct hist **b) { if (!(*a)->file || !(*b)->file) return !(*a)->file - !(*b)->file; /* with filename comes before without filename */ return strcmp((*a)->file, (*b)->file); } static int cmp_diffbytes(struct hist **a, struct hist **b) { return ((*a)->bytes - (*a)->fbytes) - ((*b)->bytes - (*b)->fbytes); } #endif /* 0 */ static int cmp_malloc_then_free(struct hist **a, struct hist **b) { return ((*a)->type == type_FREE) - ((*b)->type == type_FREE); } int (*oldcmps[])(const void *, const void *) = { /* cmp_filename, */ /* cmp_lineno, */ (int (*)(const void *, const void *))cmp_malloc_then_free, (int (*)(const void *, const void *))cmp_stacktrace, NULL }; int (*cmps[])(const void *, const void *) = { /* cmp_filename, */ /* cmp_lineno, */ (int (*)(const void *, const void *))cmp_malloc_then_free, (int (*)(const void *, const void *))cmp_leaks, (int (*)(const void *, const void *))cmp_ncalls, (int (*)(const void *, const void *))cmp_stacktrace, NULL }; extern void malloc_reset() { int i; /* can't set nhists to 0, since existing header points to them */ for (i = 0; i < nhists; ++i) { hists[i].ncalls = 0; hists[i].nbytes = 0; hists[i].ncalls_freed = 0; hists[i].nbytes_freed = 0; } ncalls_malloc = 0; ncalls_free = 0; nbytes_malloced = 0; nbytes_freed = 0; } extern void malloc_info_cleanup() { stacktrace_cleanup(); } /* verbose = 0: just totals verbose = 1: leaks with traces verbose = 2: all counts with traces */ extern void malloc_info(int nonleaks_too, int stacktrace_print_depth) { int orig_nhists; int i; struct hist *ptrs[MAX_MALLOCS], *h; char thisfile[SYMBUFSIZ]; char thisfunc[SYMBUFSIZ]; char avgbuf[30]; int thisline; _malloc_init(); LOCK_MALLOC(1); printf("%s(%d):\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid()); printf("\n"); printf("%6s %10s %10s %10s %10s\n", " ", "Mallocs", "Frees", "Diff", "Max Diff"); printf("-------------------------------------------------------------\n"); printf("%6s %10d %10d %10d %10d\n", "Bytes:", nbytes_malloced_really, nbytes_freed_really, nbytes_malloced_really-nbytes_freed_really, max_diff_bytes); printf("%6s %10d %10d %10d\n", "Calls:", ncalls_malloc_really, ncalls_free_really, ncalls_malloc_really-ncalls_free_really); printf("Since last reset:\n"); printf("%6s %10d %10d %10d\n", "Bytes:", nbytes_malloced, nbytes_freed, nbytes_malloced-nbytes_freed); printf("%6s %10d %10d %10d\n", "Calls:", ncalls_malloc, ncalls_free, ncalls_malloc-ncalls_free); /* if (!verbose) { UNLOCK_MALLOC(1); return; } */ printf("%d out of %d traces used\n", nhists, MAX_MALLOCS); #ifdef HASH_STATS _malloc_history_print_stats(); #endif printf("\n"); /* printf("%15s[%4s]: %3s %6s %9s %7s %9s %7s %9s\n", */ printf("%15s[%4s]: %3s %6s %7s %7s%6s %7s %5s %9s\n", "Filename", "Line", "Wha", "Calls", "Bytes", "Avg ", "FCalls", "FBytes", "Diff", "DiffBytes"); printf("-------------------------------------------------------------------------------\n"); orig_nhists = nhists; /* printing and the first stacktracing call malloc */ for (i = 0; i < orig_nhists; ++i) ptrs[i] = hists+i; if (getenv("_MALLOC_DONT_SORT_BY_NCALLS")) multiqsort((char *)ptrs, orig_nhists,sizeof(struct hist *),oldcmps); else multiqsort((char *)ptrs, orig_nhists, sizeof(struct hist *), cmps); for (i=0; incalls == 0 && (h->type==type_FREE || h->ncalls_freed==0)) continue; if (!nonleaks_too) { if (h->type == type_FREE) continue; if (h->ncalls == h->ncalls_freed && h->nbytes == h->nbytes_freed) continue; } thisfunc[0] = 0; thisfile[0] = 0; thisline = -1; if (h->stacktrace_depth >= 1) { stacktrace_get_ffl(h->stacktrace[0], thisfunc, thisfile, &thisline, SYMBUFSIZ-2, SYMBUFSIZ-2); } /* XXX should keep track of whether all sizes are the same */ if (h->ncalls == 0) sprintf(avgbuf, " "); else if (h->nbytes % h->ncalls != 0) sprintf(avgbuf, "%d.", h->nbytes / h->ncalls); else sprintf(avgbuf, "%d ", h->nbytes / h->ncalls); if (h->type == type_MALLOC) /* printf("%15s[%4d]: %3s %6d %9d %7d %9d %7d %9d\n", */ printf("%15s[%4d]: %3s %6d %7d %7s%6d %7d %5d %9d\n", thisfile, thisline, "mal", h->ncalls, h->nbytes, avgbuf, h->ncalls_freed, h->nbytes_freed, h->ncalls-h->ncalls_freed, h->nbytes-h->nbytes_freed); else printf("%15s[%4d]: %3s %6d %7d %7s\n", thisfile, thisline, "fre", h->ncalls, h->nbytes, avgbuf); /* if (verbose >= 2) */ { int j; for (j = 0; j < h->stacktrace_depth && (j < stacktrace_print_depth || stacktrace_print_depth < 0); j++) { if (!h->stacktrace[j]) { printf("(lost it)\n"); break; } stacktrace_get_ffl(h->stacktrace[j], thisfunc, thisfile, &thisline, SYMBUFSIZ-2, SYMBUFSIZ); /* I don't wanna see the args, so there! */ { char *p = strchr(thisfunc, '('); if (p) *p = '\0'; } if (thisfunc[strlen(thisfunc)-1] != ')') strcat(thisfunc, "()"); printf("%-20s %s:%d (%#x)\n", thisfunc, thisfile, thisline, h->stacktrace[j]); } } } printf("-------------------------------------------------------------------------------\n"); UNLOCK_MALLOC(1); } /* Gag me-- mpc's calloc doesn't call malloc, but its free is free... */ static void * a_calloc(size_t n, size_t siz, void *ap) { void *p; int old_fillarea; _malloc_init(); LOCK_MALLOC(1); old_fillarea = malloc_fillarea; malloc_fillarea = 0; p = amalloc(n*siz, ap); malloc_fillarea = old_fillarea; if (p) bzero(p, n*siz); UNLOCK_MALLOC(1); return p; } extern void * acalloc(size_t n, size_t siz, void *ap) { return a_calloc(n, siz, ap); } extern void * calloc(size_t n, size_t siz) { return a_calloc(n, siz, NULL); } extern void cfree(void *p) { free(p); } /* Smart: 580 out of 600 traces used 13 max pileup 144 distinct pileups 309 total pileups 290 distinct collisions 536 total collisions Stupid: 581 out of 600 traces used 10 max pileup 150 distinct pileups 357 total pileups 281 distinct collisions 626 total collisions With crowded hash table: Smart: 581 out of 600 traces used 17 max pileup 229 distinct pileups 652 total pileups 692 distinct collisions 1596 total collisions Stupid: 580 out of 600 traces used 60 max pileup 227 distinct pileups 542 total pileups 1494 distinct collisions 3273 total collisions */ #if RELEASE_MAJOR < 6 #define BZERO_2ND_ARG int #else #define BZERO_2ND_ARG size_t #endif extern void bzero(void *b, BZERO_2ND_ARG length)/* XXX why is this not getting found by rld? */ { int i; _malloc_init(); for (i = 0; i < length; ++i) ((char *)b)[i] = 0; } /*============================================================================= Implement all of malloc(3C)'s and malloc(3X)'s utility routines... */ extern void * amemalign(size_t alignment, size_t n, void *ap) { void *ret = a_malloc(n, ap, alignment); if (malloc_trace) fdprintf(2, "%s(%d): amemalign(%d, %d, 0x%p) returning 0x%p\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), alignment, n, ap, ret); return ret; } extern void * memalign(size_t alignment, size_t n) { void *ret = a_malloc(n, NULL, alignment); if (malloc_trace) fdprintf(2, "%s(%d): memalign(%d, %d) returning 0x%p\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), alignment, n, ret); return ret; } extern void * valloc(size_t n) { if (getenv("MALLOC_VERBOSE")) /* XXX overhead */ fdprintf(2, "%s(%d): valloc(%d) called\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), n); return memalign(sysconf(_SC_PAGESIZE), n); } static void * a_recalloc(void *p, size_t n, size_t siz, void *ap) { int old_n; if (getenv("MALLOC_VERBOSE")) /* XXX overhead */ fdprintf(2, "%s(%d): %srecalloc(%#x, %d, %d) called\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), ap ? "a" : "", p, n, siz); if (!p) return calloc(n, siz); if (!amalloc_isgoodblock_during(p, ap, ap ? "arecalloc" : "recalloc")) return 0; /* failure */ old_n = (((struct header *)p) - 1)->n; p = arealloc(p, n*siz, ap); if (p && n*siz > old_n) { bzero((char *)p+old_n, n*siz - old_n); } return p; } extern void * arecalloc(void *p, size_t n, size_t siz, void *ap) { return a_recalloc(p, n, siz, ap); } extern void * recalloc(void *p, size_t n, size_t siz) { return a_recalloc(p, n, siz, NULL); } static size_t a_mallocblksize(void *p, void *ap) { char *e; if (!amalloc_isgoodblock_during(p, ap, ap ? "amallocblksize" : "mallocblksize")) return 0; /* failure */ if ((e = getenv("MALLOC_VERBOSE")) != NULL && atoi(e) >= 2) /* XXX overhead */ fdprintf(2, "%s(%d): %smallocblksize(%#x) called, returning %d\n", stacktrace_get_argv0() ? stacktrace_get_argv0() : "", getpid(), ap ? "a" : "", p, (((struct header *)p) - 1)->n); return (((struct header *)p) - 1)->n; } extern size_t amallocblksize(void *p, void *ap) { return a_mallocblksize(p, ap); } extern size_t _amallocblksize(void *p, void *ap) { return a_mallocblksize(p, ap); } extern size_t mallocblksize(void *p) { return a_mallocblksize(p, NULL); } /* XXX redefine the strong symbol too, to combat swmgr's bad behavior (see incident #238834) */ extern size_t _mallocblksize(void *p) { return a_mallocblksize(p, NULL); }