/* $Id: trace.c,v 4.3 2004/03/24 23:43:42 tom Exp $ * * Simple malloc debugging (for finding leaks) * * This is a cut-down version of a module I wrote originally for 'vile', it * requires an ANSI compiler. Its main purpose is to allow tracing problems in * a repeatable test, including malloc/free bugs -- T.Dickey */ #if HAVE_CONFIG_H #include #endif #include /* interface of this module */ #if DOALLOC #undef malloc #undef realloc #undef free #endif /* DOALLOC */ #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include #define BEFORE 0 /* padding "before" allocated area */ #define AFTER 0 /* padding "after" allocated area */ #define BEFORE_PTR(ptr) (void *)((char *)(ptr) - BEFORE) void Where(char *file, int line) { fflush(stderr); printf("%s @%d\n", file, line); fflush(stdout); Trace("%s @%d\n", file, line); } void Trace(char *format, ...) { static FILE *fp; va_list ap; if (!fp) fp = fopen("Trace.out", "w"); if (!fp) abort(); if (format != 0) { va_start(ap,format); vfprintf(fp, format, ap); va_end(ap); (void)fflush(fp); } else { (void)fclose(fp); (void)fflush(stdout); (void)fflush(stderr); } } #define SECS(tv) (tv.tv_sec + (tv.tv_usec / 1.0e6)) void Elapsed(char *msg) { #if HAVE_GETTIMEOFDAY static struct timeval tv0, tv1; static struct timezone tz0, tz1; static int init; if (!init++) gettimeofday(&tv0, &tz0); gettimeofday(&tv1, &tz1); Trace("%10.6f %s\n", SECS(tv1) - SECS(tv0), msg); tv0 = tv1; #endif } #ifdef apollo static int contains(char *ref, char *tst) { size_t len = strlen(ref); while (*tst) { if (!strncmp(ref,tst++,len)) return TRUE; } return FALSE; } #endif /* apollo */ void WalkBack(void) { #ifdef apollo static char *first = "\"WalkBack\"", *last = "\"unix_$main\""; auto FILE *pp; auto char bfr[BUFSIZ]; auto int ok = FALSE; static int count; Trace("%s %d\n", first, ++count); sprintf(bfr, "/com/tb %d", getpid()); if (!(pp = popen(bfr, "r"))) perror(bfr); while (fgets(bfr, sizeof(bfr), pp)) { if (ok && contains(last, bfr)) break; else if (contains(first, bfr)) ok = TRUE; else if (ok) Trace("%s", bfr); } (void)fclose(pp); #endif /* apollo */ } static long count_alloc, count_freed; void fail_alloc(char *msg, char *ptr) { Trace("%s: %p\n", msg, ptr); Trace("allocs %ld, frees %ld\n", count_alloc, count_freed); WalkBack(); #ifdef NO_LEAKS show_alloc(); #endif Trace((char *)0); abort(); } #if DOALLOC #undef malloc #undef realloc #undef free typedef struct { long size; /* ...its size */ char *text; /* the actual segment */ int note; /* ...last value of 'count_alloc' */ } AREA; static AREA area[DOALLOC]; static long maxAllocated, /* maximum # of bytes allocated */ nowAllocated, /* current # of bytes allocated */ nowPending, /* current end of 'area[]' table */ maxPending; /* maximum # of segments allocated */ static int FindArea(char *ptr) { register int j; for (j = 0; j < DOALLOC; j++) if (area[j].text == ptr) { if (j >= nowPending) { nowPending = j+1; if (nowPending > maxPending) maxPending = nowPending; } return j; } return -1; } static int record_freed(char *ptr) { register int j; if ((j = FindArea(ptr)) >= 0) { nowAllocated -= area[j].size; /*memset(ptr, 0xdf, area[j].size); -* poison */ area[j].size = 0; area[j].text = 0; area[j].note = count_freed; if ((j+1) == nowPending) { register int k; for (k = j; (k >= 0) && !area[k].size; k--) nowPending = k; } } return j; } static int record_alloc(char *newp, char *oldp, unsigned len) { register int j; if (newp == oldp) { if ((j = FindArea(oldp)) >= 0) { nowAllocated -= area[j].size; area[j].size = len; area[j].note = count_alloc; } else fail_alloc("could not find", oldp); } else { if (oldp != 0) record_freed(oldp); if ((j = FindArea((char *)0)) >= 0) { area[j].text = newp; area[j].size = len; area[j].note = count_alloc; } else fail_alloc("no room in table", newp); } nowAllocated += len; if (nowAllocated > maxAllocated) maxAllocated = nowAllocated; return len; } #define OK_ALLOC(p,q,n) ((p != 0) && (record_alloc(p,q,n) >= 0)) #define OK_FREE(p) ((p != 0) && (record_freed(p) >= 0)) #else #define OK_ALLOC(p,q,n) (p != 0) #define OK_FREE(p) (p != 0) #endif /* DOALLOC */ #ifdef DEBUG2 #define LOG_PTR(msg,num) Trace("%s %p\n", msg, num) #define LOG_LEN(msg,num) Trace("%s %d\n", msg, num) #else #define LOG_PTR(msg,num) #define LOG_LEN(msg,num) #endif /************************************************************************ * public entrypoints * ************************************************************************/ #if DOALLOC void * doalloc (void *oldp, unsigned amount) { char *newp; if (oldp != 0) oldp = BEFORE_PTR(oldp); count_alloc += (oldp == 0); #if 0 if ((count_alloc > 99914 && count_alloc < 99920)) { Trace("doalloc #%d\n", count_alloc); WalkBack(); } #endif LOG_LEN("allocate", amount); LOG_PTR(" old = ", oldp); amount += (BEFORE+AFTER); /* patch */ newp = (oldp != 0) ? realloc(oldp, amount) : malloc(amount); if (!OK_ALLOC(newp,oldp,amount)) { perror("doalloc"); fail_alloc("doalloc", oldp); /*NOT REACHED*/ } LOG_PTR(" new = ", newp); return (void *)(newp+BEFORE); } /* * Entrypoint so we can validate pointers */ void dofree(void *oldp) { oldp = BEFORE_PTR(oldp); count_freed++; LOG_PTR("dealloc ", oldp); if (OK_FREE(oldp)) { free(oldp); return; } fail_alloc("free (not found)", oldp); } #endif void show_alloc(void) { #if DOALLOC static char *format = ".. %-24.24s %10ld\n"; Trace("** allocator metrics:\n"); Trace(format, "allocs:", count_alloc); Trace(format, "frees:", count_freed); { register int j, count = 0; register long total = 0; for (j = 0; j < nowPending; j++) { if (area[j].text) { if (count++ < 10) Trace("...%d) %ld bytes in alloc #%d:%p\n", j, area[j].size, area[j].note, area[j].text); total += area[j].size; } } Trace(format, "total bytes allocated:", total); Trace(format, "current bytes allocated:", nowAllocated); Trace(format, "maximum bytes allocated:", maxAllocated); Trace(format, "segment-table length:", nowPending); Trace(format, "current # of segments:", (long)count); Trace(format, "maximum # of segments:", maxPending); } #endif }