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