Save all modification
[mozilla-1.9/m8.git] / tools / trace-malloc / bloatblame.c
blob0c129f7d241dce2706b5df3b817727e2bb0e5939
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is nsTraceMalloc.c/bloatblame.c code, released
17 * April 19, 2000.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2000
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
25 * Brendan Eich, 14-April-2000
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <errno.h>
45 #ifdef HAVE_GETOPT_H
46 #include <getopt.h>
47 #else
48 #ifdef XP_WIN
49 #include "getopt.c"
50 #else
51 extern int getopt(int argc, char *const *argv, const char *shortopts);
52 extern char *optarg;
53 extern int optind;
54 #endif
55 #endif
56 #include <math.h>
57 #include <time.h>
58 #include <sys/stat.h>
59 #include "prtypes.h"
60 #include "prlog.h"
61 #include "prprf.h"
62 #include "plhash.h"
63 #include "pldhash.h"
64 #include "nsTraceMalloc.h"
65 #include "tmreader.h"
67 static char *program;
68 static int sort_by_direct = 0;
69 static int js_mode = 0;
70 static int do_tree_dump = 0;
71 static int unified_output = 0;
72 static char *function_dump = NULL;
73 static uint32 min_subtotal = 0;
75 static void compute_callsite_totals(tmcallsite *site)
77 tmcallsite *kid;
79 site->allocs.bytes.total += site->allocs.bytes.direct;
80 site->allocs.calls.total += site->allocs.calls.direct;
81 for (kid = site->kids; kid; kid = kid->siblings) {
82 compute_callsite_totals(kid);
83 site->allocs.bytes.total += kid->allocs.bytes.total;
84 site->allocs.calls.total += kid->allocs.calls.total;
88 static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp)
90 tmcallsite *parent;
91 tmgraphnode *comp, *pcomp, *lib, *plib;
92 tmmethodnode *meth, *pmeth;
93 int old_meth_low, old_comp_low, old_lib_low, nkids;
94 tmcallsite *kid;
96 parent = site->parent;
97 comp = lib = NULL;
98 meth = NULL;
99 if (parent) {
100 meth = site->method;
101 if (meth) {
102 pmeth = parent->method;
103 if (pmeth && pmeth != meth) {
104 if (!meth->graphnode.low) {
105 meth->graphnode.allocs.bytes.total += site->allocs.bytes.total;
106 meth->graphnode.allocs.calls.total += site->allocs.calls.total;
108 if (!tmgraphnode_connect(&(pmeth->graphnode), &(meth->graphnode), site))
109 goto bad;
111 comp = meth->graphnode.up;
112 if (comp) {
113 pcomp = pmeth->graphnode.up;
114 if (pcomp && pcomp != comp) {
115 if (!comp->low) {
116 comp->allocs.bytes.total
117 += site->allocs.bytes.total;
118 comp->allocs.calls.total
119 += site->allocs.calls.total;
121 if (!tmgraphnode_connect(pcomp, comp, site))
122 goto bad;
124 lib = comp->up;
125 if (lib) {
126 plib = pcomp->up;
127 if (plib && plib != lib) {
128 if (!lib->low) {
129 lib->allocs.bytes.total
130 += site->allocs.bytes.total;
131 lib->allocs.calls.total
132 += site->allocs.calls.total;
134 if (!tmgraphnode_connect(plib, lib, site))
135 goto bad;
137 old_lib_low = lib->low;
138 if (!old_lib_low)
139 lib->low = level;
142 old_comp_low = comp->low;
143 if (!old_comp_low)
144 comp->low = level;
147 old_meth_low = meth->graphnode.low;
148 if (!old_meth_low)
149 meth->graphnode.low = level;
153 if (do_tree_dump) {
154 fprintf(fp, "%c%*s%3d %3d %s %lu %ld\n",
155 site->kids ? '+' : '-', level, "", level, kidnum,
156 meth ? tmmethodnode_name(meth) : "???",
157 (unsigned long)site->allocs.bytes.direct,
158 (long)site->allocs.bytes.total);
160 nkids = 0;
161 level++;
162 for (kid = site->kids; kid; kid = kid->siblings) {
163 walk_callsite_tree(kid, level, nkids, fp);
164 nkids++;
167 if (meth) {
168 if (!old_meth_low)
169 meth->graphnode.low = 0;
170 if (comp) {
171 if (!old_comp_low)
172 comp->low = 0;
173 if (lib) {
174 if (!old_lib_low)
175 lib->low = 0;
179 return;
181 bad:
182 perror(program);
183 exit(1);
187 * Linked list bubble-sort (waterson and brendan went bald hacking this).
189 * Sort the list in non-increasing order, using the expression passed as the
190 * 'lessthan' formal macro parameter. This expression should use 'curr' as
191 * the pointer to the current node (of type nodetype) and 'next' as the next
192 * node pointer. It should return true if curr is less than next, and false
193 * otherwise.
195 #define BUBBLE_SORT_LINKED_LIST(listp, nodetype, lessthan) \
196 PR_BEGIN_MACRO \
197 nodetype *curr, **currp, *next, **nextp, *tmp; \
199 currp = listp; \
200 while ((curr = *currp) != NULL && curr->next) { \
201 nextp = &curr->next; \
202 while ((next = *nextp) != NULL) { \
203 if (lessthan) { \
204 tmp = curr->next; \
205 *currp = tmp; \
206 if (tmp == next) { \
207 PR_ASSERT(nextp == &curr->next); \
208 curr->next = next->next; \
209 next->next = curr; \
210 } else { \
211 *nextp = next->next; \
212 curr->next = next->next; \
213 next->next = tmp; \
214 *currp = next; \
215 *nextp = curr; \
216 nextp = &curr->next; \
218 curr = next; \
219 continue; \
221 nextp = &next->next; \
223 currp = &curr->next; \
225 PR_END_MACRO
227 static PRIntn tabulate_node(PLHashEntry *he, PRIntn i, void *arg)
229 tmgraphnode *node = (tmgraphnode*) he;
230 tmgraphnode **table = (tmgraphnode**) arg;
232 table[i] = node;
233 BUBBLE_SORT_LINKED_LIST(&node->down, tmgraphnode,
234 (curr->allocs.bytes.total < next->allocs.bytes.total));
235 return HT_ENUMERATE_NEXT;
238 /* Sort in reverse size order, so biggest node comes first. */
239 static int node_table_compare(const void *p1, const void *p2)
241 const tmgraphnode *node1, *node2;
242 uint32 key1, key2;
244 node1 = *(const tmgraphnode**) p1;
245 node2 = *(const tmgraphnode**) p2;
246 if (sort_by_direct) {
247 key1 = node1->allocs.bytes.direct;
248 key2 = node2->allocs.bytes.direct;
249 } else {
250 key1 = node1->allocs.bytes.total;
251 key2 = node2->allocs.bytes.total;
253 return (key2 < key1) ? -1 : (key2 > key1) ? 1 : 0;
256 static int mean_size_compare(const void *p1, const void *p2)
258 const tmgraphnode *node1, *node2;
259 double div1, div2, key1, key2;
261 node1 = *(const tmgraphnode**) p1;
262 node2 = *(const tmgraphnode**) p2;
263 div1 = (double)node1->allocs.calls.direct;
264 div2 = (double)node2->allocs.calls.direct;
265 if (div1 == 0 || div2 == 0)
266 return (int)(div2 - div1);
267 key1 = (double)node1->allocs.bytes.direct / div1;
268 key2 = (double)node2->allocs.bytes.direct / div2;
269 if (key1 < key2)
270 return 1;
271 if (key1 > key2)
272 return -1;
273 return 0;
276 static const char *prettybig(uint32 num, char *buf, size_t limit)
278 if (num >= 1000000000)
279 PR_snprintf(buf, limit, "%1.2fG", (double) num / 1e9);
280 else if (num >= 1000000)
281 PR_snprintf(buf, limit, "%1.2fM", (double) num / 1e6);
282 else if (num >= 1000)
283 PR_snprintf(buf, limit, "%1.2fK", (double) num / 1e3);
284 else
285 PR_snprintf(buf, limit, "%lu", (unsigned long) num);
286 return buf;
289 static double percent(uint32 num, uint32 total)
291 if (num == 0)
292 return 0.0;
293 return ((double) num * 100) / (double) total;
296 static void sort_graphlink_list(tmgraphlink **listp, int which)
298 BUBBLE_SORT_LINKED_LIST(listp, tmgraphlink,
299 (TM_LINK_TO_EDGE(curr, which)->allocs.bytes.total
300 < TM_LINK_TO_EDGE(next, which)->allocs.bytes.total));
303 static void dump_graphlink_list(tmgraphlink *list, int which, const char *name,
304 FILE *fp)
306 tmcounts bytes;
307 tmgraphlink *link;
308 tmgraphedge *edge;
309 char buf[16];
311 bytes.direct = bytes.total = 0;
312 for (link = list; link; link = link->next) {
313 edge = TM_LINK_TO_EDGE(link, which);
314 bytes.direct += edge->allocs.bytes.direct;
315 bytes.total += edge->allocs.bytes.total;
318 if (js_mode) {
319 fprintf(fp,
320 " %s:{dbytes:%ld, tbytes:%ld, edges:[\n",
321 name, (long) bytes.direct, (long) bytes.total);
322 for (link = list; link; link = link->next) {
323 edge = TM_LINK_TO_EDGE(link, which);
324 fprintf(fp,
325 " {node:%d, dbytes:%ld, tbytes:%ld},\n",
326 link->node->sort,
327 (long) edge->allocs.bytes.direct,
328 (long) edge->allocs.bytes.total);
330 fputs(" ]},\n", fp);
331 } else {
332 fputs("<td valign=top>", fp);
333 for (link = list; link; link = link->next) {
334 edge = TM_LINK_TO_EDGE(link, which);
335 fprintf(fp,
336 "<a href='#%s'>%s&nbsp;(%1.2f%%)</a>\n",
337 tmgraphnode_name(link->node),
338 prettybig(edge->allocs.bytes.total, buf, sizeof buf),
339 percent(edge->allocs.bytes.total, bytes.total));
341 fputs("</td>", fp);
345 static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname,
346 const char *title, FILE *fp)
348 uint32 i, count;
349 tmgraphnode **table, *node;
350 char *name;
351 size_t namelen;
352 char buf1[16], buf2[16], buf3[16], buf4[16];
353 static char NA[] = "N/A";
355 count = hashtbl->nentries;
356 table = (tmgraphnode**) malloc(count * sizeof(tmgraphnode*));
357 if (!table) {
358 perror(program);
359 exit(1);
361 PL_HashTableEnumerateEntries(hashtbl, tabulate_node, table);
362 qsort(table, count, sizeof(tmgraphnode*), node_table_compare);
363 for (i = 0; i < count; i++)
364 table[i]->sort = i;
366 if (js_mode) {
367 fprintf(fp,
368 "var %s = {\n name:'%s', title:'%s', nodes:[\n",
369 varname, varname, title);
370 } else {
371 fprintf(fp,
372 "<table border=1>\n"
373 "<tr>"
374 "<th>%s</th>"
375 "<th>Down</th>"
376 "<th>Next</th>"
377 "<th>Total/Direct (percents)</th>"
378 "<th>Allocations</th>"
379 "<th>Fan-in</th>"
380 "<th>Fan-out</th>"
381 "</tr>\n",
382 title);
385 for (i = 0; i < count; i++) {
386 /* Don't bother with truly puny nodes. */
387 node = table[i];
388 if (node->allocs.bytes.total < min_subtotal)
389 break;
391 name = tmgraphnode_name(node);
392 if (js_mode) {
393 fprintf(fp,
394 " {name:'%s', dbytes:%ld, tbytes:%ld,"
395 " dallocs:%ld, tallocs:%ld,\n",
396 name,
397 (long) node->allocs.bytes.direct,
398 (long) node->allocs.bytes.total,
399 (long) node->allocs.calls.direct,
400 (long) node->allocs.calls.total);
401 } else {
402 namelen = strlen(name);
403 fprintf(fp,
404 "<tr>"
405 "<td valign=top><a name='%s'>%.*s%s</a></td>",
406 name,
407 (namelen > 40) ? 40 : (int)namelen, name,
408 (namelen > 40) ? "<i>...</i>" : "");
409 if (node->down) {
410 fprintf(fp,
411 "<td valign=top><a href='#%s'><i>down</i></a></td>",
412 tmgraphnode_name(node->down));
413 } else {
414 fputs("<td></td>", fp);
416 if (node->next) {
417 fprintf(fp,
418 "<td valign=top><a href='#%s'><i>next</i></a></td>",
419 tmgraphnode_name(node->next));
420 } else {
421 fputs("<td></td>", fp);
423 fprintf(fp,
424 "<td valign=top>%s/%s (%1.2f%%/%1.2f%%)</td>"
425 "<td valign=top>%s/%s (%1.2f%%/%1.2f%%)</td>",
426 prettybig(node->allocs.bytes.total, buf1, sizeof buf1),
427 prettybig(node->allocs.bytes.direct, buf2, sizeof buf2),
428 percent(node->allocs.bytes.total,
429 tmr->calltree_root.allocs.bytes.total),
430 percent(node->allocs.bytes.direct,
431 tmr->calltree_root.allocs.bytes.total),
432 prettybig(node->allocs.calls.total, buf3, sizeof buf3),
433 prettybig(node->allocs.calls.direct, buf4, sizeof buf4),
434 percent(node->allocs.calls.total,
435 tmr->calltree_root.allocs.calls.total),
436 percent(node->allocs.calls.direct,
437 tmr->calltree_root.allocs.calls.total));
440 /* NB: we must use 'fin' because 'in' is a JS keyword! */
441 sort_graphlink_list(&node->in, TM_EDGE_IN_LINK);
442 dump_graphlink_list(node->in, TM_EDGE_IN_LINK, "fin", fp);
443 sort_graphlink_list(&node->out, TM_EDGE_OUT_LINK);
444 dump_graphlink_list(node->out, TM_EDGE_OUT_LINK, "out", fp);
446 if (js_mode)
447 fputs(" },\n", fp);
448 else
449 fputs("</tr>\n", fp);
452 if (js_mode) {
453 fputs("]};\n", fp);
454 } else {
455 fputs("</table>\n<hr>\n", fp);
457 qsort(table, count, sizeof(tmgraphnode*), mean_size_compare);
459 fprintf(fp,
460 "<table border=1>\n"
461 "<tr><th colspan=4>Direct Allocators</th></tr>\n"
462 "<tr>"
463 "<th>%s</th>"
464 "<th>Mean&nbsp;Size</th>"
465 "<th>StdDev</th>"
466 "<th>Allocations<th>"
467 "</tr>\n",
468 title);
470 for (i = 0; i < count; i++) {
471 double allocs, bytes, mean, variance, sigma;
473 node = table[i];
474 allocs = (double)node->allocs.calls.direct;
475 if (!allocs)
476 continue;
478 /* Compute direct-size mean and standard deviation. */
479 bytes = (double)node->allocs.bytes.direct;
480 mean = bytes / allocs;
481 variance = allocs * node->sqsum - bytes * bytes;
482 if (variance < 0 || allocs == 1)
483 variance = 0;
484 else
485 variance /= allocs * (allocs - 1);
486 sigma = sqrt(variance);
488 name = tmgraphnode_name(node);
489 namelen = strlen(name);
490 fprintf(fp,
491 "<tr>"
492 "<td valign=top>%.*s%s</td>"
493 "<td valign=top>%s</td>"
494 "<td valign=top>%s</td>"
495 "<td valign=top>%s</td>"
496 "</tr>\n",
497 (namelen > 65) ? 45 : (int)namelen, name,
498 (namelen > 65) ? "<i>...</i>" : "",
499 prettybig((uint32)mean, buf1, sizeof buf1),
500 prettybig((uint32)sigma, buf2, sizeof buf2),
501 prettybig(node->allocs.calls.direct, buf3, sizeof buf3));
503 fputs("</table>\n", fp);
506 free((void*) table);
509 static void my_tmevent_handler(tmreader *tmr, tmevent *event)
511 switch (event->type) {
512 case TM_EVENT_STATS:
513 if (js_mode)
514 break;
515 fprintf(stdout,
516 "<p><table border=1>"
517 "<tr><th>Counter</th><th>Value</th></tr>\n"
518 "<tr><td>maximum actual stack depth</td><td align=right>%lu</td></tr>\n"
519 "<tr><td>maximum callsite tree depth</td><td align=right>%lu</td></tr>\n"
520 "<tr><td>number of parent callsites</td><td align=right>%lu</td></tr>\n"
521 "<tr><td>maximum kids per parent</td><td align=right>%lu</td></tr>\n"
522 "<tr><td>hits looking for a kid</td><td align=right>%lu</td></tr>\n"
523 "<tr><td>misses looking for a kid</td><td align=right>%lu</td></tr>\n"
524 "<tr><td>steps over other kids</td><td align=right>%lu</td></tr>\n"
525 "<tr><td>callsite recurrences</td><td align=right>%lu</td></tr>\n"
526 "<tr><td>number of stack backtraces</td><td align=right>%lu</td></tr>\n"
527 "<tr><td>backtrace failures</td><td align=right>%lu</td></tr>\n"
528 "<tr><td>backtrace malloc failures</td><td align=right>%lu</td></tr>\n"
529 "<tr><td>backtrace dladdr failures</td><td align=right>%lu</td></tr>\n"
530 "<tr><td>malloc calls</td><td align=right>%lu</td></tr>\n"
531 "<tr><td>malloc failures</td><td align=right>%lu</td></tr>\n"
532 "<tr><td>calloc calls</td><td align=right>%lu</td></tr>\n"
533 "<tr><td>calloc failures</td><td align=right>%lu</td></tr>\n"
534 "<tr><td>realloc calls</td><td align=right>%lu</td></tr>\n"
535 "<tr><td>realloc failures</td><td align=right>%lu</td></tr>\n"
536 "<tr><td>free calls</td><td align=right>%lu</td></tr>\n"
537 "<tr><td>free(null) calls</td><td align=right>%lu</td></tr>\n"
538 "</table>",
539 (unsigned long) event->u.stats.tmstats.calltree_maxstack,
540 (unsigned long) event->u.stats.tmstats.calltree_maxdepth,
541 (unsigned long) event->u.stats.tmstats.calltree_parents,
542 (unsigned long) event->u.stats.tmstats.calltree_maxkids,
543 (unsigned long) event->u.stats.tmstats.calltree_kidhits,
544 (unsigned long) event->u.stats.tmstats.calltree_kidmisses,
545 (unsigned long) event->u.stats.tmstats.calltree_kidsteps,
546 (unsigned long) event->u.stats.tmstats.callsite_recurrences,
547 (unsigned long) event->u.stats.tmstats.backtrace_calls,
548 (unsigned long) event->u.stats.tmstats.backtrace_failures,
549 (unsigned long) event->u.stats.tmstats.btmalloc_failures,
550 (unsigned long) event->u.stats.tmstats.dladdr_failures,
551 (unsigned long) event->u.stats.tmstats.malloc_calls,
552 (unsigned long) event->u.stats.tmstats.malloc_failures,
553 (unsigned long) event->u.stats.tmstats.calloc_calls,
554 (unsigned long) event->u.stats.tmstats.calloc_failures,
555 (unsigned long) event->u.stats.tmstats.realloc_calls,
556 (unsigned long) event->u.stats.tmstats.realloc_failures,
557 (unsigned long) event->u.stats.tmstats.free_calls,
558 (unsigned long) event->u.stats.tmstats.null_free_calls);
560 if (event->u.stats.calltree_maxkids_parent) {
561 tmcallsite *site =
562 tmreader_callsite(tmr, event->u.stats.calltree_maxkids_parent);
563 if (site && site->method) {
564 fprintf(stdout, "<p>callsite with the most kids: %s</p>",
565 tmmethodnode_name(site->method));
569 if (event->u.stats.calltree_maxstack_top) {
570 tmcallsite *site =
571 tmreader_callsite(tmr, event->u.stats.calltree_maxstack_top);
572 fputs("<p>deepest callsite tree path:\n"
573 "<table border=1>\n"
574 "<tr><th>Method</th><th>Offset</th></tr>\n",
575 stdout);
576 while (site) {
577 fprintf(stdout,
578 "<tr><td>%s</td><td>0x%08lX</td></tr>\n",
579 site->method ? tmmethodnode_name(site->method) : "???",
580 (unsigned long) site->offset);
581 site = site->parent;
583 fputs("</table>\n<hr>\n", stdout);
585 break;
589 int main(int argc, char **argv)
591 int c, i, j, rv;
592 tmreader *tmr;
593 FILE *fp;
595 program = *argv;
596 tmr = tmreader_new(program, NULL);
597 if (!tmr) {
598 perror(program);
599 exit(1);
602 while ((c = getopt(argc, argv, "djtuf:m:")) != EOF) {
603 switch (c) {
604 case 'd':
605 sort_by_direct = 1;
606 break;
607 case 'j':
608 js_mode = 1;
609 break;
610 case 't':
611 do_tree_dump = 1;
612 break;
613 case 'u':
614 unified_output = 1;
615 break;
616 case 'f':
617 function_dump = optarg;
618 break;
619 case 'm':
620 min_subtotal = atoi(optarg);
621 break;
622 default:
623 fprintf(stderr,
624 "usage: %s [-dtu] [-f function-dump-filename] [-m min] [output.html]\n",
625 program);
626 exit(2);
630 if (!js_mode) {
631 time_t start = time(NULL);
633 fprintf(stdout,
634 "<script language=\"JavaScript\">\n"
635 "function onload() {\n"
636 " document.links[0].__proto__.onmouseover = new Function("
637 "\"window.status ="
638 " this.href.substring(this.href.lastIndexOf('#') + 1)\");\n"
639 "}\n"
640 "</script>\n");
641 fprintf(stdout, "%s starting at %s", program, ctime(&start));
642 fflush(stdout);
645 argc -= optind;
646 argv += optind;
647 if (argc == 0) {
648 if (tmreader_eventloop(tmr, "-", my_tmevent_handler) <= 0)
649 exit(1);
650 } else {
651 for (i = j = 0; i < argc; i++) {
652 fp = fopen(argv[i], "r");
653 if (!fp) {
654 fprintf(stderr, "%s: can't open %s: %s\n",
655 program, argv[i], strerror(errno));
656 exit(1);
658 rv = tmreader_eventloop(tmr, argv[i], my_tmevent_handler);
659 if (rv < 0)
660 exit(1);
661 if (rv > 0)
662 j++;
663 fclose(fp);
665 if (j == 0)
666 exit(1);
669 compute_callsite_totals(&tmr->calltree_root);
670 walk_callsite_tree(&tmr->calltree_root, 0, 0, stdout);
672 if (js_mode) {
673 fprintf(stdout,
674 "<script language='javascript'>\n"
675 "// direct and total byte and allocator-call counts\n"
676 "var dbytes = %ld, tbytes = %ld,"
677 " dallocs = %ld, tallocs = %ld;\n",
678 (long) tmr->calltree_root.allocs.bytes.direct,
679 (long) tmr->calltree_root.allocs.bytes.total,
680 (long) tmr->calltree_root.allocs.calls.direct,
681 (long) tmr->calltree_root.allocs.calls.total);
684 dump_graph(tmr, tmr->libraries, "libraries", "Library", stdout);
685 if (!js_mode)
686 fputs("<hr>\n", stdout);
688 dump_graph(tmr, tmr->components, "classes", "Class or Component", stdout);
689 if (js_mode || unified_output || function_dump) {
690 if (js_mode || unified_output || strcmp(function_dump, "-") == 0) {
691 fp = stdout;
692 if (!js_mode)
693 fputs("<hr>\n", fp);
694 } else {
695 struct stat sb, fsb;
697 fstat(fileno(stdout), &sb);
698 if (stat(function_dump, &fsb) == 0 &&
699 fsb.st_dev == sb.st_dev && fsb.st_ino == sb.st_ino) {
700 fp = stdout;
701 fputs("<hr>\n", fp);
702 } else {
703 fp = fopen(function_dump, "w");
704 if (!fp) {
705 fprintf(stderr, "%s: can't open %s: %s\n",
706 program, function_dump, strerror(errno));
707 exit(1);
712 dump_graph(tmr, tmr->methods, "methods", "Function or Method", fp);
713 if (fp != stdout)
714 fclose(fp);
716 if (js_mode) {
717 fputs("function viewnode(graph, index) {\n"
718 " view.location = viewsrc();\n"
719 "}\n"
720 "function viewnodelink(graph, index) {\n"
721 " var node = graph.nodes[index];\n"
722 " return '<a href=\"javascript:viewnode('"
723 " + graph.name.quote() + ', ' + node.sort"
724 " + ')\" onmouseover=' + node.name.quote() + '>'"
725 " + node.name + '</a>';\n"
726 "}\n"
727 "function search(expr) {\n"
728 " var re = new RegExp(expr);\n"
729 " var src = '';\n"
730 " var graphs = [libraries, classes, methods]\n"
731 " var nodes;\n"
732 " for (var n = 0; n < (nodes = graphs[n].nodes).length; n++) {\n"
733 " for (var i = 0; i < nodes.length; i++) {\n"
734 " if (re.test(nodes[i].name))\n"
735 " src += viewnodelink(graph, i) + '\\n';\n"
736 " }\n"
737 " }\n"
738 " view.location = viewsrc();\n"
739 "}\n"
740 "function ctrlsrc() {\n"
741 " return \"<form>\\n"
742 "search: <input size=40 onchange='search(this.value)'>\\n"
743 "</form>\\n\";\n"
744 "}\n"
745 "function viewsrc() {\n"
746 " return 'hiiiii'\n"
747 "}\n"
748 "</script>\n"
749 "<frameset rows='10%,*'>\n"
750 " <frame name='ctrl' src='javascript:top.ctrlsrc()'>\n"
751 " <frame name='view' src='javascript:top.viewsrc()'>\n"
752 "</frameset>\n",
753 stdout);
757 exit(0);