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