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