xref: /netbsd-src/external/bsd/tre/dist/lib/xmalloc.c (revision f2a3d14797d93b3276c22bce2e57926bf739c955)
163d4abf0Sagc /*
263d4abf0Sagc   xmalloc.c - Simple malloc debugging library implementation
363d4abf0Sagc 
463d4abf0Sagc   This software is released under a BSD-style license.
563d4abf0Sagc   See the file LICENSE for details and copyright.
663d4abf0Sagc 
763d4abf0Sagc */
863d4abf0Sagc 
963d4abf0Sagc /*
1063d4abf0Sagc   TODO:
1163d4abf0Sagc    - red zones
1263d4abf0Sagc    - group dumps by source location
1363d4abf0Sagc */
1463d4abf0Sagc 
1563d4abf0Sagc #ifdef HAVE_CONFIG_H
1663d4abf0Sagc #include <config.h>
1763d4abf0Sagc #endif /* HAVE_CONFIG_H */
1863d4abf0Sagc 
1963d4abf0Sagc #include <stdlib.h>
2063d4abf0Sagc #include <assert.h>
2163d4abf0Sagc #include <stdio.h>
2263d4abf0Sagc #define XMALLOC_INTERNAL 1
2363d4abf0Sagc #include "xmalloc.h"
2463d4abf0Sagc 
2563d4abf0Sagc 
2663d4abf0Sagc /*
2763d4abf0Sagc   Internal stuff.
2863d4abf0Sagc */
2963d4abf0Sagc 
3063d4abf0Sagc typedef struct hashTableItemRec {
3163d4abf0Sagc   void *ptr;
3263d4abf0Sagc   int bytes;
3363d4abf0Sagc   const char *file;
3463d4abf0Sagc   int line;
3563d4abf0Sagc   const char *func;
3663d4abf0Sagc   struct hashTableItemRec *next;
3763d4abf0Sagc } hashTableItem;
3863d4abf0Sagc 
3963d4abf0Sagc typedef struct {
4063d4abf0Sagc   hashTableItem **table;
4163d4abf0Sagc } hashTable;
4263d4abf0Sagc 
4363d4abf0Sagc static int xmalloc_peak;
4463d4abf0Sagc int xmalloc_current;
4563d4abf0Sagc static int xmalloc_peak_blocks;
4663d4abf0Sagc int xmalloc_current_blocks;
4763d4abf0Sagc static int xmalloc_fail_after;
4863d4abf0Sagc 
4963d4abf0Sagc #define TABLE_BITS 8
5063d4abf0Sagc #define TABLE_MASK ((1 << TABLE_BITS) - 1)
5163d4abf0Sagc #define TABLE_SIZE (1 << TABLE_BITS)
5263d4abf0Sagc 
5363d4abf0Sagc static hashTable *
hash_table_new(void)5463d4abf0Sagc hash_table_new(void)
5563d4abf0Sagc {
5663d4abf0Sagc   hashTable *tbl;
5763d4abf0Sagc 
5863d4abf0Sagc   tbl = malloc(sizeof(*tbl));
5963d4abf0Sagc 
6063d4abf0Sagc   if (tbl != NULL)
6163d4abf0Sagc     {
6263d4abf0Sagc       tbl->table = calloc(TABLE_SIZE, sizeof(*tbl->table));
6363d4abf0Sagc 
6463d4abf0Sagc       if (tbl->table == NULL)
6563d4abf0Sagc 	{
6663d4abf0Sagc 	  free(tbl);
6763d4abf0Sagc 	  return NULL;
6863d4abf0Sagc 	}
6963d4abf0Sagc     }
7063d4abf0Sagc 
7163d4abf0Sagc   return tbl;
7263d4abf0Sagc }
7363d4abf0Sagc 
7463d4abf0Sagc static int
hash_void_ptr(void * ptr)7563d4abf0Sagc hash_void_ptr(void *ptr)
7663d4abf0Sagc {
7763d4abf0Sagc   int hash;
7863d4abf0Sagc   int i;
7963d4abf0Sagc 
8063d4abf0Sagc   /* I took this hash function just off the top of my head, I have
8163d4abf0Sagc      no idea whether it is bad or very bad. */
8263d4abf0Sagc   hash = 0;
8363d4abf0Sagc   for (i = 0; i < (int)sizeof(ptr)*8 / TABLE_BITS; i++)
8463d4abf0Sagc     {
85*f2a3d147Schristos       hash ^= (int)((unsigned long)ptr >> i*8);
8663d4abf0Sagc       hash += i * 17;
8763d4abf0Sagc       hash &= TABLE_MASK;
8863d4abf0Sagc     }
8963d4abf0Sagc   return hash;
9063d4abf0Sagc }
9163d4abf0Sagc 
9263d4abf0Sagc static void
hash_table_add(hashTable * tbl,void * ptr,int bytes,const char * file,int line,const char * func)9363d4abf0Sagc hash_table_add(hashTable *tbl, void *ptr, int bytes,
9463d4abf0Sagc 	       const char *file, int line, const char *func)
9563d4abf0Sagc {
9663d4abf0Sagc   int i;
9763d4abf0Sagc   hashTableItem *item, *new;
9863d4abf0Sagc 
9963d4abf0Sagc   i = hash_void_ptr(ptr);
10063d4abf0Sagc 
10163d4abf0Sagc   item = tbl->table[i];
10263d4abf0Sagc   if (item != NULL)
10363d4abf0Sagc     while (item->next != NULL)
10463d4abf0Sagc       item = item->next;
10563d4abf0Sagc 
10663d4abf0Sagc   new = malloc(sizeof(*new));
10763d4abf0Sagc   assert(new != NULL);
10863d4abf0Sagc   new->ptr = ptr;
10963d4abf0Sagc   new->bytes = bytes;
11063d4abf0Sagc   new->file = file;
11163d4abf0Sagc   new->line = line;
11263d4abf0Sagc   new->func = func;
11363d4abf0Sagc   new->next = NULL;
11463d4abf0Sagc   if (item != NULL)
11563d4abf0Sagc     item->next = new;
11663d4abf0Sagc   else
11763d4abf0Sagc     tbl->table[i] = new;
11863d4abf0Sagc 
11963d4abf0Sagc   xmalloc_current += bytes;
12063d4abf0Sagc   if (xmalloc_current > xmalloc_peak)
12163d4abf0Sagc     xmalloc_peak = xmalloc_current;
12263d4abf0Sagc   xmalloc_current_blocks++;
12363d4abf0Sagc   if (xmalloc_current_blocks > xmalloc_peak_blocks)
12463d4abf0Sagc     xmalloc_peak_blocks = xmalloc_current_blocks;
12563d4abf0Sagc }
12663d4abf0Sagc 
12763d4abf0Sagc static void
hash_table_del(hashTable * tbl,void * ptr)12863d4abf0Sagc hash_table_del(hashTable *tbl, void *ptr)
12963d4abf0Sagc {
13063d4abf0Sagc   int i;
13163d4abf0Sagc   hashTableItem *item, *prev;
13263d4abf0Sagc 
13363d4abf0Sagc   i = hash_void_ptr(ptr);
13463d4abf0Sagc 
13563d4abf0Sagc   item = tbl->table[i];
13663d4abf0Sagc   if (item == NULL)
13763d4abf0Sagc     {
13863d4abf0Sagc       printf("xfree: invalid ptr %p\n", ptr);
13963d4abf0Sagc       abort();
14063d4abf0Sagc     }
14163d4abf0Sagc   prev = NULL;
14263d4abf0Sagc   while (item->ptr != ptr)
14363d4abf0Sagc     {
14463d4abf0Sagc       prev = item;
14563d4abf0Sagc       item = item->next;
14663d4abf0Sagc     }
14763d4abf0Sagc   if (item->ptr != ptr)
14863d4abf0Sagc     {
14963d4abf0Sagc       printf("xfree: invalid ptr %p\n", ptr);
15063d4abf0Sagc       abort();
15163d4abf0Sagc     }
15263d4abf0Sagc 
15363d4abf0Sagc   xmalloc_current -= item->bytes;
15463d4abf0Sagc   xmalloc_current_blocks--;
15563d4abf0Sagc 
15663d4abf0Sagc   if (prev != NULL)
15763d4abf0Sagc     {
15863d4abf0Sagc       prev->next = item->next;
15963d4abf0Sagc       free(item);
16063d4abf0Sagc     }
16163d4abf0Sagc   else
16263d4abf0Sagc     {
16363d4abf0Sagc       tbl->table[i] = item->next;
16463d4abf0Sagc       free(item);
16563d4abf0Sagc     }
16663d4abf0Sagc }
16763d4abf0Sagc 
16863d4abf0Sagc static hashTable *xmalloc_table = NULL;
16963d4abf0Sagc 
17063d4abf0Sagc static void
xmalloc_init(void)17163d4abf0Sagc xmalloc_init(void)
17263d4abf0Sagc {
17363d4abf0Sagc   if (xmalloc_table == NULL)
17463d4abf0Sagc     {
17563d4abf0Sagc       xmalloc_table = hash_table_new();
17663d4abf0Sagc       xmalloc_peak = 0;
17763d4abf0Sagc       xmalloc_peak_blocks = 0;
17863d4abf0Sagc       xmalloc_current = 0;
17963d4abf0Sagc       xmalloc_current_blocks = 0;
18063d4abf0Sagc       xmalloc_fail_after = -1;
18163d4abf0Sagc     }
18263d4abf0Sagc   assert(xmalloc_table != NULL);
18363d4abf0Sagc   assert(xmalloc_table->table != NULL);
18463d4abf0Sagc }
18563d4abf0Sagc 
18663d4abf0Sagc 
18763d4abf0Sagc 
18863d4abf0Sagc /*
18963d4abf0Sagc   Public API.
19063d4abf0Sagc */
19163d4abf0Sagc 
19263d4abf0Sagc void
xmalloc_configure(int fail_after)19363d4abf0Sagc xmalloc_configure(int fail_after)
19463d4abf0Sagc {
19563d4abf0Sagc   xmalloc_init();
19663d4abf0Sagc   xmalloc_fail_after = fail_after;
19763d4abf0Sagc }
19863d4abf0Sagc 
19963d4abf0Sagc int
xmalloc_dump_leaks(void)20063d4abf0Sagc xmalloc_dump_leaks(void)
20163d4abf0Sagc {
20263d4abf0Sagc   int i;
20363d4abf0Sagc   int num_leaks = 0;
20463d4abf0Sagc   int leaked_bytes = 0;
20563d4abf0Sagc   hashTableItem *item;
20663d4abf0Sagc 
20763d4abf0Sagc   xmalloc_init();
20863d4abf0Sagc 
20963d4abf0Sagc   for (i = 0; i < TABLE_SIZE; i++)
21063d4abf0Sagc     {
21163d4abf0Sagc       item = xmalloc_table->table[i];
21263d4abf0Sagc       while (item != NULL)
21363d4abf0Sagc 	{
21463d4abf0Sagc 	  printf("%s:%d: %s: %d bytes at %p not freed\n",
21563d4abf0Sagc 		 item->file, item->line, item->func, item->bytes, item->ptr);
21663d4abf0Sagc 	  num_leaks++;
21763d4abf0Sagc 	  leaked_bytes += item->bytes;
21863d4abf0Sagc 	  item = item->next;
21963d4abf0Sagc 	}
22063d4abf0Sagc     }
22163d4abf0Sagc   if (num_leaks == 0)
22263d4abf0Sagc     printf("No memory leaks.\n");
22363d4abf0Sagc   else
22463d4abf0Sagc     printf("%d unfreed memory chuncks, total %d unfreed bytes.\n",
22563d4abf0Sagc 	   num_leaks, leaked_bytes);
22663d4abf0Sagc   printf("Peak memory consumption %d bytes (%.1f kB, %.1f MB) in %d blocks ",
22763d4abf0Sagc 	 xmalloc_peak, (double)xmalloc_peak / 1024,
22863d4abf0Sagc 	 (double)xmalloc_peak / (1024*1024), xmalloc_peak_blocks);
22963d4abf0Sagc   printf("(average ");
23063d4abf0Sagc   if (xmalloc_peak_blocks)
23163d4abf0Sagc     printf("%d", ((xmalloc_peak + xmalloc_peak_blocks / 2)
23263d4abf0Sagc 		  / xmalloc_peak_blocks));
23363d4abf0Sagc   else
23463d4abf0Sagc     printf("N/A");
23563d4abf0Sagc   printf(" bytes per block).\n");
23663d4abf0Sagc 
23763d4abf0Sagc   return num_leaks;
23863d4abf0Sagc }
23963d4abf0Sagc 
24063d4abf0Sagc void *
xmalloc_impl(size_t size,const char * file,int line,const char * func)24163d4abf0Sagc xmalloc_impl(size_t size, const char *file, int line, const char *func)
24263d4abf0Sagc {
24363d4abf0Sagc   void *ptr;
24463d4abf0Sagc 
24563d4abf0Sagc   xmalloc_init();
24663d4abf0Sagc   assert(size > 0);
24763d4abf0Sagc 
24863d4abf0Sagc   if (xmalloc_fail_after == 0)
24963d4abf0Sagc     {
25063d4abf0Sagc       xmalloc_fail_after = -2;
25163d4abf0Sagc #if 0
25263d4abf0Sagc       printf("xmalloc: forced failure %s:%d: %s\n", file, line, func);
25363d4abf0Sagc #endif
25463d4abf0Sagc       return NULL;
25563d4abf0Sagc     }
25663d4abf0Sagc   else if (xmalloc_fail_after == -2)
25763d4abf0Sagc     {
25863d4abf0Sagc       printf("xmalloc: called after failure from %s:%d: %s\n",
25963d4abf0Sagc 	     file, line, func);
26063d4abf0Sagc       assert(0);
26163d4abf0Sagc     }
26263d4abf0Sagc   else if (xmalloc_fail_after > 0)
26363d4abf0Sagc     xmalloc_fail_after--;
26463d4abf0Sagc 
26563d4abf0Sagc   ptr = malloc(size);
26663d4abf0Sagc   if (ptr != NULL)
26763d4abf0Sagc     hash_table_add(xmalloc_table, ptr, (int)size, file, line, func);
26863d4abf0Sagc   return ptr;
26963d4abf0Sagc }
27063d4abf0Sagc 
27163d4abf0Sagc void *
xcalloc_impl(size_t nmemb,size_t size,const char * file,int line,const char * func)27263d4abf0Sagc xcalloc_impl(size_t nmemb, size_t size, const char *file, int line,
27363d4abf0Sagc 	     const char *func)
27463d4abf0Sagc {
27563d4abf0Sagc   void *ptr;
27663d4abf0Sagc 
27763d4abf0Sagc   xmalloc_init();
27863d4abf0Sagc   assert(size > 0);
27963d4abf0Sagc 
28063d4abf0Sagc   if (xmalloc_fail_after == 0)
28163d4abf0Sagc     {
28263d4abf0Sagc       xmalloc_fail_after = -2;
28363d4abf0Sagc #if 0
28463d4abf0Sagc       printf("xcalloc: forced failure %s:%d: %s\n", file, line, func);
28563d4abf0Sagc #endif
28663d4abf0Sagc       return NULL;
28763d4abf0Sagc     }
28863d4abf0Sagc   else if (xmalloc_fail_after == -2)
28963d4abf0Sagc     {
29063d4abf0Sagc       printf("xcalloc: called after failure from %s:%d: %s\n",
29163d4abf0Sagc 	     file, line, func);
29263d4abf0Sagc       assert(0);
29363d4abf0Sagc     }
29463d4abf0Sagc   else if (xmalloc_fail_after > 0)
29563d4abf0Sagc     xmalloc_fail_after--;
29663d4abf0Sagc 
29763d4abf0Sagc   ptr = calloc(nmemb, size);
29863d4abf0Sagc   if (ptr != NULL)
29963d4abf0Sagc     hash_table_add(xmalloc_table, ptr, (int)(nmemb * size), file, line, func);
30063d4abf0Sagc   return ptr;
30163d4abf0Sagc }
30263d4abf0Sagc 
30363d4abf0Sagc void
xfree_impl(void * ptr,const char * file,int line,const char * func)30463d4abf0Sagc xfree_impl(void *ptr, const char *file, int line, const char *func)
30563d4abf0Sagc {
30663d4abf0Sagc   /*LINTED*/(void)&file;
30763d4abf0Sagc   /*LINTED*/(void)&line;
30863d4abf0Sagc   /*LINTED*/(void)&func;
30963d4abf0Sagc   xmalloc_init();
31063d4abf0Sagc 
31163d4abf0Sagc   if (ptr != NULL)
31263d4abf0Sagc     hash_table_del(xmalloc_table, ptr);
31363d4abf0Sagc   free(ptr);
31463d4abf0Sagc }
31563d4abf0Sagc 
31663d4abf0Sagc void *
xrealloc_impl(void * ptr,size_t new_size,const char * file,int line,const char * func)31763d4abf0Sagc xrealloc_impl(void *ptr, size_t new_size, const char *file, int line,
31863d4abf0Sagc 	      const char *func)
31963d4abf0Sagc {
32063d4abf0Sagc   void *new_ptr;
32163d4abf0Sagc 
32263d4abf0Sagc   xmalloc_init();
32363d4abf0Sagc   assert(ptr != NULL);
32463d4abf0Sagc   assert(new_size > 0);
32563d4abf0Sagc 
32663d4abf0Sagc   if (xmalloc_fail_after == 0)
32763d4abf0Sagc     {
32863d4abf0Sagc       xmalloc_fail_after = -2;
32963d4abf0Sagc       return NULL;
33063d4abf0Sagc     }
33163d4abf0Sagc   else if (xmalloc_fail_after == -2)
33263d4abf0Sagc     {
33363d4abf0Sagc       printf("xrealloc: called after failure from %s:%d: %s\n",
33463d4abf0Sagc 	     file, line, func);
33563d4abf0Sagc       assert(0);
33663d4abf0Sagc     }
33763d4abf0Sagc   else if (xmalloc_fail_after > 0)
33863d4abf0Sagc     xmalloc_fail_after--;
33963d4abf0Sagc 
34063d4abf0Sagc   new_ptr = realloc(ptr, new_size);
34163d4abf0Sagc   if (new_ptr != NULL)
34263d4abf0Sagc     {
34363d4abf0Sagc       hash_table_del(xmalloc_table, ptr);
34463d4abf0Sagc       hash_table_add(xmalloc_table, new_ptr, (int)new_size, file, line, func);
34563d4abf0Sagc     }
34663d4abf0Sagc   return new_ptr;
34763d4abf0Sagc }
34863d4abf0Sagc 
34963d4abf0Sagc 
35063d4abf0Sagc 
35163d4abf0Sagc /* EOF */
352