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  * A tool designed to read a log file produced by the mpatrol library
25  * and report any unfreed memory allocations.  This should be used if
26  * the mpatrol library could not finish writing the log file due to
27  * abnormal program termination, but note that some of the unfreed
28  * allocations might have been freed if the program had terminated
29  * successfully.  Also note that no attempt is made to account for
30  * resizing of memory allocations and so the total amount of memory
31  * used by the resulting unfreed allocations may not be entirely accurate.
32  */
33 
34 
35 #include "tree.h"
36 #include "getopt.h"
37 #include "version.h"
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 
43 #if MP_IDENT_SUPPORT
44 #ident "$Id: mleak.c,v 1.16 2002/01/08 20:13:59 graeme Exp $"
45 #else /* MP_IDENT_SUPPORT */
46 static MP_CONST MP_VOLATILE char *mleak_id = "$Id: mleak.c,v 1.16 2002/01/08 20:13:59 graeme Exp $";
47 #endif /* MP_IDENT_SUPPORT */
48 
49 
50 #define PROGVERSION "1.3" /* the current version of this program */
51 
52 
53 /* The flags used to parse the command line options.
54  */
55 
56 typedef enum options_flags
57 {
58     OF_HELP     = 'h',
59     OF_IGNORE   = 'i',
60     OF_MAXSTACK = 'n',
61     OF_VERSION  = 'V'
62 }
63 options_flags;
64 
65 
66 /* Structure containing the allocation details and log file offset for a
67  * single memory allocation.
68  */
69 
70 typedef struct allocation
71 {
72     treenode node;        /* tree node */
73     unsigned long addr;   /* allocation address */
74     unsigned long size;   /* allocation size */
75     unsigned long offset; /* log file offset */
76 }
77 allocation;
78 
79 
80 /* The tree containing information about each memory allocation.
81  */
82 
83 static treeroot alloctree;
84 
85 
86 /* The total number of bytes currently allocated.
87  */
88 
89 static unsigned long alloctotal;
90 
91 
92 /* The log file produced by mpatrol.
93  */
94 
95 static FILE *logfile;
96 
97 
98 /* The current offset in the log file.
99  */
100 
101 static long fileoffset;
102 
103 
104 /* The filename used to invoke this tool.
105  */
106 
107 static char *progname;
108 
109 
110 /* Indicates that the unfreed memory allocation list in the log file, if it
111  * exists, should be ignored.
112  */
113 
114 static int ignorelist;
115 
116 
117 /* Indicates the maximum stack depth to display.
118  */
119 
120 static unsigned long maxstack;
121 
122 
123 /* The table describing all recognised options.
124  */
125 
126 static option options_table[] =
127 {
128     {"help", OF_HELP, NULL,
129      "\tDisplays this quick-reference option summary.\n"},
130     {"ignore", OF_IGNORE, NULL,
131      "\tSpecifies that the list of unfreed allocations in the log file\n"
132      "\tshould be ignored.\n"},
133     {"max-stack", OF_MAXSTACK, "depth",
134      "\tSpecifies the maximum stack depth to display.\n"},
135     {"version", OF_VERSION, NULL,
136      "\tDisplays the version number of this program.\n"},
137     NULL
138 };
139 
140 
141 /* Create a new memory allocation and record its log file offset.
142  */
143 
144 static
145 void
newalloc(unsigned long i,unsigned long a,unsigned long l,unsigned long o)146 newalloc(unsigned long i, unsigned long a, unsigned long l, unsigned long o)
147 {
148     allocation *n;
149 
150     if ((n = (allocation *) malloc(sizeof(allocation))) == NULL)
151     {
152         fprintf(stderr, "%s: Out of memory\n", progname);
153         exit(EXIT_FAILURE);
154     }
155     __mp_treeinsert(&alloctree, &n->node, i);
156     n->addr = a;
157     n->size = l;
158     n->offset = o;
159     alloctotal += l;
160 }
161 
162 
163 /* Free an existing memory allocation.
164  */
165 
166 static
167 void
freealloc(unsigned long i)168 freealloc(unsigned long i)
169 {
170     allocation *n;
171 
172     if (n = (allocation *) __mp_search(alloctree.root, i))
173     {
174         __mp_treeremove(&alloctree, &n->node);
175         alloctotal -= n->size;
176         free(n);
177     }
178 }
179 
180 
181 /* Read an input line from the log file.
182  */
183 
184 static
185 char *
getnextline(void)186 getnextline(void)
187 {
188     static char s[MP_BUFFER_SIZE + 1];
189     unsigned long i;
190     int c;
191 
192     i = 0;
193     /* Record the file offset so that we can go back to this position during
194      * the second pass of the log file.
195      */
196     if ((fileoffset = ftell(logfile)) == -1)
197     {
198         fprintf(stderr, "%s: Cannot determine file position\n", progname);
199         exit(EXIT_FAILURE);
200     }
201     while (((c = fgetc(logfile)) != EOF) && (c != '\n'))
202     {
203         if (i == MP_BUFFER_SIZE)
204         {
205             fprintf(stderr, "%s: Buffer overflow\n", progname);
206             exit(EXIT_FAILURE);
207         }
208         s[i++] = c;
209     }
210     if (c == EOF)
211         return NULL;
212     s[i] = '\0';
213     return s;
214 }
215 
216 
217 /* Log the allocations and deallocations from the log file.
218  */
219 
220 static
221 void
readfile(void)222 readfile(void)
223 {
224     char *s, *t;
225     unsigned long a, l, n, o;
226 
227     while (s = getnextline())
228         if (strncmp(s, "ALLOC: ", 7) == 0)
229         {
230             /* Parse relevant details from the memory allocation and
231              * add the allocation to the allocation tree.
232              */
233             o = fileoffset;
234             if ((s = strchr(s + 7, '(')) && (t = strchr(s + 1, ',')))
235             {
236                 /* Get the allocation index.
237                  */
238                 *t = '\0';
239                 n = strtoul(s + 1, NULL, 0);
240                 if ((*(s = t + 1) == ' ') && (t = strchr(s + 1, ' ')))
241                 {
242                     /* Get the allocation size.
243                      */
244                     *t = '\0';
245                     l = strtoul(s + 1, NULL, 0);
246                     /* Don't record the allocation if the pointer returned is
247                      * NULL.
248                      */
249                     while ((s = getnextline()) &&
250                            (strncmp(s, "returns ", 8) != 0));
251                     if ((n != 0) && (s != NULL) &&
252                         (a = strtoul(s + 8, NULL, 0)))
253                         newalloc(n, a, l, o);
254                 }
255             }
256         }
257         else if (strncmp(s, "FREE: ", 6) == 0)
258         {
259             /* Parse relevant details from the memory deallocation and
260              * remove the allocation from the allocation tree.
261              */
262             if ((s = strchr(s + 6, '(')) && (t = strchr(s + 1, ')')))
263             {
264                 /* Get the allocation address.
265                  */
266                 *t = '\0';
267                 if (a = strtoul(s + 1, NULL, 0))
268                 {
269                     while ((s = getnextline()) && (*s != '\0'));
270                     /* Don't record the deallocation if a warning or error
271                      * occurred.
272                      */
273                     if ((s = getnextline()) && (strncmp(s, "    ", 4) == 0) &&
274                         (s = strchr(s + 4, ':')) && (t = strchr(s + 1, ':')))
275                     {
276                         /* Get the allocation index.
277                          */
278                         *t = '\0';
279                         n = strtoul(s + 1, NULL, 0);
280                         freealloc(n);
281                     }
282                 }
283             }
284         }
285         else if (!ignorelist && (strncmp(s, "unfreed allocations: ", 21) == 0))
286             /* If we get here then there is already a list of unfreed memory
287              * allocations in the log file.  In this case we just parse them
288              * anyway, adding any new entries to the allocation tree.
289              */
290             while (s = getnextline())
291             {
292                 /* Parse relevant details from the unfreed allocation and
293                  * add the allocation to the allocation tree.
294                  */
295                 o = fileoffset;
296                 if ((strncmp(s, "    ", 4) == 0) && (t = strchr(s + 4, ' ')))
297                 {
298                     /* Get the allocation address.
299                      */
300                     *t = '\0';
301                     if ((a = strtoul(s + 4, NULL, 0)) &&
302                         (*(s = t + 1) == '(') && (t = strchr(s + 1, ' ')))
303                     {
304                         /* Get the allocation size.
305                          */
306                         *t = '\0';
307                         l = strtoul(s + 1, NULL, 0);
308                         if ((s = strchr(t + 1, ':')) &&
309                             (t = strchr(s + 1, ':')))
310                         {
311                             /* Get the allocation index.
312                              */
313                             *t = '\0';
314                             n = strtoul(s + 1, NULL, 0);
315                             if (!__mp_search(alloctree.root, n))
316                                 newalloc(n, a, l, o);
317                         }
318                     }
319                 }
320                 while ((s = getnextline()) && (*s != '\0'));
321             }
322 }
323 
324 
325 /* Display all remaining memory allocations.
326  */
327 
328 static
329 void
printallocs(void)330 printallocs(void)
331 {
332     allocation *n, *p;
333     char *r, *s, *t;
334     size_t i;
335 
336     printf("unfreed allocations: %lu (%lu byte%s)\n", alloctree.size,
337            alloctotal, (alloctotal == 1) ? "" : "s");
338     for (n = (allocation *) __mp_minimum(alloctree.root); n != NULL; n = p)
339     {
340         p = (allocation *) __mp_successor(&n->node);
341         /* Move to the position in the log file that records the original
342          * allocation.
343          */
344         if (fseek(logfile, n->offset, SEEK_SET) == -1)
345         {
346             fprintf(stderr, "%s: Cannot set file position\n", progname);
347             exit(EXIT_FAILURE);
348         }
349         /* Extract the relevant information from the allocation log so that
350          * we can format it in the same way as that displayed for the
351          * SHOWUNFREED option.
352          */
353         if (s = getnextline())
354             if ((strncmp(s, "ALLOC: ", 7) == 0) &&
355                 (t = strchr(s + 7, '(')) && (t > s) && (*(t = t - 1) == ' '))
356             {
357                 *t = '\0';
358                 r = s + 7;
359                 if (s = strchr(t + 2, '['))
360                 {
361                     i = 0;
362                     printf("    " MP_POINTER " (%lu byte%s) {%s:%lu:0} %s\n",
363                            n->addr, n->size, (n->size == 1) ? "" : "s", r,
364                            n->node.key, s);
365                     while ((s = getnextline()) && (*s != '\0'))
366                         if (i++ < maxstack)
367                             puts(s);
368                     if ((alloctree.size > 1) && (maxstack != 0))
369                         putchar('\n');
370                 }
371             }
372             else
373             {
374                 i = 0;
375                 puts(s);
376                 while ((s = getnextline()) && (*s != '\0'))
377                     if (i++ < maxstack)
378                         puts(s);
379                 if ((alloctree.size > 1) && (maxstack != 0))
380                     putchar('\n');
381             }
382         __mp_treeremove(&alloctree, &n->node);
383         free(n);
384     }
385 }
386 
387 
388 /* Read the log file and display all unfreed memory allocations.
389  */
390 
391 int
main(int argc,char ** argv)392 main(int argc, char **argv)
393 {
394     char b[256];
395     char *f;
396     int c, e, h, v;
397 
398     e = h = v = 0;
399     maxstack = ~0;
400     progname = __mp_basename(argv[0]);
401     while ((c = __mp_getopt(argc, argv, __mp_shortopts(b, options_table),
402              options_table)) != EOF)
403         switch (c)
404         {
405           case OF_HELP:
406             h = 1;
407             break;
408           case OF_IGNORE:
409             ignorelist = 1;
410             break;
411           case OF_MAXSTACK:
412             if (!__mp_getnum(progname, __mp_optarg, (long *) &maxstack, 1))
413                 e = 1;
414             break;
415           case OF_VERSION:
416             v = 1;
417             break;
418           default:
419             e = 1;
420             break;
421         }
422     argc -= __mp_optindex;
423     argv += __mp_optindex;
424     if (v == 1)
425     {
426         fprintf(stdout, "%s %s\n%s %s\n\n", progname, PROGVERSION,
427                 __mp_copyright, __mp_author);
428         fputs("This is free software, and you are welcome to redistribute it "
429               "under certain\n", stdout);
430         fputs("conditions; see the GNU Library General Public License for "
431               "details.\n\n", stdout);
432         fputs("For the latest mpatrol release and documentation,\n", stdout);
433         fprintf(stdout, "visit %s.\n\n", __mp_homepage);
434     }
435     if (argc > 1)
436         e = 1;
437     if ((e == 1) || (h == 1))
438     {
439         fprintf(stdout, "Usage: %s [options] [file]\n\n", progname);
440         if (h == 0)
441             fprintf(stdout, "Type `%s --help' for a complete list of "
442                     "options.\n", progname);
443         else
444             __mp_showopts(options_table);
445         if (e == 1)
446             exit(EXIT_FAILURE);
447         exit(EXIT_SUCCESS);
448     }
449     if (argc == 1)
450         f = argv[0];
451     else
452         f = MP_LOGFILE;
453     __mp_newtree(&alloctree);
454     alloctotal = 0;
455     if (strcmp(f, "-") == 0)
456         logfile = stdin;
457     else if ((logfile = fopen(f, "r")) == NULL)
458     {
459         fprintf(stderr, "%s: Cannot open file `%s'\n", progname, f);
460         exit(EXIT_FAILURE);
461     }
462     fileoffset = 0;
463     readfile();
464     printallocs();
465     fclose(logfile);
466     return EXIT_SUCCESS;
467 }
468