xref: /netbsd-src/external/bsd/tre/dist/lib/xmalloc.c (revision f2a3d14797d93b3276c22bce2e57926bf739c955)
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