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