Add slab allocator; reduce delta with srvx-gs.
[srvx.git] / src / alloc-srvx.c
1 /* alloc-srvx.c - Debug allocation wrapper
2  * Copyright 2005 srvx Development Team
3  *
4  * This file is part of srvx.
5  *
6  * srvx is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16
17 #include "common.h"
18 #include "log.h"
19
20 #undef malloc
21 #undef free
22
23 #define ALLOC_MAGIC 0x1acf
24 #define FREE_MAGIC  0xfc1d
25 const char redzone[] = { '\x03', '\x47', '\x76', '\xc7' };
26
27 struct alloc_header {
28     unsigned int file_id : 8;
29     unsigned int size : 24;
30     unsigned int line : 16;
31     unsigned int magic : 16;
32 };
33
34 static char file_id_map[256][32];
35 static unsigned int file_ids_used;
36 unsigned long alloc_count, alloc_size;
37
38 static int
39 file_id_cmp(const void *a_, const void *b_)
40 {
41     return strcmp(a_, b_);
42 }
43
44 static unsigned int
45 get_file_id(const char *fname)
46 {
47     void *entry;
48
49     entry = bsearch(fname, file_id_map, file_ids_used, sizeof(file_id_map[0]), file_id_cmp);
50     if (entry)
51         return ((char*)entry - file_id_map[0]) / sizeof(file_id_map[0]);
52     strcpy(file_id_map[file_ids_used++], fname);
53     qsort(file_id_map, file_ids_used, sizeof(file_id_map[0]), file_id_cmp);
54     return file_ids_used - 1;
55 }
56
57 void *
58 srvx_malloc(const char *file, unsigned int line, size_t size)
59 {
60     struct alloc_header *block;
61
62     block = malloc(sizeof(*block) + size + sizeof(redzone));
63     assert(block != NULL);
64     if (block->magic == ALLOC_MAGIC && block->file_id < file_ids_used) {
65         /* Only report the error, due to possible false positives. */
66         log_module(MAIN_LOG, LOG_WARNING, "Detected possible reallocation: %p (called by %s:%u/%u; allocated by %u:%u/%u).",
67                    block, file, line, size, block->file_id, block->line, block->size);
68     }
69     memset(block, 0, sizeof(*block) + size);
70     memcpy((char*)(block + 1) + size, redzone, sizeof(redzone));
71     block->file_id = get_file_id(file);
72     block->line = line;
73     block->size = size;
74     block->magic = ALLOC_MAGIC;
75     alloc_count++;
76     alloc_size += size;
77     return block + 1;
78 }
79
80 void *
81 srvx_realloc(const char *file, unsigned int line, void *ptr, size_t size)
82 {
83     struct alloc_header *block, *newblock;
84
85     if (!ptr)
86         return srvx_malloc(file, line, size);
87
88     verify(ptr);
89     block = (struct alloc_header *)ptr - 1;
90
91     if (block->size >= size)
92         return block + 1;
93
94     newblock = malloc(sizeof(*newblock) + size + sizeof(redzone));
95     assert(newblock != NULL);
96     memset(newblock, 0, sizeof(*newblock));
97     memcpy(newblock + 1, block + 1, block->size);
98     memset((char*)(newblock + 1) + block->size, 0, size - block->size);
99     memcpy((char*)(newblock + 1) + size, redzone, sizeof(redzone));
100     newblock->file_id = get_file_id(file);
101     newblock->line = line;
102     newblock->size = size;
103     newblock->magic = ALLOC_MAGIC;
104     alloc_count++;
105     alloc_size += size;
106
107     srvx_free(file, line, block + 1);
108
109     return newblock + 1;
110 }
111
112 char *
113 srvx_strdup(const char *file, unsigned int line, const char *src)
114 {
115     char *target;
116     size_t len;
117
118     len = strlen(src) + 1;
119     target = srvx_malloc(file, line, len);
120     memcpy(target, src, len);
121     return target;
122 }
123
124 void
125 srvx_free(UNUSED_ARG(const char *file), UNUSED_ARG(unsigned int line), void *ptr)
126 {
127     struct alloc_header *block;
128     size_t size;
129
130     if (!ptr)
131         return;
132     verify(ptr);
133     block = (struct alloc_header *)ptr - 1;
134     size = block->size;
135     memset(block + 1, 0xde, size);
136     block->magic = FREE_MAGIC;
137     free(block);
138     alloc_count--;
139     alloc_size -= size;
140 }
141
142 void
143 verify(const void *ptr)
144 {
145     const struct alloc_header *header;
146     if (!ptr)
147         return;
148     header = (const struct alloc_header*)ptr - 1;
149     assert(header->magic == ALLOC_MAGIC);
150     assert(!memcmp((char*)(header + 1) + header->size, redzone, sizeof(redzone)));
151 }