1 /*
2 * mpatrol
3 * A library for controlling and tracing dynamic memory allocations.
4 * Copyright (C) 1997-2002 Graeme S. Roy <graeme.roy@analog.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19 * MA 02111-1307, USA.
20 */
21
22
23 /*
24 * Dbmalloc-compatible interface. Implements Dbmalloc functions using
25 * mpatrol. Dbmalloc is copyright (C) 1990-1992 Conor P. Cahill.
26 */
27
28
29 #include "config.h"
30 #include <stdio.h>
31 #if TARGET == TARGET_WINDOWS
32 #include <io.h>
33 #else /* TARGET */
34 #include <unistd.h>
35 #endif /* TARGET */
36
37 #include "dbmalloc.h"
38
39 #if MP_IDENT_SUPPORT
40 #ident "$Id: dbmalloc.c,v 1.14 2002/01/08 20:05:10 graeme Exp $"
41 #else /* MP_IDENT_SUPPORT */
42 static MP_CONST MP_VOLATILE char *dbmalloc_id = "$Id: dbmalloc.c,v 1.14 2002/01/08 20:05:10 graeme Exp $";
43 #endif /* MP_IDENT_SUPPORT */
44
45
46 /* The structure used to pass information to the callback function from
47 * __mp_iterate() when __mpt_dbmallocdump() and __mpt_dbmalloclist() are
48 * called.
49 */
50
51 typedef struct listinfo
52 {
53 int file; /* file descriptor */
54 unsigned long event; /* upper event bound */
55 int header : 1; /* header output flag */
56 int dump : 1; /* dump output flag */
57 }
58 listinfo;
59
60
61 #ifdef __cplusplus
62 extern "C"
63 {
64 #endif /* __cplusplus */
65
66
67 /* Indicates if this module has been initialised.
68 */
69
70 static int malloc_initialised;
71
72
73 /* Specifies that more details should be shown when __mpt_dbmallocdump() is
74 * called.
75 */
76
77 static int malloc_detail;
78
79
80 /* Display the header for __mpt_dbmallocdump() and __mpt_dbmalloclist().
81 */
82
83 static
84 void
printheader(int f)85 printheader(int f)
86 {
87 char b[70 + (sizeof(void *) * 2)];
88 size_t i;
89
90 for (i = 0; i < 22 + sizeof(void *); i++)
91 b[i] = '*';
92 strcpy(b + i, " Dump of Malloc Chain ");
93 i += strlen(b + i);
94 while (i < 68 + (sizeof(void *) * 2))
95 b[i++] = '*';
96 b[i++] = '\n';
97 b[i] = '\0';
98 if (f > 0)
99 write(f, b, i);
100 else
101 __mp_printf(b);
102 for (i = 0; i < sizeof(void *) - 4; i++)
103 b[i] = ' ';
104 strcpy(b + i, "POINTER");
105 i += strlen(b + i);
106 while (i < 4 + (sizeof(void *) * 2))
107 b[i++] = ' ';
108 strcpy(b + i, "FILE WHERE LINE ALLOC DATA "
109 "HEX DUMP\n");
110 i += strlen(b + i);
111 if (f > 0)
112 write(f, b, i);
113 else
114 __mp_printf(b);
115 for (i = 0; i < sizeof(void *) - 4; i++)
116 b[i] = ' ';
117 strcpy(b + i, "TO DATA");
118 i += strlen(b + i);
119 while (i < 5 + (sizeof(void *) * 2))
120 b[i++] = ' ';
121 strcpy(b + i, "ALLOCATED NUMBER FUNCT LENGTH "
122 "OF BYTES 1-7\n");
123 i += strlen(b + i);
124 if (f > 0)
125 write(f, b, i);
126 else
127 __mp_printf(b);
128 for (i = 0; i < sizeof(void *) * 2; i++)
129 b[i] = '-';
130 b[i++] = ' ';
131 while (i < 21 + (sizeof(void *) * 2))
132 b[i++] = '-';
133 b[i++] = ' ';
134 while (i < 29 + (sizeof(void *) * 2))
135 b[i++] = '-';
136 b[i++] = ' ';
137 while (i < 44 + (sizeof(void *) * 2))
138 b[i++] = '-';
139 b[i++] = ' ';
140 while (i < 52 + (sizeof(void *) * 2))
141 b[i++] = '-';
142 b[i++] = ' ';
143 while (i < 67 + (sizeof(void *) * 2))
144 b[i++] = '-';
145 b[i++] = '\n';
146 b[i] = '\0';
147 if (f > 0)
148 write(f, b, i);
149 else
150 __mp_printf(b);
151 }
152
153
154 /* The callback function that is called by __mp_iterate() for every heap
155 * allocation that has changed since a specified heap event.
156 */
157
158 static
159 int
callback(MP_CONST void * p,void * t)160 callback(MP_CONST void *p, void *t)
161 {
162 char b[69 + (sizeof(void *) * 2)];
163 char m[64];
164 listinfo *i;
165 __mp_allocstack *a;
166 __mp_allocinfo d;
167 __mp_symbolinfo s;
168 size_t j, n;
169
170 if (!__mp_info(p, &d))
171 return 0;
172 i = (listinfo *) t;
173 if ((d.event <= i->event) && (!d.freed || (i->dump && malloc_detail)) &&
174 (!d.marked || i->dump))
175 {
176 if (!i->header)
177 {
178 printheader(i->file);
179 i->header = 1;
180 }
181 sprintf(b, "%0*lX ", sizeof(void *) * 2, d.block);
182 n = strlen(b);
183 if (d.file != NULL)
184 {
185 sprintf(m, "%7lu", d.line);
186 sprintf(b + n, "%-20.20s %7.7s ", d.file, m);
187 }
188 else
189 sprintf(b + n, "%-28s ", "unknown");
190 n += strlen(b + n);
191 sprintf(m, "%s(%lu)", __mp_function(d.type), d.alloc);
192 sprintf(b + n, "%-14.14s ", m);
193 n += strlen(b + n);
194 sprintf(m, "%7lu", d.size);
195 sprintf(b + n, "%7.7s ", m);
196 n += strlen(b + n);
197 for (j = 0; (j < 7) && (j < d.size); j++)
198 sprintf(m + (j << 1), "%02X", ((unsigned char *) d.block)[j]);
199 m[j << 1] = '\0';
200 sprintf(b + n, "%s\n", m);
201 n += strlen(b + n);
202 if (i->file > 0)
203 write(i->file, b, n);
204 else
205 __mp_printf(b);
206 for (a = d.stack; a != NULL; a = a->next)
207 {
208 for (n = 0; n <= sizeof(void *) * 2; n++)
209 b[n] = ' ';
210 strcpy(b + n, "-> ");
211 n += 3;
212 if (__mp_syminfo(a->addr, &s))
213 if (i->file > 0)
214 {
215 write(i->file, b, n);
216 write(i->file, s.name, strlen(s.name));
217 if (s.file != NULL)
218 {
219 write(i->file, " in ", 4);
220 write(i->file, s.file, strlen(s.file));
221 sprintf(b, "(%lu)\n", s.line);
222 write(i->file, b, strlen(b));
223 }
224 else
225 write(i->file, "\n", 1);
226 }
227 else if (s.file != NULL)
228 __mp_printf("%s%s in %s(%lu)\n", b, s.name, s.file, s.line);
229 else
230 __mp_printf("%s%s\n", b, s.name);
231 else
232 {
233 sprintf(b + n, "%0*lX\n", sizeof(void *) * 2, a->addr);
234 n += strlen(b + n);
235 if (i->file > 0)
236 write(i->file, b, n);
237 else
238 __mp_printf(b);
239 }
240 }
241 return 1;
242 }
243 return 0;
244 }
245
246
247 /* Set a malloc library option.
248 */
249
250 int
__mpt_dbmallocoption(int c,union dbmalloptarg * v)251 __mpt_dbmallocoption(int c, union dbmalloptarg *v)
252 {
253 char *s;
254 unsigned long n;
255 int r;
256
257 if (!malloc_initialised)
258 __mp_init_dbmalloc();
259 r = 0;
260 switch (c)
261 {
262 case MALLOC_ERRFILE:
263 if (strcmp(v->str, "-") == 0)
264 s = "stderr";
265 else
266 s = v->str;
267 r = __mp_setoption(MP_OPT_LOGFILE, (unsigned long) s);
268 break;
269 case MALLOC_CKCHAIN:
270 if (v->i != 0)
271 n = (unsigned long) -1;
272 else
273 n = 0;
274 if (((r = __mp_setoption(MP_OPT_CHECKLOWER, n)) == 0) &&
275 ((r = __mp_setoption(MP_OPT_CHECKUPPER, n)) == 0))
276 r = __mp_setoption(MP_OPT_CHECKFREQ, 1);
277 break;
278 case MALLOC_FILLAREA:
279 if (v->i == 0)
280 n = 0;
281 else if (!__mp_getoption(MP_OPT_OFLOWSIZE, &n) || (n == 0))
282 n = 1;
283 if ((r = __mp_setoption(MP_OPT_OFLOWSIZE, n)) == 0)
284 {
285 if ((v->i >= 0) && (v->i <= 2))
286 c = MP_OPT_SETFLAGS;
287 else
288 c = MP_OPT_UNSETFLAGS;
289 r = __mp_setoption(c, MP_FLG_PRESERVE);
290 }
291 break;
292 case MALLOC_REUSE:
293 if (v->i != 0)
294 n = 0;
295 else
296 n = ~0L;
297 r = __mp_setoption(MP_OPT_NOFREE, n);
298 break;
299 case MALLOC_DETAIL:
300 malloc_detail = v->i;
301 break;
302 case MALLOC_ZERO:
303 if (v->i != 0)
304 c = MP_OPT_SETFLAGS;
305 else
306 c = MP_OPT_UNSETFLAGS;
307 if (__mp_setoption(c, MP_FLG_CHECKALLOCS))
308 r = 1;
309 break;
310 default:
311 r = 1;
312 break;
313 }
314 return r;
315 }
316
317
318 /* Display a malloc library error message.
319 */
320
321 void
__mpt_dbmallocperror(MP_CONST char * s)322 __mpt_dbmallocperror(MP_CONST char *s)
323 {
324 if ((s != NULL) && (*s != '\0'))
325 fprintf(stderr, "%s: ", s);
326 if ((s = __mp_strerror(__mp_errno)) == NULL)
327 s = "unknown error";
328 fprintf(stderr, "%s\n", s);
329 }
330
331
332 /* Verify that the malloc chain is still intact and the heap has not been
333 * corrupted.
334 */
335
336 int
__mpt_dbmallocchaincheck(int f,MP_CONST char * s,MP_CONST char * t,unsigned long u)337 __mpt_dbmallocchaincheck(int f, MP_CONST char *s, MP_CONST char *t,
338 unsigned long u)
339 {
340 if (!malloc_initialised)
341 __mp_init_dbmalloc();
342 __mp_checkheap(s, t, u);
343 return 0;
344 }
345
346
347 /* Display all of the heap allocations and their associated data.
348 */
349
350 void
__mpt_dbmallocdump(int f)351 __mpt_dbmallocdump(int f)
352 {
353 listinfo i;
354
355 if (!malloc_initialised)
356 __mp_init_dbmalloc();
357 i.file = f;
358 i.event = __mp_snapshot();
359 i.header = 0;
360 i.dump = 1;
361 __mp_iterate(callback, &i, 0);
362 if (f > 0)
363 write(f, "\n", 1);
364 else if (i.header)
365 __mp_printf("\n");
366 }
367
368
369 /* Display some of the heap allocations and their associated data.
370 */
371
372 void
__mpt_dbmalloclist(int f,unsigned long l,unsigned long u)373 __mpt_dbmalloclist(int f, unsigned long l, unsigned long u)
374 {
375 listinfo i;
376
377 if (!malloc_initialised)
378 __mp_init_dbmalloc();
379 i.file = f;
380 if (l <= u)
381 i.event = u;
382 else
383 {
384 i.event = l;
385 l = u;
386 u = i.event;
387 }
388 i.header = 0;
389 i.dump = 0;
390 __mp_iterate(callback, &i, l);
391 if (f > 0)
392 write(f, "\n", 1);
393 else if (i.header)
394 __mp_printf("\n");
395 }
396
397
398 /* Return the number of bytes of heap memory currently in use and optionally
399 * return the current malloc history id.
400 */
401
402 unsigned long
__mpt_dbmallocinuse(unsigned long * h)403 __mpt_dbmallocinuse(unsigned long *h)
404 {
405 __mp_heapinfo i;
406 unsigned long t;
407
408 if (!malloc_initialised)
409 __mp_init_dbmalloc();
410 if (__mp_stats(&i))
411 t = i.atotal - i.mtotal;
412 else
413 t = 0;
414 if (h != NULL)
415 *h = __mp_snapshot();
416 return t;
417 }
418
419
420 /* Return the size in bytes of the memory allocation that contains a specified
421 * address.
422 */
423
424 size_t
__mpt_dbmallocsize(MP_CONST void * p)425 __mpt_dbmallocsize(MP_CONST void *p)
426 {
427 __mp_allocinfo i;
428 size_t t;
429
430 if (!malloc_initialised)
431 __mp_init_dbmalloc();
432 if (__mp_info(p, &i) && i.allocated && !i.freed)
433 t = i.size;
434 else
435 t = (size_t) -1;
436 return t;
437 }
438
439
440 /* Initialise the dbmalloc module.
441 */
442
443 void
__mp_init_dbmalloc(void)444 __mp_init_dbmalloc(void)
445 {
446 char *v;
447 union dbmalloptarg a;
448 long n;
449
450 if (!malloc_initialised)
451 {
452 malloc_initialised = 1;
453 malloc_detail = 0;
454 if ((v = getenv("MALLOC_BOUNDSIZE")) && (*v != '\0'))
455 {
456 if ((n = strtol(v, NULL, 10)) < 1)
457 n = 1;
458 __mp_setoption(MP_OPT_OFLOWSIZE, n);
459 }
460 if ((v = getenv("MALLOC_FILLBYTE")) && (*v != '\0'))
461 {
462 if (((n = strtol(v, NULL, 10)) < 0) || (n > 255))
463 n = 1;
464 __mp_setoption(MP_OPT_ALLOCBYTE, n);
465 }
466 if ((v = getenv("MALLOC_FREEBYTE")) && (*v != '\0'))
467 {
468 if (((n = strtol(v, NULL, 10)) < 0) || (n > 255))
469 n = 2;
470 __mp_setoption(MP_OPT_FREEBYTE, n);
471 }
472 if ((v = getenv("MALLOC_WARN")) && (*v != '\0'))
473 {
474 a.i = strtol(v, NULL, 10);
475 __mpt_dbmallocoption(MALLOC_WARN, &a);
476 }
477 if ((v = getenv("MALLOC_FATAL")) && (*v != '\0'))
478 {
479 a.i = strtol(v, NULL, 10);
480 __mpt_dbmallocoption(MALLOC_FATAL, &a);
481 }
482 if ((v = getenv("MALLOC_ERRFILE")) && (*v != '\0'))
483 {
484 a.str = v;
485 __mpt_dbmallocoption(MALLOC_ERRFILE, &a);
486 }
487 if ((v = getenv("MALLOC_CKCHAIN")) && (*v != '\0'))
488 {
489 a.i = strtol(v, NULL, 10);
490 __mpt_dbmallocoption(MALLOC_CKCHAIN, &a);
491 }
492 if ((v = getenv("MALLOC_FILLAREA")) && (*v != '\0'))
493 {
494 a.i = strtol(v, NULL, 10);
495 __mpt_dbmallocoption(MALLOC_FILLAREA, &a);
496 }
497 if ((v = getenv("MALLOC_LOWFRAG")) && (*v != '\0'))
498 {
499 a.i = strtol(v, NULL, 10);
500 __mpt_dbmallocoption(MALLOC_LOWFRAG, &a);
501 }
502 if ((v = getenv("MALLOC_CKDATA")) && (*v != '\0'))
503 {
504 a.i = strtol(v, NULL, 10);
505 __mpt_dbmallocoption(MALLOC_CKDATA, &a);
506 }
507 if ((v = getenv("MALLOC_REUSE")) && (*v != '\0'))
508 {
509 a.i = strtol(v, NULL, 10);
510 __mpt_dbmallocoption(MALLOC_REUSE, &a);
511 }
512 if ((v = getenv("MALLOC_SHOWLINKS")) && (*v != '\0'))
513 {
514 a.i = strtol(v, NULL, 10);
515 __mpt_dbmallocoption(MALLOC_SHOWLINKS, &a);
516 }
517 if ((v = getenv("MALLOC_DETAIL")) && (*v != '\0'))
518 {
519 a.i = strtol(v, NULL, 10);
520 __mpt_dbmallocoption(MALLOC_DETAIL, &a);
521 }
522 if ((v = getenv("MALLOC_FREEMARK")) && (*v != '\0'))
523 {
524 a.i = strtol(v, NULL, 10);
525 __mpt_dbmallocoption(MALLOC_FREEMARK, &a);
526 }
527 if ((v = getenv("MALLOC_ZERO")) && (*v != '\0'))
528 {
529 a.i = strtol(v, NULL, 10);
530 __mpt_dbmallocoption(MALLOC_ZERO, &a);
531 }
532 }
533 }
534
535
536 #ifdef __cplusplus
537 }
538 #endif /* __cplusplus */
539