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 30 typedef 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 39 typedef struct { 40 hashTableItem **table; 41 } hashTable; 42 43 static int xmalloc_peak; 44 int xmalloc_current; 45 static int xmalloc_peak_blocks; 46 int xmalloc_current_blocks; 47 static 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 53 static hashTable * 54 hash_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 74 static int 75 hash_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 ^= (int)((unsigned long)ptr >> i*8); 86 hash += i * 17; 87 hash &= TABLE_MASK; 88 } 89 return hash; 90 } 91 92 static void 93 hash_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 127 static void 128 hash_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 168 static hashTable *xmalloc_table = NULL; 169 170 static void 171 xmalloc_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 192 void 193 xmalloc_configure(int fail_after) 194 { 195 xmalloc_init(); 196 xmalloc_fail_after = fail_after; 197 } 198 199 int 200 xmalloc_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 240 void * 241 xmalloc_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 271 void * 272 xcalloc_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 303 void 304 xfree_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 316 void * 317 xrealloc_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