add Imager's memory debugger
[poe-xs-queue-array.git] / alloc.c
1 #include "alloc.h"
2 #include <stdio.h>
3 #include <errno.h>
4 #include <stdarg.h>
5
6 /* cheap version of Imager's logging */
7 char const *last_file; int last_line;
8
9 static void do_log(int level, char const *fmt, ...);
10 static
11 void
12 bndcheck(int idx);
13 void
14 bndcheck_all(void);
15
16 #define mm_log(x) ((last_file = __FILE__), (last_line = __LINE__), do_log x )
17
18 #ifdef MEM_DEBUG
19
20 #define MAXMAL 102400
21 #define MAXDESC 65
22
23 #define UNDRRNVAL 10
24 #define OVERRNVAL 10
25
26 #define PADBYTE 0xaa
27
28 static int malloc_need_init = 1;
29
30 void
31 malloc_state(void);
32
33 typedef struct {
34   void* ptr;
35   size_t size;
36   char comm[MAXDESC];
37 } malloc_entry;
38
39 malloc_entry malloc_pointers[MAXMAL];
40
41
42
43 /* Utility functions */
44
45
46 static
47 void
48 malloc_init(void) {
49   int i;
50   for(i=0; i<MAXMAL; i++) malloc_pointers[i].ptr = NULL;
51   malloc_need_init = 0;
52   atexit(malloc_state);
53 }
54
55
56 static
57 int 
58 find_ptr(void *p) {
59   int i;
60   for(i=0;i<MAXMAL;i++)
61     if (malloc_pointers[i].ptr == p)
62       return i;
63   return -1;
64 }
65
66
67 /* Takes a pointer to real start of array,
68  * sets the entries in the table, returns
69  * the offset corrected pointer */
70
71 static
72 void *
73 set_entry(int i, char *buf, size_t size, char const *file, int line) {
74   memset( buf, PADBYTE, UNDRRNVAL );
75   memset( &buf[UNDRRNVAL+size], PADBYTE, OVERRNVAL );
76   buf += UNDRRNVAL;
77   malloc_pointers[i].ptr  = buf;
78   malloc_pointers[i].size = size;
79   sprintf(malloc_pointers[i].comm,"%s (%d)", file, line);
80   return buf;
81 }
82
83 void
84 malloc_state(void) {
85   int i, total = 0;
86
87   mm_log((0,"malloc_state()\n"));
88   bndcheck_all();
89   for(i=0; i<MAXMAL; i++) if (malloc_pointers[i].ptr != NULL) {
90     mm_log((0,"%d: %d (0x%x) : %s\n", i, malloc_pointers[i].size, malloc_pointers[i].ptr, malloc_pointers[i].comm));
91     total += malloc_pointers[i].size;
92   }
93   if (total == 0) mm_log((0,"No memory currently used!\n"));
94                     else mm_log((0,"total: %d\n",total));
95 }
96
97
98
99 void*
100 mymalloc_file_line(size_t size, char const* file, int line) {
101   char *buf;
102   int i;
103   if (malloc_need_init) malloc_init();
104   
105   /* bndcheck_all(); Uncomment for LOTS OF THRASHING */
106   
107   if ( (i = find_ptr(NULL)) < 0 ) {
108     mm_log((0,"more than %d segments allocated at %s (%d)\n", MAXMAL, file, line));
109     exit(3);
110   }
111
112   if ( (buf = malloc(size+UNDRRNVAL+OVERRNVAL)) == NULL ) {
113     mm_log((1,"Unable to allocate %i for %s (%i)\n", size, file, line));
114     exit(3);
115   }
116   
117   buf = set_entry(i, buf, size, file, line);
118   mm_log((1,"mymalloc_file_line: slot <%d> %d bytes allocated at %p for %s (%d)\n", i, size, buf, file, line));
119   return buf;
120 }
121
122 void *
123 (mymalloc)(int size) {
124   return mymalloc_file_line(size, "unknown", 0);
125 }
126
127 void*
128 myrealloc_file_line(void *ptr, size_t newsize, char const * file, int line) {
129   char *buf;
130   int i;
131
132   if (malloc_need_init) malloc_init();
133   /* bndcheck_all(); ACTIVATE FOR LOTS OF THRASHING */
134   
135   if (!ptr) {
136     mm_log((1, "realloc called with ptr = NULL, sending request to malloc\n"));
137     return mymalloc_file_line(newsize, file, line);
138   }
139   
140   if (!newsize) {
141     mm_log((1, "newsize = 0, sending request to free\n"));
142     myfree_file_line(ptr, file, line);
143     return NULL;
144   }
145
146   if ( (i = find_ptr(ptr)) == -1) {
147     mm_log((0, "Unable to find %p in realloc for %s (%i)\n", ptr, file, line));
148     exit(3);
149   }
150   
151   if ( (buf = realloc(((char *)ptr)-UNDRRNVAL, UNDRRNVAL+OVERRNVAL+newsize)) == NULL ) {
152     mm_log((1,"Unable to reallocate %i bytes at %p for %s (%i)\n", newsize, ptr, file, line));
153     exit(3); 
154   }
155   
156   buf = set_entry(i, buf, newsize, file, line);
157   mm_log((1,"realloc_file_line: slot <%d> %d bytes allocated at %p for %s (%d)\n", i, newsize, buf, file, line));
158   return buf;
159 }
160
161 void *
162 (myrealloc)(void *ptr, size_t newsize) {
163   return myrealloc_file_line(ptr, newsize, "unknown", 0);
164 }
165
166 static
167 void
168 bndcheck(int idx) {
169   int i;
170   size_t s = malloc_pointers[idx].size;
171   unsigned char *pp = malloc_pointers[idx].ptr;
172   if (!pp) {
173     mm_log((1, "bndcheck: No pointer in slot %d\n", idx));
174     return;
175   }
176   
177   for(i=0;i<UNDRRNVAL;i++)
178      if (pp[-(1+i)] != PADBYTE)
179      mm_log((1,"bndcheck: UNDERRUN OF %d bytes detected: slot = %d, point = %p, size = %d\n", i+1, idx, pp, s ));
180   
181      for(i=0;i<OVERRNVAL;i++)
182     if (pp[s+i] != PADBYTE)
183       mm_log((1,"bndcheck: OVERRUN OF %d bytes detected: slot = %d, point = %p, size = %d\n", i+1, idx, pp, s ));
184 }
185
186 void
187 bndcheck_all(void) {
188   int idx;
189   mm_log((1, "bndcheck_all()\n"));
190   for(idx=0; idx<MAXMAL; idx++)
191     if (malloc_pointers[idx].ptr)
192       bndcheck(idx);
193 }
194
195 void
196 myfree_file_line(void *p, char const *file, int line) {
197   char  *pp = p;
198   int match = 0;
199   int i;
200   
201   for(i=0; i<MAXMAL; i++) if (malloc_pointers[i].ptr == p) {
202     mm_log((1,"myfree_file_line: pointer %i (%s) freed at %s (%i)\n", i, malloc_pointers[i].comm, file, line));
203     bndcheck(i);
204     malloc_pointers[i].ptr = NULL;
205     match++;
206   }
207
208   mm_log((1, "myfree_file_line: freeing address %p (real %p) (%s:%d)\n", pp, pp-UNDRRNVAL, file, line));
209   
210   if (match != 1) {
211     mm_log((1, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line));
212     fprintf(stderr, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line);
213                 exit(255);
214   }
215   
216   
217   free(pp-UNDRRNVAL);
218 }
219
220 void
221 (myfree)(void *block) {
222   myfree_file_line(block, "unknown", 0);
223 }
224
225 #else 
226
227 #define malloc_comm(a,b) (mymalloc(a))
228
229 void
230 malloc_state() {
231 }
232
233 void*
234 mymalloc(size_t size) {
235   void *buf;
236
237   if (size < 0) {
238     fprintf(stderr, "Attempt to allocate size %d\n", size);
239     exit(3);
240   }
241
242   if ( (buf = malloc(size)) == NULL ) {
243     mm_log((1, "mymalloc: unable to malloc %d\n", size));
244     fprintf(stderr,"Unable to malloc %d.\n", size); exit(3);
245   }
246   mm_log((1, "mymalloc(size %d) -> %p\n", size, buf));
247   return buf;
248 }
249
250 void *
251 mymalloc_file_line(size_t size, char *file, int line) {
252   return mymalloc(size);
253 }
254
255 void
256 myfree(void *p) {
257   mm_log((1, "myfree(p %p)\n", p));
258   free(p);
259 }
260
261 void
262 myfree_file_line(void *p, char *file, int line) {
263   myfree(p);
264 }
265
266 void *
267 myrealloc(void *block, size_t size) {
268   void *result;
269
270   mm_log((1, "myrealloc(block %p, size %u)\n", block, size));
271   if ((result = realloc(block, size)) == NULL) {
272     mm_log((1, "myrealloc: out of memory\n"));
273     fprintf(stderr, "Out of memory.\n");
274     exit(3);
275   }
276   return result;
277 }
278
279 void *
280 myrealloc_file_line(void *block, size_t newsize, char *file, int size) {
281   return myrealloc(block, newsize);
282 }
283
284 #endif /* IMAGER_MALLOC_DEBUG */
285
286 FILE *log_file;
287
288 void
289 setup_log(void) {
290   char *log_env = getenv("MEM_DEBUG");
291   if (!log_env)
292     return;
293
294   if (strcmp(log_env, "STDERR") == 0) {
295     log_file = stderr;
296     return;
297   }
298   log_file = fopen(log_env, "w+");
299   if (!log_file) {
300     fprintf(stderr, "Could not open log %s: %s\n", log_env, strerror(errno));
301     exit(3);
302   }
303 }
304
305 static void
306 do_log(int level, char const *fmt, ...) {
307   if (!log_file) setup_log();
308   
309   if (log_file) {
310     va_list ap;
311     va_start(ap, fmt);
312     fprintf(log_file, "[%s:%d] %d:", last_file, last_line, level);
313     
314     vfprintf(stderr, fmt, ap);
315     va_end(ap);
316   }
317 }