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 *
hash_table_new(void)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
hash_void_ptr(void * ptr)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
hash_table_add(hashTable * tbl,void * ptr,int bytes,const char * file,int line,const char * func)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
hash_table_del(hashTable * tbl,void * ptr)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
xmalloc_init(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
xmalloc_configure(int fail_after)193 xmalloc_configure(int fail_after)
194 {
195 xmalloc_init();
196 xmalloc_fail_after = fail_after;
197 }
198
199 int
xmalloc_dump_leaks(void)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 *
xmalloc_impl(size_t size,const char * file,int line,const char * func)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 *
xcalloc_impl(size_t nmemb,size_t size,const char * file,int line,const char * func)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
xfree_impl(void * ptr,const char * file,int line,const char * func)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 *
xrealloc_impl(void * ptr,size_t new_size,const char * file,int line,const char * func)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