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