1/* 2 xmalloc.c - Simple malloc debugging library implementation 3 4 This software is released under a BSD-style license. 5 See the file LICENSE for details and copyright. 6 7*/ 8 9/* 10 TODO: 11 - red zones 12 - group dumps by source location 13*/ 14 15#ifdef HAVE_CONFIG_H 16#include <config.h> 17#endif /* HAVE_CONFIG_H */ 18 19#include <stdlib.h> 20#include <assert.h> 21#include <stdio.h> 22#define XMALLOC_INTERNAL 1 23#include "xmalloc.h" 24 25 26/* 27 Internal stuff. 28*/ 29 30typedef struct hashTableItemRec { 31 void *ptr; 32 int bytes; 33 const char *file; 34 int line; 35 const char *func; 36 struct hashTableItemRec *next; 37} hashTableItem; 38 39typedef struct { 40 hashTableItem **table; 41} hashTable; 42 43static int xmalloc_peak; 44int xmalloc_current; 45static int xmalloc_peak_blocks; 46int xmalloc_current_blocks; 47static int xmalloc_fail_after; 48 49#define TABLE_BITS 8 50#define TABLE_MASK ((1 << TABLE_BITS) - 1) 51#define TABLE_SIZE (1 << TABLE_BITS) 52 53static hashTable * 54hash_table_new(void) 55{ 56 hashTable *tbl; 57 58 tbl = malloc(sizeof(*tbl)); 59 60 if (tbl != NULL) 61 { 62 tbl->table = calloc(TABLE_SIZE, sizeof(*tbl->table)); 63 64 if (tbl->table == NULL) 65 { 66 free(tbl); 67 return NULL; 68 } 69 } 70 71 return tbl; 72} 73 74static int 75hash_void_ptr(void *ptr) 76{ 77 int hash; 78 int i; 79 80 /* I took this hash function just off the top of my head, I have 81 no idea whether it is bad or very bad. */ 82 hash = 0; 83 for (i = 0; i < (int)sizeof(ptr)*8 / TABLE_BITS; i++) 84 { 85 hash ^= (unsigned long)ptr >> i*8; 86 hash += i * 17; 87 hash &= TABLE_MASK; 88 } 89 return hash; 90} 91 92static void 93hash_table_add(hashTable *tbl, void *ptr, int bytes, 94 const char *file, int line, const char *func) 95{ 96 int i; 97 hashTableItem *item, *new; 98 99 i = hash_void_ptr(ptr); 100 101 item = tbl->table[i]; 102 if (item != NULL) 103 while (item->next != NULL) 104 item = item->next; 105 106 new = malloc(sizeof(*new)); 107 assert(new != NULL); 108 new->ptr = ptr; 109 new->bytes = bytes; 110 new->file = file; 111 new->line = line; 112 new->func = func; 113 new->next = NULL; 114 if (item != NULL) 115 item->next = new; 116 else 117 tbl->table[i] = new; 118 119 xmalloc_current += bytes; 120 if (xmalloc_current > xmalloc_peak) 121 xmalloc_peak = xmalloc_current; 122 xmalloc_current_blocks++; 123 if (xmalloc_current_blocks > xmalloc_peak_blocks) 124 xmalloc_peak_blocks = xmalloc_current_blocks; 125} 126 127static void 128hash_table_del(hashTable *tbl, void *ptr) 129{ 130 int i; 131 hashTableItem *item, *prev; 132 133 i = hash_void_ptr(ptr); 134 135 item = tbl->table[i]; 136 if (item == NULL) 137 { 138 printf("xfree: invalid ptr %p\n", ptr); 139 abort(); 140 } 141 prev = NULL; 142 while (item->ptr != ptr) 143 { 144 prev = item; 145 item = item->next; 146 } 147 if (item->ptr != ptr) 148 { 149 printf("xfree: invalid ptr %p\n", ptr); 150 abort(); 151 } 152 153 xmalloc_current -= item->bytes; 154 xmalloc_current_blocks--; 155 156 if (prev != NULL) 157 { 158 prev->next = item->next; 159 free(item); 160 } 161 else 162 { 163 tbl->table[i] = item->next; 164 free(item); 165 } 166} 167 168static hashTable *xmalloc_table = NULL; 169 170static void 171xmalloc_init(void) 172{ 173 if (xmalloc_table == NULL) 174 { 175 xmalloc_table = hash_table_new(); 176 xmalloc_peak = 0; 177 xmalloc_peak_blocks = 0; 178 xmalloc_current = 0; 179 xmalloc_current_blocks = 0; 180 xmalloc_fail_after = -1; 181 } 182 assert(xmalloc_table != NULL); 183 assert(xmalloc_table->table != NULL); 184} 185 186 187 188/* 189 Public API. 190*/ 191 192void 193xmalloc_configure(int fail_after) 194{ 195 xmalloc_init(); 196 xmalloc_fail_after = fail_after; 197} 198 199int 200xmalloc_dump_leaks(void) 201{ 202 int i; 203 int num_leaks = 0; 204 int leaked_bytes = 0; 205 hashTableItem *item; 206 207 xmalloc_init(); 208 209 for (i = 0; i < TABLE_SIZE; i++) 210 { 211 item = xmalloc_table->table[i]; 212 while (item != NULL) 213 { 214 printf("%s:%d: %s: %d bytes at %p not freed\n", 215 item->file, item->line, item->func, item->bytes, item->ptr); 216 num_leaks++; 217 leaked_bytes += item->bytes; 218 item = item->next; 219 } 220 } 221 if (num_leaks == 0) 222 printf("No memory leaks.\n"); 223 else 224 printf("%d unfreed memory chuncks, total %d unfreed bytes.\n", 225 num_leaks, leaked_bytes); 226 printf("Peak memory consumption %d bytes (%.1f kB, %.1f MB) in %d blocks ", 227 xmalloc_peak, (double)xmalloc_peak / 1024, 228 (double)xmalloc_peak / (1024*1024), xmalloc_peak_blocks); 229 printf("(average "); 230 if (xmalloc_peak_blocks) 231 printf("%d", ((xmalloc_peak + xmalloc_peak_blocks / 2) 232 / xmalloc_peak_blocks)); 233 else 234 printf("N/A"); 235 printf(" bytes per block).\n"); 236 237 return num_leaks; 238} 239 240void * 241xmalloc_impl(size_t size, const char *file, int line, const char *func) 242{ 243 void *ptr; 244 245 xmalloc_init(); 246 assert(size > 0); 247 248 if (xmalloc_fail_after == 0) 249 { 250 xmalloc_fail_after = -2; 251#if 0 252 printf("xmalloc: forced failure %s:%d: %s\n", file, line, func); 253#endif 254 return NULL; 255 } 256 else if (xmalloc_fail_after == -2) 257 { 258 printf("xmalloc: called after failure from %s:%d: %s\n", 259 file, line, func); 260 assert(0); 261 } 262 else if (xmalloc_fail_after > 0) 263 xmalloc_fail_after--; 264 265 ptr = malloc(size); 266 if (ptr != NULL) 267 hash_table_add(xmalloc_table, ptr, (int)size, file, line, func); 268 return ptr; 269} 270 271void * 272xcalloc_impl(size_t nmemb, size_t size, const char *file, int line, 273 const char *func) 274{ 275 void *ptr; 276 277 xmalloc_init(); 278 assert(size > 0); 279 280 if (xmalloc_fail_after == 0) 281 { 282 xmalloc_fail_after = -2; 283#if 0 284 printf("xcalloc: forced failure %s:%d: %s\n", file, line, func); 285#endif 286 return NULL; 287 } 288 else if (xmalloc_fail_after == -2) 289 { 290 printf("xcalloc: called after failure from %s:%d: %s\n", 291 file, line, func); 292 assert(0); 293 } 294 else if (xmalloc_fail_after > 0) 295 xmalloc_fail_after--; 296 297 ptr = calloc(nmemb, size); 298 if (ptr != NULL) 299 hash_table_add(xmalloc_table, ptr, (int)(nmemb * size), file, line, func); 300 return ptr; 301} 302 303void 304xfree_impl(void *ptr, const char *file, int line, const char *func) 305{ 306 /*LINTED*/(void)&file; 307 /*LINTED*/(void)&line; 308 /*LINTED*/(void)&func; 309 xmalloc_init(); 310 311 if (ptr != NULL) 312 hash_table_del(xmalloc_table, ptr); 313 free(ptr); 314} 315 316void * 317xrealloc_impl(void *ptr, size_t new_size, const char *file, int line, 318 const char *func) 319{ 320 void *new_ptr; 321 322 xmalloc_init(); 323 assert(ptr != NULL); 324 assert(new_size > 0); 325 326 if (xmalloc_fail_after == 0) 327 { 328 xmalloc_fail_after = -2; 329 return NULL; 330 } 331 else if (xmalloc_fail_after == -2) 332 { 333 printf("xrealloc: called after failure from %s:%d: %s\n", 334 file, line, func); 335 assert(0); 336 } 337 else if (xmalloc_fail_after > 0) 338 xmalloc_fail_after--; 339 340 new_ptr = realloc(ptr, new_size); 341 if (new_ptr != NULL) 342 { 343 hash_table_del(xmalloc_table, ptr); 344 hash_table_add(xmalloc_table, new_ptr, (int)new_size, file, line, func); 345 } 346 return new_ptr; 347} 348 349 350 351/* EOF */ 352