/* Smartalloc.c Copyright Clinton Staley 1991 * modified by ceh 1992 to include calloc()) * modified by bkm 1992 to include realloc() and strdup() * * Rewritten April 10, 1992 by Steve Mead (smead) to use a hash table * instead of a linked list. Also changed for loops to memset's and memcpy's. * Speed improvements are phenomenal. */ #undef MDEBUG #include #ifdef MACOSX #include #endif #ifndef MACOSX #include #endif #include #include #include "salloc.h" #include "table.h" #include "str_table.h" #undef report_offenders #define BOUND 0xC #define FREED 0xB #define BOUND_BYTES 8 #ifndef YES #define YES 1 #endif #ifndef NO #define NO 0 #endif typedef struct track_t { void *data; int space; char *file; int line; } track_t; static table_t table = NULL; static long malloc_count = 0; static long free_count = 0; void _free(void *data) { free(data); } typedef struct line_stats_t { int line; int size; int number; struct line_stats_t *next; } line_stats_t; typedef struct file_stats_t { char *file; line_stats_t *lines; long size; long number; long num_lines; } file_stats_t; int sort_files(void *f1, void *f2) { return ((file_stats_t*)f2)->size - ((file_stats_t*)f1)->size; } int sort_lines(void *c1, void *c2) { return ((line_stats_t*)c2)->size - ((line_stats_t*)c1)->size; } void output_offenders(file_stats_t *stats, int num_stats, long min) { int cnt, line_cnt; line_stats_t *next, *temp; for (cnt=0; cnt < num_stats; stats++, cnt++) { line_stats_t *lines, *l; fprintf(stderr, "File: %s - %ld total bytes in %ld allocations.\n", stats->file, stats->size, stats->number); l = lines = (line_stats_t*)malloc(sizeof(line_stats_t)*stats-> num_lines); for (temp = stats->lines; (next=temp) != NULL; ) { temp = next->next; *l++ = *next; free(next); } qsort(lines, stats->num_lines, sizeof(line_stats_t), sort_lines); for (line_cnt=0; line_cnt < stats->num_lines && (min == -1 || lines[line_cnt].size > min); line_cnt++) fprintf(stderr, "%5ld allocations from line %4ld totalling %8ld " "bytes.\n", lines[line_cnt].number, lines[line_cnt].line, lines[line_cnt].size); free(lines); } } long report_offenders(long min) { track_t *temp; long key, pieces, bytes; iterator_t itr; str_table_t offenders; str_iterator_t str_itr; char *str_key; line_stats_t *line, **find; file_stats_t *stats, *next; file_stats_t *stat_array; if (table == NULL) return; bytes = pieces = 0; itr = create_iterator(table); offenders = create_str_table(); while (next_item(itr, &key, (void **) &temp)) { pieces++; bytes += temp->space; if ((stats = get_str_table_item(offenders, temp->file)) == NULL) { stats = (file_stats_t*)calloc(sizeof(file_stats_t), 1); stats->file = temp->file; add_str_table_item(offenders, temp->file, (void**)stats); } stats->size += temp->space; stats->number++; for (find=&stats->lines; *find != NULL && (*find)->line != temp->line; find = &(*find)->next) ; if (*find == NULL) { *find = (line_stats_t*)calloc(sizeof(line_stats_t), 1); (*find)->line = temp->line; stats->num_lines++; } (*find)->size += temp->space; (*find)->number++; } destroy_iterator(itr); str_itr = create_str_table_iterator(offenders); next = stat_array = (file_stats_t*)malloc(sizeof(file_stats_t)* num_str_table_items(offenders)); while (next_str_table_item(str_itr, &str_key, (void**)&stats)) { *next++ = *stats; free(stats); } destroy_str_table_iterator(str_itr); qsort(stat_array, num_str_table_items(offenders), sizeof(file_stats_t), sort_files); output_offenders(stat_array, num_str_table_items(offenders), min); if (bytes != 0 || pieces != 0) { fprintf(stderr, "------------------------------------------------\n"); fprintf(stderr, "%ld bytes unfreed space in %ld pieces.\n", bytes, pieces); fprintf(stderr, "%ld malloc calls, %ld free calls.\n", malloc_count, free_count); fprintf(stderr, "------------------------------------------------\n"); } free(stat_array); destroy_str_table(offenders); return bytes; } void *smartalloc(unsigned int bytes, char *file, int line, int fill) { track_t *temp; register int i; char *data; /* debug */ assert(bytes > 0); malloc_count++; if ((bytes + 2*BOUND_BYTES) < bytes) { fprintf(stderr, "Malloc failure of %ld bytes in file %s on line %d\n", bytes, file, line); assert(NO); exit(1); } if ((temp = (track_t *) malloc(sizeof(track_t))) == NULL) { fprintf(stderr, "Malloc failure of %ld bytes in file %s on line %d\n", bytes, file, line); assert(NO); exit(1); } if ((temp->data = data = malloc(bytes + 2*BOUND_BYTES)) == NULL) { fprintf(stderr, "Malloc failure of %ld bytes in file %s on line %d\n", bytes, file, line); assert(NO); exit(1); } data += BOUND_BYTES; for (i = 0; i < BOUND_BYTES; i++) { data[bytes + i] = BOUND; data[-1-i] = BOUND; } memset(data, fill, bytes); temp->space = bytes; temp->file = file; temp->line = line; if (table == NULL) table = create_table(); if (add_item(table, ((long) data) >> 1, temp) != YES) { fprintf(stderr, "Attempt to allocate something already allocated in %s at line %d!\n", file, line); fprintf(stderr, "Internal memory heap has been corrupted. Aborting.\n"); assert(NO); exit(1); } return (void *)data; } void chk_tromp(char *file, int line) { track_t *temp; register int i; long key, space; char *data; iterator_t itr; if (table == NULL) return; itr = create_iterator(table); while (next_item(itr, &key, (void **) &temp)) { data = (char *) temp->data + BOUND_BYTES; space = temp->space; for (i = 0; i < BOUND_BYTES; i++) if (data[space + i] != BOUND || data[-1 - i] != BOUND) { fprintf(stderr, "Check_tromp failed from %s at line %d: \n", file, line); fprintf(stderr, "Memory at address 0x%p, allocated from file %s, line %d has been" " written past bounds.\n", data, temp->file, temp->line); break; } } destroy_iterator(itr); } void smartfree(void *data, char *file, int line) { track_t *temp; void *address; register int i; free_count++; address = (char *) data - BOUND_BYTES; if (table == NULL) { fprintf(stderr, "Attempt to free space when none has been malloced.\n" "Attempt made from file %s at line " "%d.\n", file, line); return; } if ((temp = get_item(table, ((long) data) >> 1)) == NULL) { fprintf(stderr, "Attempt to free non-malloced space at address 0x%p.\n" "Attempt made from file %s at line %d.\n", data, file, line); return; } for (i = 0; i < BOUND_BYTES; i++) if (((char *)data)[temp->space + i] != BOUND || ((char *)data)[-1-i] != BOUND) { fprintf(stderr, "Memory at address 0x%p, allocated from file %s, line %d has been" " written past bounds.\n", data, temp->file, temp->line); break; } memset(data, FREED, temp->space); destroy_item(table, ((long) data) >> 1); free(address); free(temp); } void *smartrealloc(void *old, unsigned int bytes, char *file, int line, int fill) { void *data; track_t *old_track; if ((old_track = get_item(table, ((long) old) >> 1)) == NULL) { fprintf(stderr, "Attempt to realloc something not allocated!\n"); fprintf(stderr, "Realloc failure of %ld bytes in file %s at line %d.\n", bytes, file, line); assert(NO); exit(1); } data = smartalloc(bytes, file, line, fill); memcpy(data, old, bytes < old_track->space ? bytes : old_track->space); smartfree(old, file, line); return data; } char *smartdup(const char *oldstr, char *file, int line, int fill) { void *data; unsigned int bytes; bytes = strlen((char *) oldstr) + 1; data = smartalloc(bytes, file, line, fill); memcpy(data, oldstr, bytes); return data; }