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 profiling output file produced by the mpatrol
25  * library and display the profiling information that was obtained.
26  */
27 
28 
29 #include "tree.h"
30 #include "graph.h"
31 #include "getopt.h"
32 #include "version.h"
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 
38 #if MP_IDENT_SUPPORT
39 #ident "$Id: mprof.c,v 1.34 2002/01/08 20:13:59 graeme Exp $"
40 #else /* MP_IDENT_SUPPORT */
41 static MP_CONST MP_VOLATILE char *mprof_id = "$Id: mprof.c,v 1.34 2002/01/08 20:13:59 graeme Exp $";
42 #endif /* MP_IDENT_SUPPORT */
43 
44 
45 #define PROGVERSION "1.3" /* the current version of this program */
46 
47 
48 /* The flags used to parse the command line options.
49  */
50 
51 typedef enum options_flags
52 {
53     OF_ADDRESSES  = 'a',
54     OF_COUNTS     = 'c',
55     OF_GRAPHFILE  = 'g',
56     OF_HELP       = 'h',
57     OF_LEAKS      = 'l',
58     OF_STACKDEPTH = 'n',
59     OF_VERSION    = 'V',
60     OF_CALLGRAPH  = 'v'
61 }
62 options_flags;
63 
64 
65 /* Structure containing statistics about the counts and totals of all of the
66  * small, medium, large and extra large allocations and deallocations for a
67  * particular call site.
68  */
69 
70 typedef struct profiledata
71 {
72     size_t acount[4]; /* total numbers of allocations */
73     size_t dcount[4]; /* total numbers of deallocations */
74     size_t atotal[4]; /* total numbers of allocated bytes */
75     size_t dtotal[4]; /* total numbers of deallocated bytes */
76 }
77 profiledata;
78 
79 
80 /* Structure containing profiling details for a function in a call stack.
81  */
82 
83 typedef struct profilenode
84 {
85     treenode node;        /* tree node */
86     treenode tnode;       /* temporary tree node */
87     unsigned long parent; /* parent node */
88     void *addr;           /* return address */
89     unsigned long symbol; /* associated symbol */
90     unsigned long name;   /* associated symbol name */
91     unsigned long data;   /* profiling data */
92     profiledata tdata;    /* temporary profiling data */
93     unsigned char flags;  /* temporary flags */
94 }
95 profilenode;
96 
97 
98 /* Structure representing a vertex in the allocation call graph.
99  */
100 
101 typedef struct vertex
102 {
103     treenode node;       /* tree node */
104     graphnode gnode;     /* graph node */
105     profilenode *pnode;  /* profiling node */
106     unsigned long index; /* vertex index */
107 }
108 vertex;
109 
110 
111 /* Structure representing an edge in the allocation call graph.
112  */
113 
114 typedef struct edge
115 {
116     listnode node;       /* list node */
117     graphedge gnode;     /* edge node */
118     profiledata data;    /* profiling data */
119     unsigned char flags; /* temporary flags */
120 }
121 edge;
122 
123 
124 /* The version of the mpatrol library which produced the profiling output file.
125  */
126 
127 static unsigned long version;
128 
129 
130 /* The total number of allocations and deallocations.
131  */
132 
133 static size_t acount, dcount;
134 
135 
136 /* The total bytes of allocations and deallocations.
137  */
138 
139 static size_t atotal, dtotal;
140 
141 
142 /* The allocation and deallocation bins.
143  */
144 
145 static size_t *acounts, *dcounts;
146 
147 
148 /* The total bytes of large allocations and deallocations.
149  */
150 
151 static size_t atotals, dtotals;
152 
153 
154 /* The number of allocation bins.
155  */
156 
157 static size_t binsize;
158 
159 
160 /* The allocations and deallocations for all call sites.
161  */
162 
163 static profiledata *data;
164 
165 
166 /* The number of profiledata structures for all call sites.
167  */
168 
169 static size_t datasize;
170 
171 
172 /* The profiling details for all call sites.
173  */
174 
175 static profilenode *nodes;
176 
177 
178 /* The number of profilenode structures for all call sites.
179  */
180 
181 static size_t nodesize;
182 
183 
184 /* The array containing the symbol addresses.
185  */
186 
187 static void **addrs;
188 
189 
190 /* The string table containing the symbol names.
191  */
192 
193 static char *symbols;
194 
195 
196 /* The small, medium and large allocation boundaries.
197  */
198 
199 static size_t sbound, mbound, lbound;
200 
201 
202 /* The tree containing profiling details for all call sites.
203  */
204 
205 static treeroot proftree;
206 
207 
208 /* The tree containing temporary profiling details for all call sites.
209  */
210 
211 static treeroot temptree;
212 
213 
214 /* The list of edges in the allocation call graph.
215  */
216 
217 static listhead edgelist;
218 
219 
220 /* The allocation call graph.
221  */
222 
223 static graphhead graph;
224 
225 
226 /* The profiling output file produced by mpatrol.
227  */
228 
229 static FILE *proffile;
230 
231 
232 /* The graph specification file optionally produced by mprof.
233  */
234 
235 static FILE *graphfile;
236 
237 
238 /* The filename used to invoke this tool.
239  */
240 
241 static char *progname;
242 
243 
244 /* Indicates if different allocation points within single functions should
245  * be noted when displaying the profiling tables.
246  */
247 
248 static int useaddresses;
249 
250 
251 /* Indicates if the emphasis will be on allocation counts rather than
252  * allocated bytes when displaying the profiling tables.
253  */
254 
255 static int showcounts;
256 
257 
258 /* Indicates if memory leaks rather than memory allocations should be written
259  * to the graph specification file.
260  */
261 
262 static int showleaks;
263 
264 
265 /* Indicates the maximum stack depth to use when comparing function call
266  * stacks for the memory leak table.
267  */
268 
269 static unsigned long maxstack;
270 
271 
272 /* Indicates if the allocation call graph should be displayed.
273  */
274 
275 static int showgraph;
276 
277 
278 /* The table describing all recognised options.
279  */
280 
281 static option options_table[] =
282 {
283     {"addresses", OF_ADDRESSES, NULL,
284      "\tSpecifies that different call sites from within the same function\n"
285      "\tare to be differentiated and that the names of all functions should\n"
286      "\tbe displayed with their call site offset in bytes.\n"},
287     {"call-graph", OF_CALLGRAPH, NULL,
288      "\tSpecifies that the allocation call graph should be displayed.\n"},
289     {"counts", OF_COUNTS, NULL,
290      "\tSpecifies that certain tables should be sorted by the number of\n"
291      "\tallocations or deallocations rather than the total number of bytes\n"
292      "\tallocated or deallocated.\n"},
293     {"graph-file", OF_GRAPHFILE, "file",
294      "\tSpecifies that the allocation call graph should also be written to a\n"
295      "\tgraph specification file for later visualisation with dot.\n"},
296     {"help", OF_HELP, NULL,
297      "\tDisplays this quick-reference option summary.\n"},
298     {"leaks", OF_LEAKS, NULL,
299      "\tSpecifies that memory leaks rather than memory allocations are to be\n"
300      "\twritten to the graph specification file.\n"},
301     {"stack-depth", OF_STACKDEPTH, "depth",
302      "\tSpecifies the maximum stack depth to display and also use when\n"
303      "\tcalculating if one call site has the same call stack as another call\n"
304      "\tsite.\n"},
305     {"version", OF_VERSION, NULL,
306      "\tDisplays the version number of this program.\n"},
307     NULL
308 };
309 
310 
311 /* Clear the statistics for a set of profiling data.
312  */
313 
314 static
315 void
cleardata(profiledata * a)316 cleardata(profiledata *a)
317 {
318     size_t i;
319 
320     for (i = 0; i < 4; i++)
321     {
322         a->acount[i] = 0;
323         a->dcount[i] = 0;
324         a->atotal[i] = 0;
325         a->dtotal[i] = 0;
326     }
327 }
328 
329 
330 /* Sum the statistics from two sets of profiling data.
331  */
332 
333 static
334 void
sumdata(profiledata * a,profiledata * b)335 sumdata(profiledata *a, profiledata *b)
336 {
337     size_t i;
338 
339     for (i = 0; i < 4; i++)
340     {
341         a->acount[i] += b->acount[i];
342         a->dcount[i] += b->dcount[i];
343         a->atotal[i] += b->atotal[i];
344         a->dtotal[i] += b->dtotal[i];
345     }
346 }
347 
348 
349 /* Compare two function call stacks.
350  */
351 
352 static
353 int
comparestack(profilenode * n,profilenode * p,int c)354 comparestack(profilenode *n, profilenode *p, int c)
355 {
356     size_t i;
357 
358     for (i = 1; (c != 0) || (maxstack == 0) || (i < maxstack); i++)
359     {
360         if ((n->parent == 0) || (p->parent == 0))
361             return ((n->parent == 0) && (p->parent == 0));
362         n = &nodes[n->parent - 1];
363         p = &nodes[p->parent - 1];
364         if ((n->addr != p->addr) && (useaddresses || (n->symbol == 0) ||
365              (n->symbol != p->symbol)))
366             return 0;
367     }
368     return 1;
369 }
370 
371 
372 /* Byte-swap a block of memory.
373  */
374 
375 static
376 void
byteswap(void * b,size_t n)377 byteswap(void *b, size_t n)
378 {
379     char *s, *t;
380     char c;
381 
382     s = (char *) b;
383     t = (char *) b + n - 1;
384     while (s < t)
385     {
386         c = *s;
387         *s++ = *t;
388         *t-- = c;
389     }
390 }
391 
392 
393 /* Read an entry from the profiling output file.
394  */
395 
396 static
397 void
getentry(void * d,size_t l,size_t n,int b)398 getentry(void *d, size_t l, size_t n, int b)
399 {
400     size_t i;
401 
402     if (fread(d, l, n, proffile) != n)
403     {
404         fprintf(stderr, "%s: Error reading file\n", progname);
405         exit(EXIT_FAILURE);
406     }
407     /* Byte-swap all of the elements if necessary.
408      */
409     if (b != 0)
410         for (i = 0; i < n; i++)
411         {
412             byteswap(d, l);
413             d = (char *) d + l;
414         }
415 }
416 
417 
418 /* Read all of the data from the profiling output file.
419  */
420 
421 static
422 void
readfile(void)423 readfile(void)
424 {
425     char s[4];
426     profiledata *d;
427     profilenode *p;
428     size_t i;
429     unsigned long n;
430     int b;
431 
432     /* When reading the profiling output file, we assume that if it begins and
433      * ends with the magic sequence of characters then it is a valid profiling
434      * output file from the mpatrol library.  There are probably an infinite
435      * number of checks we could do to ensure that the rest of the data in the
436      * file is valid, but that would be overcomplicated and probably slow this
437      * program down.  However, if the file is only partially written then the
438      * getentry() function will catch the error before we do something silly.
439      */
440     getentry(s, sizeof(char), 4, 0);
441     if (memcmp(s, MP_PROFMAGIC, 4) != 0)
442     {
443         fprintf(stderr, "%s: Invalid file format\n", progname);
444         exit(EXIT_FAILURE);
445     }
446     /* The following test allows us to read profiling output files that were
447      * produced on a different processor architecture.  If the next word in the
448      * file does not contain the value 1 then we have to byte-swap any further
449      * data that we read from the file.  Note that this test only works if the
450      * word size is the same on both machines.
451      */
452     getentry(&i, sizeof(size_t), 1, 0);
453     b = (i != 1);
454     /* Get the version number of the mpatrol library which produced the
455      * profiling output file.  The profiling file format changed to include the
456      * version number at mpatrol 1.3.0 so we can't reliably read files produced
457      * before then.  We also assume that we can't read files produced by later
458      * versions of mpatrol.
459      */
460     getentry(&version, sizeof(unsigned long), 1, b);
461     if (version < 10300)
462     {
463         fprintf(stderr, "%s: Profiling file version too old\n", progname);
464         exit(EXIT_FAILURE);
465     }
466     else if (version / 100 > MP_VERNUM / 100)
467     {
468         fprintf(stderr, "%s: Profiling file version too new\n", progname);
469         exit(EXIT_FAILURE);
470     }
471     getentry(&sbound, sizeof(size_t), 1, b);
472     getentry(&mbound, sizeof(size_t), 1, b);
473     getentry(&lbound, sizeof(size_t), 1, b);
474     /* Read the allocation and deallocation bins.
475      */
476     getentry(&binsize, sizeof(size_t), 1, b);
477     if (binsize > 0)
478     {
479         if (((acounts = (size_t *) malloc(binsize * sizeof(size_t))) == NULL) ||
480             ((dcounts = (size_t *) malloc(binsize * sizeof(size_t))) == NULL))
481         {
482             fprintf(stderr, "%s: Out of memory\n", progname);
483             exit(EXIT_FAILURE);
484         }
485         getentry(acounts, sizeof(size_t), binsize, b);
486         getentry(&atotals, sizeof(size_t), 1, b);
487         getentry(dcounts, sizeof(size_t), binsize, b);
488         getentry(&dtotals, sizeof(size_t), 1, b);
489         for (i = 0; i < binsize; i++)
490         {
491             acount += acounts[i];
492             dcount += dcounts[i];
493             if (i == binsize - 1)
494             {
495                 atotal += atotals;
496                 dtotal += dtotals;
497             }
498             else
499             {
500                 atotal += acounts[i] * (i + 1);
501                 dtotal += dcounts[i] * (i + 1);
502             }
503         }
504     }
505     /* Read the profiling data structures.
506      */
507     getentry(&datasize, sizeof(size_t), 1, b);
508     if (datasize > 0)
509     {
510         if ((data = (profiledata *) malloc(datasize * sizeof(profiledata))) ==
511             NULL)
512         {
513             fprintf(stderr, "%s: Out of memory\n", progname);
514             exit(EXIT_FAILURE);
515         }
516         for (i = 0; i < datasize; i++)
517         {
518             getentry(&n, sizeof(unsigned long), 1, b);
519             d = &data[n - 1];
520             getentry(d->acount, sizeof(size_t), 4, b);
521             getentry(d->atotal, sizeof(size_t), 4, b);
522             getentry(d->dcount, sizeof(size_t), 4, b);
523             getentry(d->dtotal, sizeof(size_t), 4, b);
524         }
525     }
526     /* Read the statistics for every call site.
527      */
528     getentry(&nodesize, sizeof(size_t), 1, b);
529     if (nodesize > 0)
530     {
531         if ((nodes = (profilenode *) malloc(nodesize * sizeof(profilenode))) ==
532             NULL)
533         {
534             fprintf(stderr, "%s: Out of memory\n", progname);
535             exit(EXIT_FAILURE);
536         }
537         for (i = 0; i < nodesize; i++)
538         {
539             getentry(&n, sizeof(unsigned long), 1, b);
540             p = &nodes[n - 1];
541             getentry(&p->parent, sizeof(unsigned long), 1, b);
542             getentry(&p->addr, sizeof(void *), 1, b);
543             getentry(&p->symbol, sizeof(unsigned long), 1, b);
544             getentry(&p->name, sizeof(unsigned long), 1, b);
545             getentry(&p->data, sizeof(unsigned long), 1, b);
546             __mp_treeinsert(&proftree, &p->node, (unsigned long) p->addr);
547             cleardata(&p->tdata);
548             p->flags = 0;
549         }
550     }
551     /* Read the table containing the symbol addresses.
552      */
553     getentry(&i, sizeof(size_t), 1, b);
554     if (i > 0)
555     {
556         if ((addrs = (void **) malloc(i * sizeof(void *))) == NULL)
557         {
558             fprintf(stderr, "%s: Out of memory\n", progname);
559             exit(EXIT_FAILURE);
560         }
561         getentry(addrs, sizeof(void *), i, b);
562     }
563     /* Read the string table containing the symbol names.
564      */
565     getentry(&i, sizeof(size_t), 1, b);
566     if (i > 0)
567     {
568         if ((symbols = (char *) malloc(i * sizeof(char))) == NULL)
569         {
570             fprintf(stderr, "%s: Out of memory\n", progname);
571             exit(EXIT_FAILURE);
572         }
573         getentry(symbols, sizeof(char), i, 0);
574     }
575     getentry(s, sizeof(char), 4, 0);
576     if (memcmp(s, MP_PROFMAGIC, 4) != 0)
577     {
578         fprintf(stderr, "%s: Invalid file format\n", progname);
579         exit(EXIT_FAILURE);
580     }
581 }
582 
583 
584 /* Count the number of decimal digits in a number.
585  */
586 
587 static
588 unsigned char
countdigits(unsigned long n)589 countdigits(unsigned long n)
590 {
591     unsigned char d;
592 
593     for (d = 1; n /= 10; d++);
594     return d;
595 }
596 
597 
598 /* Display a character a specified number of times.
599  */
600 
601 static
602 void
printchar(char c,size_t n)603 printchar(char c, size_t n)
604 {
605     size_t i;
606 
607     for (i = 0; i < n; i++)
608         fputc(c, stdout);
609 }
610 
611 
612 /* Display a set of profiling data.
613  */
614 
615 static
616 void
printdata(size_t * d,size_t t)617 printdata(size_t *d, size_t t)
618 {
619     size_t i;
620     double n;
621 
622     for (i = 0; i < 4; i++)
623         if ((d[i] == 0) || (t == 0))
624             fputs("   ", stdout);
625         else
626         {
627             n = ((double) d[i] / (double) t) * 100.0;
628             if (n <= 0.5)
629                 fputs("  .", stdout);
630             else if (n >= 99.5)
631                 fputs(" %%", stdout);
632             else
633                 fprintf(stdout, " %2.0f", n);
634         }
635 }
636 
637 
638 /* Display the symbol associated with a particular call site.
639  */
640 
641 static
642 void
printsymbol(FILE * f,profilenode * n)643 printsymbol(FILE *f, profilenode *n)
644 {
645     ptrdiff_t o;
646 
647     if (n->name == 0)
648         fprintf(f, MP_POINTER, n->addr);
649     else
650     {
651         fputs(symbols + n->name, f);
652         if (useaddresses && (n->symbol != 0) &&
653             ((o = (char *) n->addr - (char *) addrs[n->symbol - 1]) != 0))
654             fprintf(f, "%+ld", o);
655     }
656 }
657 
658 
659 /* Add a new vertex to the allocation call graph.
660  */
661 
662 static
663 vertex *
addvertex(profilenode * n)664 addvertex(profilenode *n)
665 {
666     vertex *v;
667 
668     if ((v = (vertex *) malloc(sizeof(vertex))) == NULL)
669     {
670         fprintf(stderr, "%s: Out of memory\n", progname);
671         exit(EXIT_FAILURE);
672     }
673     if (useaddresses || (n->symbol == 0))
674         __mp_treeinsert(&temptree, &v->node, (unsigned long) n->addr);
675     else
676         __mp_treeinsert(&temptree, &v->node, n->symbol);
677     __mp_addnode(&graph, &v->gnode);
678     v->pnode = n;
679     v->index = 0;
680     return v;
681 }
682 
683 
684 /* Add a new edge to the allocation call graph.
685  */
686 
687 static
688 edge *
addedge(graphnode * p,graphnode * c)689 addedge(graphnode *p, graphnode *c)
690 {
691     edge *e;
692 
693     if ((e = (edge *) malloc(sizeof(edge))) == NULL)
694     {
695         fprintf(stderr, "%s: Out of memory\n", progname);
696         exit(EXIT_FAILURE);
697     }
698     __mp_addtail(&edgelist, &e->node);
699     __mp_addedge(&graph, &e->gnode, p, c);
700     cleardata(&e->data);
701     e->flags = 0;
702     return e;
703 }
704 
705 
706 /* Build the allocation call graph.
707  */
708 
709 static
710 void
buildgraph(void)711 buildgraph(void)
712 {
713     profilenode *n, *p;
714     vertex *u, *v;
715     edge *e;
716     graphedge *g;
717     profiledata d;
718 
719     for (n = (profilenode *) __mp_minimum(proftree.root); n != NULL;
720          n = (profilenode *) __mp_successor(&n->node))
721         if ((n->data != 0) && !(n->flags & 1))
722         {
723             cleardata(&d);
724             sumdata(&d, &data[n->data - 1]);
725             p = (profilenode *) __mp_successor(&n->node);
726             while ((p != NULL) && ((p->addr == n->addr) || (!useaddresses &&
727                      (p->symbol != 0) && (p->symbol == n->symbol))))
728             {
729                 if ((p->data != 0) && !(p->flags & 1) && comparestack(n, p, 1))
730                 {
731                     sumdata(&d, &data[p->data - 1]);
732                     p->flags |= 1;
733                 }
734                 p = (profilenode *) __mp_successor(&p->node);
735             }
736             p = n;
737             if (useaddresses || (p->symbol == 0))
738                 u = (vertex *) __mp_search(temptree.root,
739                                            (unsigned long) p->addr);
740             else
741                 u = (vertex *) __mp_search(temptree.root, p->symbol);
742             if (u == NULL)
743                 u = addvertex(p);
744             if ((g = __mp_findedge(&graph, &u->gnode, &graph.end)) == NULL)
745                 e = addedge(&u->gnode, &graph.end);
746             else
747                 e = (edge *) ((char *) g - offsetof(edge, gnode));
748             sumdata(&e->data, &d);
749             u->pnode->flags |= 2;
750             for (v = u; p->parent != 0; u = v)
751             {
752                 p = &nodes[p->parent - 1];
753                 if (useaddresses || (p->symbol == 0))
754                     v = (vertex *) __mp_search(temptree.root,
755                                                (unsigned long) p->addr);
756                 else
757                     v = (vertex *) __mp_search(temptree.root, p->symbol);
758                 if (v == NULL)
759                     v = addvertex(p);
760                 if ((g = __mp_findedge(&graph, &v->gnode, &u->gnode)) == NULL)
761                     e = addedge(&v->gnode, &u->gnode);
762                 else
763                     e = (edge *) ((char *) g - offsetof(edge, gnode));
764                 /* Find out if we've been here before.  If we have then we have
765                  * detected a cycle in the call graph and we should not
766                  * contribute anything to the current edge since we have done so
767                  * already.  However, we mark the edge as being part of a cycle
768                  * since it is useful to know this later on.
769                  */
770                 if (v->pnode->flags & 2)
771                     e->flags |= 2;
772                 else
773                 {
774                     sumdata(&e->data, &d);
775                     v->pnode->flags |= 2;
776                 }
777             }
778             if ((g = __mp_findedge(&graph, &graph.start, &v->gnode)) == NULL)
779                 e = addedge(&graph.start, &v->gnode);
780             else
781                 e = (edge *) ((char *) g - offsetof(edge, gnode));
782             sumdata(&e->data, &d);
783             /* Clear all of the flags from the current call stack that we used
784              * to determine cycles in the call graph.
785              */
786             p = n;
787             do
788             {
789                 if (useaddresses || (p->symbol == 0))
790                     v = (vertex *) __mp_search(temptree.root,
791                                                (unsigned long) p->addr);
792                 else
793                     v = (vertex *) __mp_search(temptree.root, p->symbol);
794                 v->pnode->flags &= ~2;
795                 if (p->parent != 0)
796                     p = &nodes[p->parent - 1];
797                 else
798                     p = NULL;
799             }
800             while (p != NULL);
801         }
802     for (n = (profilenode *) __mp_minimum(proftree.root); n != NULL;
803          n = (profilenode *) __mp_successor(&n->node))
804         n->flags = 0;
805 }
806 
807 
808 /* Delete the allocation call graph.
809  */
810 
811 static
812 void
deletegraph(void)813 deletegraph(void)
814 {
815     vertex *n, *v;
816     edge *e, *m;
817 
818     /* Remove the nodes from the graph and free up the memory that they
819      * inhabit.  This is done by traversing the tree rather than the graph
820      * in order to avoid deleting nodes that result in other nodes being
821      * inaccessible.
822      */
823     for (v = (vertex *) __mp_minimum(temptree.root); v != NULL; v = n)
824     {
825         n = (vertex *) __mp_successor(&v->node);
826         __mp_treeremove(&temptree, &v->node);
827         __mp_removenode(&graph, &v->gnode);
828         free(v);
829     }
830     /* Remove the edges from the graph.  The graph is now empty following
831      * the removal of the nodes in the previous loop, so we just need to
832      * free the memory that each edge uses.
833      */
834     for (e = (edge *) edgelist.head; m = (edge *) e->node.next; e = m)
835     {
836         __mp_remove(&edgelist, &e->node);
837         free(e);
838     }
839 }
840 
841 
842 /* Display the allocation bin table.
843  */
844 
845 static
846 void
bintable(void)847 bintable(void)
848 {
849     size_t i;
850     unsigned long a, b, c, d;
851     double e, f, g, h;
852     int p;
853 
854     p = 0;
855     printchar(' ', 32);
856     fputs("ALLOCATION BINS\n\n", stdout);
857     printchar(' ', 29);
858     fprintf(stdout, "(number of bins: %lu)\n\n", binsize);
859     printchar(' ', 21);
860     fputs("allocated", stdout);
861     printchar(' ', 26);
862     fputs("unfreed\n", stdout);
863     printchar(' ', 10);
864     printchar('-', 32);
865     fputs("  ", stdout);
866     printchar('-', 32);
867     fputs("\n    size   count       %     bytes       %   "
868           "count       %     bytes       %\n\n", stdout);
869     for (i = 0; i < binsize; i++)
870         if (acounts[i] != 0)
871         {
872             a = acounts[i];
873             b = a - dcounts[i];
874             if (i == binsize - 1)
875             {
876                 c = atotals;
877                 d = c - dtotals;
878             }
879             else
880             {
881                 c = a * (i + 1);
882                 d = b * (i + 1);
883             }
884             e = ((double) a / (double) acount) * 100.0;
885             if (acount != dcount)
886                 f = ((double) b / (double) (acount - dcount)) * 100.0;
887             else
888                 f = 0.0;
889             g = ((double) c / (double) atotal) * 100.0;
890             if (atotal != dtotal)
891                 h = ((double) d / (double) (atotal - dtotal)) * 100.0;
892             else
893                 h = 0.0;
894             fprintf(stdout, " %s %4lu  %6lu  %6.2f  %8lu  %6.2f  "
895                     "%6lu  %6.2f  %8lu  %6.2f\n",
896                     (i == binsize - 1) ? ">=" : "  ",
897                     i + 1, a, e, c, g, b, f, d, h);
898             p = 1;
899         }
900     if (p == 1)
901         fputc('\n', stdout);
902     fprintf(stdout, "   total  %6lu          %8lu          "
903             "%6lu          %8lu\n", acount, atotal,
904             acount - dcount, atotal - dtotal);
905 }
906 
907 
908 /* Display the direct allocation table.
909  */
910 
911 static
912 void
directtable(void)913 directtable(void)
914 {
915     profiledata *d;
916     profilenode *n, *p;
917     treenode *t;
918     profiledata m;
919     size_t i;
920     unsigned long a, b, c;
921     double e, f;
922 
923     cleardata(&m);
924     printchar(' ', 31);
925     fputs("DIRECT ALLOCATIONS\n\n", stdout);
926     printchar(' ', 20);
927     fprintf(stdout, "(0 < s <= %lu < m <= %lu < l <= %lu < x)\n\n",
928             sbound, mbound, lbound);
929     if (showcounts)
930     {
931         printchar(' ', 9);
932         fputs("allocated", stdout);
933         printchar(' ', 21);
934         fputs("unfreed\n", stdout);
935         printchar('-', 27);
936         fputs("  ", stdout);
937         printchar('-', 27);
938         fputs("\n count       %   s  m  l  x   "
939               "count       %   s  m  l  x     bytes  function\n\n", stdout);
940     }
941     else
942     {
943         printchar(' ', 10);
944         fputs("allocated", stdout);
945         printchar(' ', 23);
946         fputs("unfreed\n", stdout);
947         printchar('-', 29);
948         fputs("  ", stdout);
949         printchar('-', 29);
950         fputs("\n   bytes       %   s  m  l  x     "
951               "bytes       %   s  m  l  x   count  function\n\n", stdout);
952     }
953     for (n = (profilenode *) __mp_minimum(proftree.root); n != NULL; n = p)
954     {
955         p = (profilenode *) __mp_successor(&n->node);
956         if (n->data != 0)
957         {
958             d = &n->tdata;
959             sumdata(d, &data[n->data - 1]);
960             while ((p != NULL) && ((p->addr == n->addr) || (!useaddresses &&
961                      (p->symbol != 0) && (p->symbol == n->symbol))))
962             {
963                 if (p->data != 0)
964                     sumdata(d, &data[p->data - 1]);
965                 p = (profilenode *) __mp_successor(&p->node);
966             }
967             a = 0;
968             for (i = 0; i < 4; i++)
969                 if (showcounts)
970                     a += d->acount[i];
971                 else
972                     a += d->atotal[i];
973             __mp_treeinsert(&temptree, &n->tnode, a);
974             sumdata(&m, d);
975         }
976     }
977     for (t = __mp_maximum(temptree.root); t != NULL; t = __mp_predecessor(t))
978     {
979         n = (profilenode *) ((char *) t - offsetof(profilenode, tnode));
980         d = &n->tdata;
981         a = t->key;
982         b = c = 0;
983         for (i = 0; i < 4; i++)
984         {
985             if (showcounts)
986             {
987                 b += d->dcount[i];
988                 c += d->atotal[i];
989             }
990             else
991             {
992                 b += d->dtotal[i];
993                 c += d->acount[i];
994             }
995             d->dcount[i] = d->acount[i] - d->dcount[i];
996             d->dtotal[i] = d->atotal[i] - d->dtotal[i];
997         }
998         b = a - b;
999         if (showcounts)
1000         {
1001             e = ((double) a / (double) acount) * 100.0;
1002             if (acount != dcount)
1003                 f = ((double) b / (double) (acount - dcount)) * 100.0;
1004             else
1005                 f = 0.0;
1006             fprintf(stdout, "%6lu  %6.2f ", a, e);
1007             printdata(d->acount, acount);
1008             fprintf(stdout, "  %6lu  %6.2f ", b, f);
1009             printdata(d->dcount, acount - dcount);
1010             fprintf(stdout, "  %8lu  ", c);
1011         }
1012         else
1013         {
1014             e = ((double) a / (double) atotal) * 100.0;
1015             if (atotal != dtotal)
1016                 f = ((double) b / (double) (atotal - dtotal)) * 100.0;
1017             else
1018                 f = 0.0;
1019             fprintf(stdout, "%8lu  %6.2f ", a, e);
1020             printdata(d->atotal, atotal);
1021             fprintf(stdout, "  %8lu  %6.2f ", b, f);
1022             printdata(d->dtotal, atotal - dtotal);
1023             fprintf(stdout, "  %6lu  ", c);
1024         }
1025         printsymbol(stdout, n);
1026         fputc('\n', stdout);
1027         cleardata(d);
1028     }
1029     for (i = 0; i < 4; i++)
1030     {
1031         m.dcount[i] = m.acount[i] - m.dcount[i];
1032         m.dtotal[i] = m.atotal[i] - m.dtotal[i];
1033     }
1034     if (temptree.size != 0)
1035         fputc('\n', stdout);
1036     if (showcounts)
1037     {
1038         fprintf(stdout, "%6lu         ", acount);
1039         printdata(m.acount, acount);
1040         fprintf(stdout, "  %6lu         ", acount - dcount);
1041         printdata(m.dcount, acount - dcount);
1042         fprintf(stdout, "  %8lu  total\n", atotal);
1043     }
1044     else
1045     {
1046         fprintf(stdout, "%8lu         ", atotal);
1047         printdata(m.atotal, atotal);
1048         fprintf(stdout, "  %8lu         ", atotal - dtotal);
1049         printdata(m.dtotal, atotal - dtotal);
1050         fprintf(stdout, "  %6lu  total\n", acount);
1051     }
1052     __mp_newtree(&temptree);
1053 }
1054 
1055 
1056 /* Display the memory leak table.
1057  */
1058 
1059 static
1060 void
leaktable(void)1061 leaktable(void)
1062 {
1063     profiledata *d;
1064     profilenode *n, *p;
1065     treenode *t;
1066     size_t i;
1067     unsigned long a, b, j, k;
1068     double e, f, g;
1069 
1070     printchar(' ', 34);
1071     fputs("MEMORY LEAKS\n\n", stdout);
1072     printchar(' ', 28);
1073     fprintf(stdout, "(maximum stack depth: %lu)\n\n", maxstack);
1074     printchar(' ', 16);
1075     fputs("unfreed", stdout);
1076     printchar(' ', 22);
1077     fputs("allocated\n", stdout);
1078     printchar('-', 40);
1079     fputs("  ", stdout);
1080     printchar('-', 16);
1081     if (showcounts)
1082         fputs("\n     %   count       %     bytes       %   "
1083               "count     bytes  function\n\n", stdout);
1084     else
1085         fputs("\n     %     bytes       %   count       %     "
1086               "bytes   count  function\n\n", stdout);
1087     for (n = (profilenode *) __mp_minimum(proftree.root); n != NULL; n = p)
1088     {
1089         p = (profilenode *) __mp_successor(&n->node);
1090         if ((n->data != 0) && !n->flags)
1091         {
1092             d = &n->tdata;
1093             sumdata(d, &data[n->data - 1]);
1094             while ((p != NULL) && ((p->addr == n->addr) || (!useaddresses &&
1095                      (p->symbol != 0) && (p->symbol == n->symbol))))
1096             {
1097                 if ((p->data != 0) && !p->flags && comparestack(n, p, 0))
1098                 {
1099                     sumdata(d, &data[p->data - 1]);
1100                     p->flags = 1;
1101                 }
1102                 p = (profilenode *) __mp_successor(&p->node);
1103             }
1104             p = (profilenode *) __mp_successor(&n->node);
1105             a = 0;
1106             for (i = 0; i < 4; i++)
1107                 if (showcounts)
1108                     a += d->acount[i] - d->dcount[i];
1109                 else
1110                     a += d->atotal[i] - d->dtotal[i];
1111             if (a > 0)
1112                 __mp_treeinsert(&temptree, &n->tnode, a);
1113         }
1114     }
1115     for (n = (profilenode *) __mp_minimum(proftree.root); n != NULL;
1116          n = (profilenode *) __mp_successor(&n->node))
1117         n->flags = 0;
1118     for (t = __mp_maximum(temptree.root); t != NULL; t = __mp_predecessor(t))
1119     {
1120         n = (profilenode *) ((char *) t - offsetof(profilenode, tnode));
1121         d = &n->tdata;
1122         a = t->key;
1123         b = j = k = 0;
1124         for (i = 0; i < 4; i++)
1125             if (showcounts)
1126             {
1127                 b += d->dtotal[i];
1128                 j += d->acount[i];
1129                 k += d->atotal[i];
1130             }
1131             else
1132             {
1133                 b += d->dcount[i];
1134                 j += d->atotal[i];
1135                 k += d->acount[i];
1136             }
1137         b = k - b;
1138         e = ((double) a / (double) j) * 100.0;
1139         f = ((double) b / (double) k) * 100.0;
1140         if (showcounts)
1141         {
1142             g = ((double) a / (double) (acount - dcount)) * 100.0;
1143             fprintf(stdout, "%6.2f  %6lu  %6.2f  %8lu  %6.2f  %6lu  %8lu  ",
1144                     g, a, e, b, f, j, k);
1145         }
1146         else
1147         {
1148             g = ((double) a / (double) (atotal - dtotal)) * 100.0;
1149             fprintf(stdout, "%6.2f  %8lu  %6.2f  %6lu  %6.2f  %8lu  %6lu  ",
1150                     g, a, e, b, f, j, k);
1151         }
1152         printsymbol(stdout, n);
1153         fputc('\n', stdout);
1154         p = n;
1155         for (i = 1; (maxstack == 0) || (i < maxstack); i++)
1156         {
1157             if (p->parent == 0)
1158                 break;
1159             p = &nodes[p->parent - 1];
1160             printchar(' ', 60);
1161             printsymbol(stdout, p);
1162             fputc('\n', stdout);
1163         }
1164         cleardata(d);
1165     }
1166     if (acount != 0)
1167         e = ((double) (acount - dcount) / (double) acount) * 100.0;
1168     else
1169         e = 0.0;
1170     if (atotal != 0)
1171         f = ((double) (atotal - dtotal) / (double) atotal) * 100.0;
1172     else
1173         f = 0.0;
1174     if (temptree.size != 0)
1175         fputc('\n', stdout);
1176     if (showcounts)
1177         fprintf(stdout, "        %6lu  %6.2f  %8lu  %6.2f  %6lu  %8lu  total\n",
1178                 acount - dcount, e, atotal - dtotal, f, acount, atotal);
1179     else
1180         fprintf(stdout, "        %8lu  %6.2f  %6lu  %6.2f  %8lu  %6lu  total\n",
1181                 atotal - dtotal, f, acount - dcount, e, atotal, acount);
1182     __mp_newtree(&temptree);
1183 }
1184 
1185 
1186 /* Display the allocation call graph.
1187  */
1188 
1189 static
1190 void
callgraph(void)1191 callgraph(void)
1192 {
1193     size_t d[4];
1194     listnode *l;
1195     vertex *u, *v;
1196     edge *e;
1197     graphedge *g;
1198     size_t i, s, t;
1199 
1200     i = 1;
1201     printchar(' ', 29);
1202     fputs("ALLOCATION CALL GRAPH\n\n", stdout);
1203     printchar(' ', 28);
1204     fprintf(stdout, "(number of vertices: %lu)\n\n", temptree.size);
1205     if (showcounts)
1206     {
1207         printchar(' ', 10);
1208         fputs("allocated", stdout);
1209         printchar(' ', 13);
1210         fputs("unfreed\n", stdout);
1211         printchar(' ', 5);
1212         printchar('-', 19);
1213         fputs("  ", stdout);
1214         printchar('-', 19);
1215         fputs("\nindex count   s  m  l  x   count   s  m  l  x  function\n",
1216               stdout);
1217     }
1218     else
1219     {
1220         printchar(' ', 11);
1221         fputs("allocated", stdout);
1222         printchar(' ', 15);
1223         fputs("unfreed\n", stdout);
1224         printchar(' ', 5);
1225         printchar('-', 21);
1226         fputs("  ", stdout);
1227         printchar('-', 21);
1228         fputs("\nindex   bytes   s  m  l  x     bytes   s  m  l  x  function\n",
1229               stdout);
1230     }
1231     /* Compute the index for each vertex in the graph.  This is currently
1232      * done after the graph has been completely constructed so that the
1233      * ordering can be done by code address.  It has to be done before we
1234      * start displaying the graph since each vertex will contain forward
1235      * references to its parents and children.
1236      */
1237     for (v = (vertex *) __mp_minimum(temptree.root); v != NULL;
1238          v = (vertex *) __mp_successor(&v->node))
1239         v->index = i++;
1240     for (v = (vertex *) __mp_minimum(temptree.root); v != NULL;
1241          v = (vertex *) __mp_successor(&v->node))
1242     {
1243         if (showcounts)
1244             printchar('-', 45);
1245         else
1246             printchar('-', 49);
1247         fputc('\n', stdout);
1248         /* Calculate the details of the parents.
1249          */
1250         for (l = v->gnode.parents.head; l->next != NULL; l = l->next)
1251         {
1252             /* The following three lines are a bit excessive and result from
1253              * using class-based programming in C.  These conversions would be
1254              * automatically calculated by the compiler if we were using C++
1255              * classes.
1256              */
1257             g = (graphedge *) ((char *) l - offsetof(graphedge, pnode));
1258             e = (edge *) ((char *) g - offsetof(edge, gnode));
1259             u = (vertex *) ((char *) e->gnode.parent - offsetof(vertex, gnode));
1260             if (&u->gnode != &graph.start)
1261             {
1262                 for (i = s = t = 0; i < 4; i++)
1263                 {
1264                     if (showcounts)
1265                     {
1266                         t += e->data.acount[i];
1267                         d[i] = e->data.acount[i] - e->data.dcount[i];
1268                     }
1269                     else
1270                     {
1271                         t += e->data.atotal[i];
1272                         d[i] = e->data.atotal[i] - e->data.dtotal[i];
1273                     }
1274                     s += d[i];
1275                 }
1276                 if (e->flags & 2)
1277                     fputs(" (*) ", stdout);
1278                 else
1279                     printchar(' ', 5);
1280                 if (showcounts)
1281                 {
1282                     fprintf(stdout, "%6lu ", t);
1283                     printdata(e->data.acount, t);
1284                     fprintf(stdout, "  %6lu ", s);
1285                 }
1286                 else
1287                 {
1288                     fprintf(stdout, "%8lu ", t);
1289                     printdata(e->data.atotal, t);
1290                     fprintf(stdout, "  %8lu ", s);
1291                 }
1292                 printdata(d, s);
1293                 printchar(' ', 6);
1294                 printsymbol(stdout, u->pnode);
1295                 fprintf(stdout, " [%lu]\n", u->index);
1296             }
1297         }
1298         fprintf(stdout, "[%lu] ", v->index);
1299         if (showcounts)
1300             printchar(' ', 44 - countdigits(v->index));
1301         else
1302             printchar(' ', 48 - countdigits(v->index));
1303         printsymbol(stdout, v->pnode);
1304         fprintf(stdout, " [%lu]\n", v->index);
1305         /* Calculate the details of the children.
1306          */
1307         for (l = v->gnode.children.head; l->next != NULL; l = l->next)
1308         {
1309             /* The following three lines are a bit excessive and result from
1310              * using class-based programming in C.  These conversions would be
1311              * automatically calculated by the compiler if we were using C++
1312              * classes.
1313              */
1314             g = (graphedge *) ((char *) l - offsetof(graphedge, cnode));
1315             e = (edge *) ((char *) g - offsetof(edge, gnode));
1316             u = (vertex *) ((char *) e->gnode.child - offsetof(vertex, gnode));
1317             if (&u->gnode != &graph.end)
1318             {
1319                 for (i = s = t = 0; i < 4; i++)
1320                 {
1321                     if (showcounts)
1322                     {
1323                         t += e->data.acount[i];
1324                         d[i] = e->data.acount[i] - e->data.dcount[i];
1325                     }
1326                     else
1327                     {
1328                         t += e->data.atotal[i];
1329                         d[i] = e->data.atotal[i] - e->data.dtotal[i];
1330                     }
1331                     s += d[i];
1332                 }
1333                 if (e->flags & 2)
1334                     fputs(" (*) ", stdout);
1335                 else
1336                     printchar(' ', 5);
1337                 if (showcounts)
1338                 {
1339                     fprintf(stdout, "%6lu ", t);
1340                     printdata(e->data.acount, t);
1341                     fprintf(stdout, "  %6lu ", s);
1342                 }
1343                 else
1344                 {
1345                     fprintf(stdout, "%8lu ", t);
1346                     printdata(e->data.atotal, t);
1347                     fprintf(stdout, "  %8lu ", s);
1348                 }
1349                 printdata(d, s);
1350                 printchar(' ', 6);
1351                 printsymbol(stdout, u->pnode);
1352                 fprintf(stdout, " [%lu]\n", u->index);
1353             }
1354         }
1355     }
1356 }
1357 
1358 
1359 /* Write out the graph specification file in dot format.
1360  */
1361 
1362 static
1363 void
writegraph(profilenode * p,graphnode * n)1364 writegraph(profilenode *p, graphnode *n)
1365 {
1366     listnode *l;
1367     vertex *v;
1368     edge *e;
1369     graphedge *g;
1370     size_t i, t;
1371 
1372     for (l = n->children.head; l->next != NULL; l = l->next)
1373     {
1374         /* The following three lines are a bit excessive and result from
1375          * using class-based programming in C.  These conversions would be
1376          * automatically calculated by the compiler if we were using C++
1377          * classes.
1378          */
1379         g = (graphedge *) ((char *) l - offsetof(graphedge, cnode));
1380         e = (edge *) ((char *) g - offsetof(edge, gnode));
1381         v = (vertex *) ((char *) e->gnode.child - offsetof(vertex, gnode));
1382         if (!(e->flags & 1))
1383         {
1384             e->flags |= 1;
1385             for (i = t = 0; i < 4; i++)
1386                 if (showcounts)
1387                 {
1388                     t += e->data.acount[i];
1389                     if (showleaks)
1390                         t -= e->data.dcount[i];
1391                 }
1392                 else
1393                 {
1394                     t += e->data.atotal[i];
1395                     if (showleaks)
1396                         t -= e->data.dtotal[i];
1397                 }
1398             if ((t > 0) || !showleaks)
1399             {
1400                 fputs("    \"", graphfile);
1401                 if (p == NULL)
1402                     fputs("START", graphfile);
1403                 else
1404                     printsymbol(graphfile, p);
1405                 fputs("\" -> \"", graphfile);
1406                 if (&v->gnode == &graph.end)
1407                     fputs("ALLOC", graphfile);
1408                 else
1409                     printsymbol(graphfile, v->pnode);
1410                 fprintf(graphfile, "\" [label = \"%lu\"", t);
1411                 if (e->flags & 2)
1412                     fputs(", style = dotted", graphfile);
1413                 fputs("];\n", graphfile);
1414             }
1415             writegraph(v->pnode, &v->gnode);
1416         }
1417     }
1418     if (p == NULL)
1419         for (e = (edge *) edgelist.head; e->node.next != NULL;
1420              e = (edge *) e->node.next)
1421             e->flags &= ~1;
1422 }
1423 
1424 
1425 /* Read the profiling output file and display all specified information.
1426  */
1427 
1428 int
main(int argc,char ** argv)1429 main(int argc, char **argv)
1430 {
1431     char b[256];
1432     char *f, *g;
1433     int c, e, h, r, v;
1434 
1435     g = NULL;
1436     e = h = v = 0;
1437     r = EXIT_SUCCESS;
1438     maxstack = 1;
1439     progname = __mp_basename(argv[0]);
1440     while ((c = __mp_getopt(argc, argv, __mp_shortopts(b, options_table),
1441              options_table)) != EOF)
1442         switch (c)
1443         {
1444           case OF_ADDRESSES:
1445             useaddresses = 1;
1446             break;
1447           case OF_CALLGRAPH:
1448             showgraph = 1;
1449             break;
1450           case OF_COUNTS:
1451             showcounts = 1;
1452             break;
1453           case OF_GRAPHFILE:
1454             g = __mp_optarg;
1455             break;
1456           case OF_HELP:
1457             h = 1;
1458             break;
1459           case OF_LEAKS:
1460             showleaks = 1;
1461             break;
1462           case OF_STACKDEPTH:
1463             if (!__mp_getnum(progname, __mp_optarg, (long *) &maxstack, 1))
1464                 e = 1;
1465             break;
1466           case OF_VERSION:
1467             v = 1;
1468             break;
1469           default:
1470             e = 1;
1471             break;
1472         }
1473     argc -= __mp_optindex;
1474     argv += __mp_optindex;
1475     if (v == 1)
1476     {
1477         fprintf(stdout, "%s %s\n%s %s\n\n", progname, PROGVERSION,
1478                 __mp_copyright, __mp_author);
1479         fputs("This is free software, and you are welcome to redistribute it "
1480               "under certain\n", stdout);
1481         fputs("conditions; see the GNU Library General Public License for "
1482               "details.\n\n", stdout);
1483         fputs("For the latest mpatrol release and documentation,\n", stdout);
1484         fprintf(stdout, "visit %s.\n\n", __mp_homepage);
1485     }
1486     if (argc > 1)
1487         e = 1;
1488     if ((e == 1) || (h == 1))
1489     {
1490         fprintf(stdout, "Usage: %s [options] [file]\n\n", progname);
1491         if (h == 0)
1492             fprintf(stdout, "Type `%s --help' for a complete list of "
1493                     "options.\n", progname);
1494         else
1495             __mp_showopts(options_table);
1496         if (e == 1)
1497             exit(EXIT_FAILURE);
1498         exit(EXIT_SUCCESS);
1499     }
1500     if (argc == 1)
1501         f = argv[0];
1502     else
1503         f = MP_PROFFILE;
1504     acount = dcount = 0;
1505     atotal = dtotal = 0;
1506     acounts = dcounts = NULL;
1507     atotals = dtotals = 0;
1508     binsize = 0;
1509     data = NULL;
1510     datasize = 0;
1511     nodes = NULL;
1512     nodesize = 0;
1513     addrs = NULL;
1514     symbols = NULL;
1515     sbound = mbound = lbound = 0;
1516     __mp_newtree(&proftree);
1517     __mp_newtree(&temptree);
1518     __mp_newlist(&edgelist);
1519     __mp_newgraph(&graph);
1520     if (strcmp(f, "-") == 0)
1521         proffile = stdin;
1522     else if ((proffile = fopen(f, "rb")) == NULL)
1523     {
1524         fprintf(stderr, "%s: Cannot open file `%s'\n", progname, f);
1525         exit(EXIT_FAILURE);
1526     }
1527     readfile();
1528     fclose(proffile);
1529     bintable();
1530     fputs("\n\n", stdout);
1531     directtable();
1532     fputs("\n\n", stdout);
1533     leaktable();
1534     /* The reason that the allocation call graph is not used for the direct
1535      * allocation and memory leak tables is that the code to build and display
1536      * the allocation call graph was added much later.  Rather than convert
1537      * these tables to use the new call graph, I decided to keep the code that
1538      * already worked and only use the call graph for any new tables.
1539      */
1540     buildgraph();
1541     if (showgraph)
1542     {
1543         fputs("\n\n", stdout);
1544         callgraph();
1545     }
1546     if (g != NULL)
1547     {
1548         if (strcmp(g, "stdout") == 0)
1549             graphfile = stdout;
1550         else if (strcmp(g, "stderr") == 0)
1551             graphfile = stderr;
1552         else if ((graphfile = fopen(g, "w")) == NULL)
1553         {
1554             fprintf(stderr, "%s: Cannot open file `%s'\n", progname, g);
1555             r = EXIT_FAILURE;
1556         }
1557         if (r == EXIT_SUCCESS)
1558         {
1559             fprintf(graphfile, "/* produced by %s %s from %s */\n\n", progname,
1560                     PROGVERSION, f);
1561             if (showleaks)
1562                 fputs("digraph \"memory leak call graph\"\n{\n", graphfile);
1563             else
1564                 fputs("digraph \"allocation call graph\"\n{\n", graphfile);
1565             writegraph(NULL, &graph.start);
1566             fputs("}\n", graphfile);
1567             if ((graphfile != stdout) && (graphfile != stderr))
1568                 fclose(graphfile);
1569         }
1570     }
1571     deletegraph();
1572     if (acounts != NULL)
1573         free(acounts);
1574     if (dcounts != NULL)
1575         free(dcounts);
1576     if (data != NULL)
1577         free(data);
1578     if (nodes != NULL)
1579         free(nodes);
1580     if (addrs != NULL)
1581         free(addrs);
1582     if (symbols != NULL)
1583         free(symbols);
1584     return r;
1585 }
1586