1226035Sgabor/* $FreeBSD$ */ 2226035Sgabor 3226035Sgabor/* 4226035Sgabor xmalloc.c - Simple malloc debugging library implementation 5226035Sgabor 6226035Sgabor This software is released under a BSD-style license. 7226035Sgabor See the file LICENSE for details and copyright. 8226035Sgabor 9226035Sgabor*/ 10226035Sgabor 11226035Sgabor/* 12226035Sgabor TODO: 13226035Sgabor - red zones 14226035Sgabor - group dumps by source location 15226035Sgabor*/ 16226035Sgabor 17226035Sgabor#include <stdlib.h> 18226035Sgabor#include <assert.h> 19226035Sgabor#include <stdio.h> 20226035Sgabor#define XMALLOC_INTERNAL 1 21226035Sgabor#include "xmalloc.h" 22226035Sgabor 23226035Sgabor 24226035Sgabor/* 25226035Sgabor Internal stuff. 26226035Sgabor*/ 27226035Sgabor 28226035Sgabortypedef struct hashTableItemRec { 29226035Sgabor void *ptr; 30226035Sgabor int bytes; 31226035Sgabor const char *file; 32226035Sgabor int line; 33226035Sgabor const char *func; 34226035Sgabor struct hashTableItemRec *next; 35226035Sgabor} hashTableItem; 36226035Sgabor 37226035Sgabortypedef struct { 38226035Sgabor hashTableItem **table; 39226035Sgabor} hashTable; 40226035Sgabor 41226035Sgaborstatic int xmalloc_peak; 42241737Sedstatic int xmalloc_current; 43226035Sgaborstatic int xmalloc_peak_blocks; 44241737Sedstatic int xmalloc_current_blocks; 45226035Sgaborstatic int xmalloc_fail_after; 46226035Sgabor 47226035Sgabor#define TABLE_BITS 8 48226035Sgabor#define TABLE_MASK ((1 << TABLE_BITS) - 1) 49226035Sgabor#define TABLE_SIZE (1 << TABLE_BITS) 50226035Sgabor 51226035Sgaborstatic hashTable * 52226035Sgaborhash_table_new(void) 53226035Sgabor{ 54226035Sgabor hashTable *tbl; 55226035Sgabor 56226035Sgabor tbl = malloc(sizeof(*tbl)); 57226035Sgabor 58226035Sgabor if (tbl != NULL) 59226035Sgabor { 60226035Sgabor tbl->table = calloc(TABLE_SIZE, sizeof(*tbl->table)); 61226035Sgabor 62226035Sgabor if (tbl->table == NULL) 63226035Sgabor { 64226035Sgabor free(tbl); 65226035Sgabor return NULL; 66226035Sgabor } 67226035Sgabor } 68226035Sgabor 69226035Sgabor return tbl; 70226035Sgabor} 71226035Sgabor 72226035Sgaborstatic int 73226035Sgaborhash_void_ptr(void *ptr) 74226035Sgabor{ 75226035Sgabor int hash; 76226035Sgabor int i; 77226035Sgabor 78226035Sgabor /* I took this hash function just off the top of my head, I have 79226035Sgabor no idea whether it is bad or very bad. */ 80226035Sgabor hash = 0; 81226035Sgabor for (i = 0; i < (int)sizeof(ptr)*8 / TABLE_BITS; i++) 82226035Sgabor { 83226035Sgabor hash ^= (unsigned long)ptr >> i*8; 84226035Sgabor hash += i * 17; 85226035Sgabor hash &= TABLE_MASK; 86226035Sgabor } 87226035Sgabor return hash; 88226035Sgabor} 89226035Sgabor 90226035Sgaborstatic void 91226035Sgaborhash_table_add(hashTable *tbl, void *ptr, int bytes, 92226035Sgabor const char *file, int line, const char *func) 93226035Sgabor{ 94226035Sgabor int i; 95226035Sgabor hashTableItem *item, *new; 96226035Sgabor 97226035Sgabor i = hash_void_ptr(ptr); 98226035Sgabor 99226035Sgabor item = tbl->table[i]; 100226035Sgabor if (item != NULL) 101226035Sgabor while (item->next != NULL) 102226035Sgabor item = item->next; 103226035Sgabor 104226035Sgabor new = malloc(sizeof(*new)); 105226035Sgabor assert(new != NULL); 106226035Sgabor new->ptr = ptr; 107226035Sgabor new->bytes = bytes; 108226035Sgabor new->file = file; 109226035Sgabor new->line = line; 110226035Sgabor new->func = func; 111226035Sgabor new->next = NULL; 112226035Sgabor if (item != NULL) 113226035Sgabor item->next = new; 114226035Sgabor else 115226035Sgabor tbl->table[i] = new; 116226035Sgabor 117226035Sgabor xmalloc_current += bytes; 118226035Sgabor if (xmalloc_current > xmalloc_peak) 119226035Sgabor xmalloc_peak = xmalloc_current; 120226035Sgabor xmalloc_current_blocks++; 121226035Sgabor if (xmalloc_current_blocks > xmalloc_peak_blocks) 122226035Sgabor xmalloc_peak_blocks = xmalloc_current_blocks; 123226035Sgabor} 124226035Sgabor 125226035Sgaborstatic void 126226035Sgaborhash_table_del(hashTable *tbl, void *ptr) 127226035Sgabor{ 128226035Sgabor int i; 129226035Sgabor hashTableItem *item, *prev; 130226035Sgabor 131226035Sgabor i = hash_void_ptr(ptr); 132226035Sgabor 133226035Sgabor item = tbl->table[i]; 134226035Sgabor if (item == NULL) 135226035Sgabor { 136226035Sgabor printf("xfree: invalid ptr %p\n", ptr); 137226035Sgabor abort(); 138226035Sgabor } 139226035Sgabor prev = NULL; 140226035Sgabor while (item->ptr != ptr) 141226035Sgabor { 142226035Sgabor prev = item; 143226035Sgabor item = item->next; 144226035Sgabor } 145226035Sgabor if (item->ptr != ptr) 146226035Sgabor { 147226035Sgabor printf("xfree: invalid ptr %p\n", ptr); 148226035Sgabor abort(); 149226035Sgabor } 150226035Sgabor 151226035Sgabor xmalloc_current -= item->bytes; 152226035Sgabor xmalloc_current_blocks--; 153226035Sgabor 154226035Sgabor if (prev != NULL) 155226035Sgabor { 156226035Sgabor prev->next = item->next; 157226035Sgabor free(item); 158226035Sgabor } 159226035Sgabor else 160226035Sgabor { 161226035Sgabor tbl->table[i] = item->next; 162226035Sgabor free(item); 163226035Sgabor } 164226035Sgabor} 165226035Sgabor 166226035Sgaborstatic hashTable *xmalloc_table = NULL; 167226035Sgabor 168226035Sgaborstatic void 169226035Sgaborxmalloc_init(void) 170226035Sgabor{ 171226035Sgabor if (xmalloc_table == NULL) 172226035Sgabor { 173226035Sgabor xmalloc_table = hash_table_new(); 174226035Sgabor xmalloc_peak = 0; 175226035Sgabor xmalloc_peak_blocks = 0; 176226035Sgabor xmalloc_current = 0; 177226035Sgabor xmalloc_current_blocks = 0; 178226035Sgabor xmalloc_fail_after = -1; 179226035Sgabor } 180226035Sgabor assert(xmalloc_table != NULL); 181226035Sgabor assert(xmalloc_table->table != NULL); 182226035Sgabor} 183226035Sgabor 184226035Sgabor 185226035Sgabor 186226035Sgabor/* 187226035Sgabor Public API. 188226035Sgabor*/ 189226035Sgabor 190226035Sgaborvoid 191226035Sgaborxmalloc_configure(int fail_after) 192226035Sgabor{ 193226035Sgabor xmalloc_init(); 194226035Sgabor xmalloc_fail_after = fail_after; 195226035Sgabor} 196226035Sgabor 197226035Sgaborint 198226035Sgaborxmalloc_dump_leaks(void) 199226035Sgabor{ 200226035Sgabor int i; 201226035Sgabor int num_leaks = 0; 202226035Sgabor int leaked_bytes = 0; 203226035Sgabor hashTableItem *item; 204226035Sgabor 205226035Sgabor xmalloc_init(); 206226035Sgabor 207226035Sgabor for (i = 0; i < TABLE_SIZE; i++) 208226035Sgabor { 209226035Sgabor item = xmalloc_table->table[i]; 210226035Sgabor while (item != NULL) 211226035Sgabor { 212226035Sgabor printf("%s:%d: %s: %d bytes at %p not freed\n", 213226035Sgabor item->file, item->line, item->func, item->bytes, item->ptr); 214226035Sgabor num_leaks++; 215226035Sgabor leaked_bytes += item->bytes; 216226035Sgabor item = item->next; 217226035Sgabor } 218226035Sgabor } 219226035Sgabor if (num_leaks == 0) 220226035Sgabor printf("No memory leaks.\n"); 221226035Sgabor else 222226035Sgabor printf("%d unfreed memory chuncks, total %d unfreed bytes.\n", 223226035Sgabor num_leaks, leaked_bytes); 224226035Sgabor printf("Peak memory consumption %d bytes (%.1f kB, %.1f MB) in %d blocks ", 225226035Sgabor xmalloc_peak, (double)xmalloc_peak / 1024, 226226035Sgabor (double)xmalloc_peak / (1024*1024), xmalloc_peak_blocks); 227226035Sgabor printf("(average "); 228226035Sgabor if (xmalloc_peak_blocks) 229226035Sgabor printf("%d", ((xmalloc_peak + xmalloc_peak_blocks / 2) 230226035Sgabor / xmalloc_peak_blocks)); 231226035Sgabor else 232226035Sgabor printf("N/A"); 233226035Sgabor printf(" bytes per block).\n"); 234226035Sgabor 235226035Sgabor return num_leaks; 236226035Sgabor} 237226035Sgabor 238226035Sgaborvoid * 239226035Sgaborxmalloc_impl(size_t size, const char *file, int line, const char *func) 240226035Sgabor{ 241226035Sgabor void *ptr; 242226035Sgabor 243226035Sgabor xmalloc_init(); 244226035Sgabor assert(size > 0); 245226035Sgabor 246226035Sgabor if (xmalloc_fail_after == 0) 247226035Sgabor { 248226035Sgabor xmalloc_fail_after = -2; 249226035Sgabor#if 0 250226035Sgabor printf("xmalloc: forced failure %s:%d: %s\n", file, line, func); 251226035Sgabor#endif 252226035Sgabor return NULL; 253226035Sgabor } 254226035Sgabor else if (xmalloc_fail_after == -2) 255226035Sgabor { 256226035Sgabor printf("xmalloc: called after failure from %s:%d: %s\n", 257226035Sgabor file, line, func); 258226035Sgabor assert(0); 259226035Sgabor } 260226035Sgabor else if (xmalloc_fail_after > 0) 261226035Sgabor xmalloc_fail_after--; 262226035Sgabor 263226035Sgabor ptr = malloc(size); 264226035Sgabor if (ptr != NULL) 265226035Sgabor hash_table_add(xmalloc_table, ptr, (int)size, file, line, func); 266226035Sgabor return ptr; 267226035Sgabor} 268226035Sgabor 269226035Sgaborvoid * 270226035Sgaborxcalloc_impl(size_t nmemb, size_t size, const char *file, int line, 271226035Sgabor const char *func) 272226035Sgabor{ 273226035Sgabor void *ptr; 274226035Sgabor 275226035Sgabor xmalloc_init(); 276226035Sgabor assert(size > 0); 277226035Sgabor 278226035Sgabor if (xmalloc_fail_after == 0) 279226035Sgabor { 280226035Sgabor xmalloc_fail_after = -2; 281226035Sgabor#if 0 282226035Sgabor printf("xcalloc: forced failure %s:%d: %s\n", file, line, func); 283226035Sgabor#endif 284226035Sgabor return NULL; 285226035Sgabor } 286226035Sgabor else if (xmalloc_fail_after == -2) 287226035Sgabor { 288226035Sgabor printf("xcalloc: called after failure from %s:%d: %s\n", 289226035Sgabor file, line, func); 290226035Sgabor assert(0); 291226035Sgabor } 292226035Sgabor else if (xmalloc_fail_after > 0) 293226035Sgabor xmalloc_fail_after--; 294226035Sgabor 295226035Sgabor ptr = calloc(nmemb, size); 296226035Sgabor if (ptr != NULL) 297226035Sgabor hash_table_add(xmalloc_table, ptr, (int)(nmemb * size), file, line, func); 298226035Sgabor return ptr; 299226035Sgabor} 300226035Sgabor 301226035Sgaborvoid 302226035Sgaborxfree_impl(void *ptr, const char *file, int line, const char *func) 303226035Sgabor{ 304226035Sgabor /*LINTED*/(void)&file; 305226035Sgabor /*LINTED*/(void)&line; 306226035Sgabor /*LINTED*/(void)&func; 307226035Sgabor xmalloc_init(); 308226035Sgabor 309226035Sgabor if (ptr != NULL) 310226035Sgabor hash_table_del(xmalloc_table, ptr); 311226035Sgabor free(ptr); 312226035Sgabor} 313226035Sgabor 314226035Sgaborvoid * 315226035Sgaborxrealloc_impl(void *ptr, size_t new_size, const char *file, int line, 316226035Sgabor const char *func) 317226035Sgabor{ 318226035Sgabor void *new_ptr; 319226035Sgabor 320226035Sgabor xmalloc_init(); 321226035Sgabor assert(ptr != NULL); 322226035Sgabor assert(new_size > 0); 323226035Sgabor 324226035Sgabor if (xmalloc_fail_after == 0) 325226035Sgabor { 326226035Sgabor xmalloc_fail_after = -2; 327226035Sgabor return NULL; 328226035Sgabor } 329226035Sgabor else if (xmalloc_fail_after == -2) 330226035Sgabor { 331226035Sgabor printf("xrealloc: called after failure from %s:%d: %s\n", 332226035Sgabor file, line, func); 333226035Sgabor assert(0); 334226035Sgabor } 335226035Sgabor else if (xmalloc_fail_after > 0) 336226035Sgabor xmalloc_fail_after--; 337226035Sgabor 338226035Sgabor new_ptr = realloc(ptr, new_size); 339226035Sgabor if (new_ptr != NULL) 340226035Sgabor { 341226035Sgabor hash_table_del(xmalloc_table, ptr); 342226035Sgabor hash_table_add(xmalloc_table, new_ptr, (int)new_size, file, line, func); 343226035Sgabor } 344226035Sgabor return new_ptr; 345226035Sgabor} 346226035Sgabor 347226035Sgabor 348226035Sgabor 349226035Sgabor/* EOF */ 350