1226035Sgabor/* $FreeBSD$ */
2226035Sgabor
3226035Sgabor/*
4226035Sgabor  xmalloc.c - Simple malloc debugging library implementation
5226035Sgabor
6226035Sgabor  This software is released under a BSD-style license.
7226035Sgabor  See the file LICENSE for details and copyright.
8226035Sgabor
9226035Sgabor*/
10226035Sgabor
11226035Sgabor/*
12226035Sgabor  TODO:
13226035Sgabor   - red zones
14226035Sgabor   - group dumps by source location
15226035Sgabor*/
16226035Sgabor
17226035Sgabor#include <stdlib.h>
18226035Sgabor#include <assert.h>
19226035Sgabor#include <stdio.h>
20226035Sgabor#define XMALLOC_INTERNAL 1
21226035Sgabor#include "xmalloc.h"
22226035Sgabor
23226035Sgabor
24226035Sgabor/*
25226035Sgabor  Internal stuff.
26226035Sgabor*/
27226035Sgabor
28226035Sgabortypedef struct hashTableItemRec {
29226035Sgabor  void *ptr;
30226035Sgabor  int bytes;
31226035Sgabor  const char *file;
32226035Sgabor  int line;
33226035Sgabor  const char *func;
34226035Sgabor  struct hashTableItemRec *next;
35226035Sgabor} hashTableItem;
36226035Sgabor
37226035Sgabortypedef struct {
38226035Sgabor  hashTableItem **table;
39226035Sgabor} hashTable;
40226035Sgabor
41226035Sgaborstatic int xmalloc_peak;
42241737Sedstatic int xmalloc_current;
43226035Sgaborstatic int xmalloc_peak_blocks;
44241737Sedstatic int xmalloc_current_blocks;
45226035Sgaborstatic int xmalloc_fail_after;
46226035Sgabor
47226035Sgabor#define TABLE_BITS 8
48226035Sgabor#define TABLE_MASK ((1 << TABLE_BITS) - 1)
49226035Sgabor#define TABLE_SIZE (1 << TABLE_BITS)
50226035Sgabor
51226035Sgaborstatic hashTable *
52226035Sgaborhash_table_new(void)
53226035Sgabor{
54226035Sgabor  hashTable *tbl;
55226035Sgabor
56226035Sgabor  tbl = malloc(sizeof(*tbl));
57226035Sgabor
58226035Sgabor  if (tbl != NULL)
59226035Sgabor    {
60226035Sgabor      tbl->table = calloc(TABLE_SIZE, sizeof(*tbl->table));
61226035Sgabor
62226035Sgabor      if (tbl->table == NULL)
63226035Sgabor	{
64226035Sgabor	  free(tbl);
65226035Sgabor	  return NULL;
66226035Sgabor	}
67226035Sgabor    }
68226035Sgabor
69226035Sgabor  return tbl;
70226035Sgabor}
71226035Sgabor
72226035Sgaborstatic int
73226035Sgaborhash_void_ptr(void *ptr)
74226035Sgabor{
75226035Sgabor  int hash;
76226035Sgabor  int i;
77226035Sgabor
78226035Sgabor  /* I took this hash function just off the top of my head, I have
79226035Sgabor     no idea whether it is bad or very bad. */
80226035Sgabor  hash = 0;
81226035Sgabor  for (i = 0; i < (int)sizeof(ptr)*8 / TABLE_BITS; i++)
82226035Sgabor    {
83226035Sgabor      hash ^= (unsigned long)ptr >> i*8;
84226035Sgabor      hash += i * 17;
85226035Sgabor      hash &= TABLE_MASK;
86226035Sgabor    }
87226035Sgabor  return hash;
88226035Sgabor}
89226035Sgabor
90226035Sgaborstatic void
91226035Sgaborhash_table_add(hashTable *tbl, void *ptr, int bytes,
92226035Sgabor	       const char *file, int line, const char *func)
93226035Sgabor{
94226035Sgabor  int i;
95226035Sgabor  hashTableItem *item, *new;
96226035Sgabor
97226035Sgabor  i = hash_void_ptr(ptr);
98226035Sgabor
99226035Sgabor  item = tbl->table[i];
100226035Sgabor  if (item != NULL)
101226035Sgabor    while (item->next != NULL)
102226035Sgabor      item = item->next;
103226035Sgabor
104226035Sgabor  new = malloc(sizeof(*new));
105226035Sgabor  assert(new != NULL);
106226035Sgabor  new->ptr = ptr;
107226035Sgabor  new->bytes = bytes;
108226035Sgabor  new->file = file;
109226035Sgabor  new->line = line;
110226035Sgabor  new->func = func;
111226035Sgabor  new->next = NULL;
112226035Sgabor  if (item != NULL)
113226035Sgabor    item->next = new;
114226035Sgabor  else
115226035Sgabor    tbl->table[i] = new;
116226035Sgabor
117226035Sgabor  xmalloc_current += bytes;
118226035Sgabor  if (xmalloc_current > xmalloc_peak)
119226035Sgabor    xmalloc_peak = xmalloc_current;
120226035Sgabor  xmalloc_current_blocks++;
121226035Sgabor  if (xmalloc_current_blocks > xmalloc_peak_blocks)
122226035Sgabor    xmalloc_peak_blocks = xmalloc_current_blocks;
123226035Sgabor}
124226035Sgabor
125226035Sgaborstatic void
126226035Sgaborhash_table_del(hashTable *tbl, void *ptr)
127226035Sgabor{
128226035Sgabor  int i;
129226035Sgabor  hashTableItem *item, *prev;
130226035Sgabor
131226035Sgabor  i = hash_void_ptr(ptr);
132226035Sgabor
133226035Sgabor  item = tbl->table[i];
134226035Sgabor  if (item == NULL)
135226035Sgabor    {
136226035Sgabor      printf("xfree: invalid ptr %p\n", ptr);
137226035Sgabor      abort();
138226035Sgabor    }
139226035Sgabor  prev = NULL;
140226035Sgabor  while (item->ptr != ptr)
141226035Sgabor    {
142226035Sgabor      prev = item;
143226035Sgabor      item = item->next;
144226035Sgabor    }
145226035Sgabor  if (item->ptr != ptr)
146226035Sgabor    {
147226035Sgabor      printf("xfree: invalid ptr %p\n", ptr);
148226035Sgabor      abort();
149226035Sgabor    }
150226035Sgabor
151226035Sgabor  xmalloc_current -= item->bytes;
152226035Sgabor  xmalloc_current_blocks--;
153226035Sgabor
154226035Sgabor  if (prev != NULL)
155226035Sgabor    {
156226035Sgabor      prev->next = item->next;
157226035Sgabor      free(item);
158226035Sgabor    }
159226035Sgabor  else
160226035Sgabor    {
161226035Sgabor      tbl->table[i] = item->next;
162226035Sgabor      free(item);
163226035Sgabor    }
164226035Sgabor}
165226035Sgabor
166226035Sgaborstatic hashTable *xmalloc_table = NULL;
167226035Sgabor
168226035Sgaborstatic void
169226035Sgaborxmalloc_init(void)
170226035Sgabor{
171226035Sgabor  if (xmalloc_table == NULL)
172226035Sgabor    {
173226035Sgabor      xmalloc_table = hash_table_new();
174226035Sgabor      xmalloc_peak = 0;
175226035Sgabor      xmalloc_peak_blocks = 0;
176226035Sgabor      xmalloc_current = 0;
177226035Sgabor      xmalloc_current_blocks = 0;
178226035Sgabor      xmalloc_fail_after = -1;
179226035Sgabor    }
180226035Sgabor  assert(xmalloc_table != NULL);
181226035Sgabor  assert(xmalloc_table->table != NULL);
182226035Sgabor}
183226035Sgabor
184226035Sgabor
185226035Sgabor
186226035Sgabor/*
187226035Sgabor  Public API.
188226035Sgabor*/
189226035Sgabor
190226035Sgaborvoid
191226035Sgaborxmalloc_configure(int fail_after)
192226035Sgabor{
193226035Sgabor  xmalloc_init();
194226035Sgabor  xmalloc_fail_after = fail_after;
195226035Sgabor}
196226035Sgabor
197226035Sgaborint
198226035Sgaborxmalloc_dump_leaks(void)
199226035Sgabor{
200226035Sgabor  int i;
201226035Sgabor  int num_leaks = 0;
202226035Sgabor  int leaked_bytes = 0;
203226035Sgabor  hashTableItem *item;
204226035Sgabor
205226035Sgabor  xmalloc_init();
206226035Sgabor
207226035Sgabor  for (i = 0; i < TABLE_SIZE; i++)
208226035Sgabor    {
209226035Sgabor      item = xmalloc_table->table[i];
210226035Sgabor      while (item != NULL)
211226035Sgabor	{
212226035Sgabor	  printf("%s:%d: %s: %d bytes at %p not freed\n",
213226035Sgabor		 item->file, item->line, item->func, item->bytes, item->ptr);
214226035Sgabor	  num_leaks++;
215226035Sgabor	  leaked_bytes += item->bytes;
216226035Sgabor	  item = item->next;
217226035Sgabor	}
218226035Sgabor    }
219226035Sgabor  if (num_leaks == 0)
220226035Sgabor    printf("No memory leaks.\n");
221226035Sgabor  else
222226035Sgabor    printf("%d unfreed memory chuncks, total %d unfreed bytes.\n",
223226035Sgabor	   num_leaks, leaked_bytes);
224226035Sgabor  printf("Peak memory consumption %d bytes (%.1f kB, %.1f MB) in %d blocks ",
225226035Sgabor	 xmalloc_peak, (double)xmalloc_peak / 1024,
226226035Sgabor	 (double)xmalloc_peak / (1024*1024), xmalloc_peak_blocks);
227226035Sgabor  printf("(average ");
228226035Sgabor  if (xmalloc_peak_blocks)
229226035Sgabor    printf("%d", ((xmalloc_peak + xmalloc_peak_blocks / 2)
230226035Sgabor		  / xmalloc_peak_blocks));
231226035Sgabor  else
232226035Sgabor    printf("N/A");
233226035Sgabor  printf(" bytes per block).\n");
234226035Sgabor
235226035Sgabor  return num_leaks;
236226035Sgabor}
237226035Sgabor
238226035Sgaborvoid *
239226035Sgaborxmalloc_impl(size_t size, const char *file, int line, const char *func)
240226035Sgabor{
241226035Sgabor  void *ptr;
242226035Sgabor
243226035Sgabor  xmalloc_init();
244226035Sgabor  assert(size > 0);
245226035Sgabor
246226035Sgabor  if (xmalloc_fail_after == 0)
247226035Sgabor    {
248226035Sgabor      xmalloc_fail_after = -2;
249226035Sgabor#if 0
250226035Sgabor      printf("xmalloc: forced failure %s:%d: %s\n", file, line, func);
251226035Sgabor#endif
252226035Sgabor      return NULL;
253226035Sgabor    }
254226035Sgabor  else if (xmalloc_fail_after == -2)
255226035Sgabor    {
256226035Sgabor      printf("xmalloc: called after failure from %s:%d: %s\n",
257226035Sgabor	     file, line, func);
258226035Sgabor      assert(0);
259226035Sgabor    }
260226035Sgabor  else if (xmalloc_fail_after > 0)
261226035Sgabor    xmalloc_fail_after--;
262226035Sgabor
263226035Sgabor  ptr = malloc(size);
264226035Sgabor  if (ptr != NULL)
265226035Sgabor    hash_table_add(xmalloc_table, ptr, (int)size, file, line, func);
266226035Sgabor  return ptr;
267226035Sgabor}
268226035Sgabor
269226035Sgaborvoid *
270226035Sgaborxcalloc_impl(size_t nmemb, size_t size, const char *file, int line,
271226035Sgabor	     const char *func)
272226035Sgabor{
273226035Sgabor  void *ptr;
274226035Sgabor
275226035Sgabor  xmalloc_init();
276226035Sgabor  assert(size > 0);
277226035Sgabor
278226035Sgabor  if (xmalloc_fail_after == 0)
279226035Sgabor    {
280226035Sgabor      xmalloc_fail_after = -2;
281226035Sgabor#if 0
282226035Sgabor      printf("xcalloc: forced failure %s:%d: %s\n", file, line, func);
283226035Sgabor#endif
284226035Sgabor      return NULL;
285226035Sgabor    }
286226035Sgabor  else if (xmalloc_fail_after == -2)
287226035Sgabor    {
288226035Sgabor      printf("xcalloc: called after failure from %s:%d: %s\n",
289226035Sgabor	     file, line, func);
290226035Sgabor      assert(0);
291226035Sgabor    }
292226035Sgabor  else if (xmalloc_fail_after > 0)
293226035Sgabor    xmalloc_fail_after--;
294226035Sgabor
295226035Sgabor  ptr = calloc(nmemb, size);
296226035Sgabor  if (ptr != NULL)
297226035Sgabor    hash_table_add(xmalloc_table, ptr, (int)(nmemb * size), file, line, func);
298226035Sgabor  return ptr;
299226035Sgabor}
300226035Sgabor
301226035Sgaborvoid
302226035Sgaborxfree_impl(void *ptr, const char *file, int line, const char *func)
303226035Sgabor{
304226035Sgabor  /*LINTED*/(void)&file;
305226035Sgabor  /*LINTED*/(void)&line;
306226035Sgabor  /*LINTED*/(void)&func;
307226035Sgabor  xmalloc_init();
308226035Sgabor
309226035Sgabor  if (ptr != NULL)
310226035Sgabor    hash_table_del(xmalloc_table, ptr);
311226035Sgabor  free(ptr);
312226035Sgabor}
313226035Sgabor
314226035Sgaborvoid *
315226035Sgaborxrealloc_impl(void *ptr, size_t new_size, const char *file, int line,
316226035Sgabor	      const char *func)
317226035Sgabor{
318226035Sgabor  void *new_ptr;
319226035Sgabor
320226035Sgabor  xmalloc_init();
321226035Sgabor  assert(ptr != NULL);
322226035Sgabor  assert(new_size > 0);
323226035Sgabor
324226035Sgabor  if (xmalloc_fail_after == 0)
325226035Sgabor    {
326226035Sgabor      xmalloc_fail_after = -2;
327226035Sgabor      return NULL;
328226035Sgabor    }
329226035Sgabor  else if (xmalloc_fail_after == -2)
330226035Sgabor    {
331226035Sgabor      printf("xrealloc: called after failure from %s:%d: %s\n",
332226035Sgabor	     file, line, func);
333226035Sgabor      assert(0);
334226035Sgabor    }
335226035Sgabor  else if (xmalloc_fail_after > 0)
336226035Sgabor    xmalloc_fail_after--;
337226035Sgabor
338226035Sgabor  new_ptr = realloc(ptr, new_size);
339226035Sgabor  if (new_ptr != NULL)
340226035Sgabor    {
341226035Sgabor      hash_table_del(xmalloc_table, ptr);
342226035Sgabor      hash_table_add(xmalloc_table, new_ptr, (int)new_size, file, line, func);
343226035Sgabor    }
344226035Sgabor  return new_ptr;
345226035Sgabor}
346226035Sgabor
347226035Sgabor
348226035Sgabor
349226035Sgabor/* EOF */
350