1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/trace-malloc/bloatblame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,722 @@ 1.4 +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 +#include <stdio.h> 1.10 +#include <stdlib.h> 1.11 +#include <string.h> 1.12 +#include <ctype.h> 1.13 +#include <errno.h> 1.14 +#ifdef HAVE_GETOPT_H 1.15 +#include <getopt.h> 1.16 +#else 1.17 +#ifdef XP_WIN 1.18 +#include "getopt.c" 1.19 +#else 1.20 +extern int getopt(int argc, char *const *argv, const char *shortopts); 1.21 +extern char *optarg; 1.22 +extern int optind; 1.23 +#endif 1.24 +#endif 1.25 +#include <math.h> 1.26 +#include <time.h> 1.27 +#include <sys/stat.h> 1.28 +#include "prlog.h" 1.29 +#include "prprf.h" 1.30 +#include "plhash.h" 1.31 +#include "pldhash.h" 1.32 +#include "nsTraceMalloc.h" 1.33 +#include "tmreader.h" 1.34 + 1.35 +static char *program; 1.36 +static int sort_by_direct = 0; 1.37 +static int js_mode = 0; 1.38 +static int do_tree_dump = 0; 1.39 +static int unified_output = 0; 1.40 +static char *function_dump = NULL; 1.41 +static uint32_t min_subtotal = 0; 1.42 + 1.43 +static void compute_callsite_totals(tmcallsite *site) 1.44 +{ 1.45 + tmcallsite *kid; 1.46 + 1.47 + site->allocs.bytes.total += site->allocs.bytes.direct; 1.48 + site->allocs.calls.total += site->allocs.calls.direct; 1.49 + for (kid = site->kids; kid; kid = kid->siblings) { 1.50 + compute_callsite_totals(kid); 1.51 + site->allocs.bytes.total += kid->allocs.bytes.total; 1.52 + site->allocs.calls.total += kid->allocs.calls.total; 1.53 + } 1.54 +} 1.55 + 1.56 +static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp) 1.57 +{ 1.58 + tmcallsite *parent; 1.59 + tmgraphnode *comp, *pcomp, *lib, *plib; 1.60 + tmmethodnode *meth, *pmeth; 1.61 + int old_meth_low, old_comp_low, old_lib_low, nkids; 1.62 + tmcallsite *kid; 1.63 + 1.64 + parent = site->parent; 1.65 + comp = lib = NULL; 1.66 + meth = NULL; 1.67 + if (parent) { 1.68 + meth = site->method; 1.69 + if (meth) { 1.70 + pmeth = parent->method; 1.71 + if (pmeth && pmeth != meth) { 1.72 + if (!meth->graphnode.low) { 1.73 + meth->graphnode.allocs.bytes.total += site->allocs.bytes.total; 1.74 + meth->graphnode.allocs.calls.total += site->allocs.calls.total; 1.75 + } 1.76 + if (!tmgraphnode_connect(&(pmeth->graphnode), &(meth->graphnode), site)) 1.77 + goto bad; 1.78 + 1.79 + comp = meth->graphnode.up; 1.80 + if (comp) { 1.81 + pcomp = pmeth->graphnode.up; 1.82 + if (pcomp && pcomp != comp) { 1.83 + if (!comp->low) { 1.84 + comp->allocs.bytes.total 1.85 + += site->allocs.bytes.total; 1.86 + comp->allocs.calls.total 1.87 + += site->allocs.calls.total; 1.88 + } 1.89 + if (!tmgraphnode_connect(pcomp, comp, site)) 1.90 + goto bad; 1.91 + 1.92 + lib = comp->up; 1.93 + if (lib) { 1.94 + plib = pcomp->up; 1.95 + if (plib && plib != lib) { 1.96 + if (!lib->low) { 1.97 + lib->allocs.bytes.total 1.98 + += site->allocs.bytes.total; 1.99 + lib->allocs.calls.total 1.100 + += site->allocs.calls.total; 1.101 + } 1.102 + if (!tmgraphnode_connect(plib, lib, site)) 1.103 + goto bad; 1.104 + } 1.105 + old_lib_low = lib->low; 1.106 + if (!old_lib_low) 1.107 + lib->low = level; 1.108 + } 1.109 + } 1.110 + old_comp_low = comp->low; 1.111 + if (!old_comp_low) 1.112 + comp->low = level; 1.113 + } 1.114 + } 1.115 + old_meth_low = meth->graphnode.low; 1.116 + if (!old_meth_low) 1.117 + meth->graphnode.low = level; 1.118 + } 1.119 + } 1.120 + 1.121 + if (do_tree_dump) { 1.122 + fprintf(fp, "%c%*s%3d %3d %s %lu %ld\n", 1.123 + site->kids ? '+' : '-', level, "", level, kidnum, 1.124 + meth ? tmmethodnode_name(meth) : "???", 1.125 + (unsigned long)site->allocs.bytes.direct, 1.126 + (long)site->allocs.bytes.total); 1.127 + } 1.128 + nkids = 0; 1.129 + level++; 1.130 + for (kid = site->kids; kid; kid = kid->siblings) { 1.131 + walk_callsite_tree(kid, level, nkids, fp); 1.132 + nkids++; 1.133 + } 1.134 + 1.135 + if (meth) { 1.136 + if (!old_meth_low) 1.137 + meth->graphnode.low = 0; 1.138 + if (comp) { 1.139 + if (!old_comp_low) 1.140 + comp->low = 0; 1.141 + if (lib) { 1.142 + if (!old_lib_low) 1.143 + lib->low = 0; 1.144 + } 1.145 + } 1.146 + } 1.147 + return; 1.148 + 1.149 +bad: 1.150 + perror(program); 1.151 + exit(1); 1.152 +} 1.153 + 1.154 +/* 1.155 + * Linked list bubble-sort (waterson and brendan went bald hacking this). 1.156 + * 1.157 + * Sort the list in non-increasing order, using the expression passed as the 1.158 + * 'lessthan' formal macro parameter. This expression should use 'curr' as 1.159 + * the pointer to the current node (of type nodetype) and 'next' as the next 1.160 + * node pointer. It should return true if curr is less than next, and false 1.161 + * otherwise. 1.162 + */ 1.163 +#define BUBBLE_SORT_LINKED_LIST(listp, nodetype, lessthan) \ 1.164 + PR_BEGIN_MACRO \ 1.165 + nodetype *curr, **currp, *next, **nextp, *tmp; \ 1.166 + \ 1.167 + currp = listp; \ 1.168 + while ((curr = *currp) != NULL && curr->next) { \ 1.169 + nextp = &curr->next; \ 1.170 + while ((next = *nextp) != NULL) { \ 1.171 + if (lessthan) { \ 1.172 + tmp = curr->next; \ 1.173 + *currp = tmp; \ 1.174 + if (tmp == next) { \ 1.175 + PR_ASSERT(nextp == &curr->next); \ 1.176 + curr->next = next->next; \ 1.177 + next->next = curr; \ 1.178 + } else { \ 1.179 + *nextp = next->next; \ 1.180 + curr->next = next->next; \ 1.181 + next->next = tmp; \ 1.182 + *currp = next; \ 1.183 + *nextp = curr; \ 1.184 + nextp = &curr->next; \ 1.185 + } \ 1.186 + curr = next; \ 1.187 + continue; \ 1.188 + } \ 1.189 + nextp = &next->next; \ 1.190 + } \ 1.191 + currp = &curr->next; \ 1.192 + } \ 1.193 + PR_END_MACRO 1.194 + 1.195 +static int tabulate_node(PLHashEntry *he, int i, void *arg) 1.196 +{ 1.197 + tmgraphnode *node = (tmgraphnode*) he; 1.198 + tmgraphnode **table = (tmgraphnode**) arg; 1.199 + 1.200 + table[i] = node; 1.201 + BUBBLE_SORT_LINKED_LIST(&node->down, tmgraphnode, 1.202 + (curr->allocs.bytes.total < next->allocs.bytes.total)); 1.203 + return HT_ENUMERATE_NEXT; 1.204 +} 1.205 + 1.206 +/* Sort in reverse size order, so biggest node comes first. */ 1.207 +static int node_table_compare(const void *p1, const void *p2) 1.208 +{ 1.209 + const tmgraphnode *node1, *node2; 1.210 + uint32_t key1, key2; 1.211 + 1.212 + node1 = *(const tmgraphnode**) p1; 1.213 + node2 = *(const tmgraphnode**) p2; 1.214 + if (sort_by_direct) { 1.215 + key1 = node1->allocs.bytes.direct; 1.216 + key2 = node2->allocs.bytes.direct; 1.217 + } else { 1.218 + key1 = node1->allocs.bytes.total; 1.219 + key2 = node2->allocs.bytes.total; 1.220 + } 1.221 + return (key2 < key1) ? -1 : (key2 > key1) ? 1 : 0; 1.222 +} 1.223 + 1.224 +static int mean_size_compare(const void *p1, const void *p2) 1.225 +{ 1.226 + const tmgraphnode *node1, *node2; 1.227 + double div1, div2, key1, key2; 1.228 + 1.229 + node1 = *(const tmgraphnode**) p1; 1.230 + node2 = *(const tmgraphnode**) p2; 1.231 + div1 = (double)node1->allocs.calls.direct; 1.232 + div2 = (double)node2->allocs.calls.direct; 1.233 + if (div1 == 0 || div2 == 0) 1.234 + return (int)(div2 - div1); 1.235 + key1 = (double)node1->allocs.bytes.direct / div1; 1.236 + key2 = (double)node2->allocs.bytes.direct / div2; 1.237 + if (key1 < key2) 1.238 + return 1; 1.239 + if (key1 > key2) 1.240 + return -1; 1.241 + return 0; 1.242 +} 1.243 + 1.244 +static const char *prettybig(uint32_t num, char *buf, size_t limit) 1.245 +{ 1.246 + if (num >= 1000000000) 1.247 + PR_snprintf(buf, limit, "%1.2fG", (double) num / 1e9); 1.248 + else if (num >= 1000000) 1.249 + PR_snprintf(buf, limit, "%1.2fM", (double) num / 1e6); 1.250 + else if (num >= 1000) 1.251 + PR_snprintf(buf, limit, "%1.2fK", (double) num / 1e3); 1.252 + else 1.253 + PR_snprintf(buf, limit, "%lu", (unsigned long) num); 1.254 + return buf; 1.255 +} 1.256 + 1.257 +static double percent(uint32_t num, uint32_t total) 1.258 +{ 1.259 + if (num == 0) 1.260 + return 0.0; 1.261 + return ((double) num * 100) / (double) total; 1.262 +} 1.263 + 1.264 +static void sort_graphlink_list(tmgraphlink **listp, int which) 1.265 +{ 1.266 + BUBBLE_SORT_LINKED_LIST(listp, tmgraphlink, 1.267 + (TM_LINK_TO_EDGE(curr, which)->allocs.bytes.total 1.268 + < TM_LINK_TO_EDGE(next, which)->allocs.bytes.total)); 1.269 +} 1.270 + 1.271 +static void dump_graphlink_list(tmgraphlink *list, int which, const char *name, 1.272 + FILE *fp) 1.273 +{ 1.274 + tmcounts bytes; 1.275 + tmgraphlink *link; 1.276 + tmgraphedge *edge; 1.277 + char buf[16]; 1.278 + 1.279 + bytes.direct = bytes.total = 0; 1.280 + for (link = list; link; link = link->next) { 1.281 + edge = TM_LINK_TO_EDGE(link, which); 1.282 + bytes.direct += edge->allocs.bytes.direct; 1.283 + bytes.total += edge->allocs.bytes.total; 1.284 + } 1.285 + 1.286 + if (js_mode) { 1.287 + fprintf(fp, 1.288 + " %s:{dbytes:%ld, tbytes:%ld, edges:[\n", 1.289 + name, (long) bytes.direct, (long) bytes.total); 1.290 + for (link = list; link; link = link->next) { 1.291 + edge = TM_LINK_TO_EDGE(link, which); 1.292 + fprintf(fp, 1.293 + " {node:%d, dbytes:%ld, tbytes:%ld},\n", 1.294 + link->node->sort, 1.295 + (long) edge->allocs.bytes.direct, 1.296 + (long) edge->allocs.bytes.total); 1.297 + } 1.298 + fputs(" ]},\n", fp); 1.299 + } else { 1.300 + fputs("<td valign=top>", fp); 1.301 + for (link = list; link; link = link->next) { 1.302 + edge = TM_LINK_TO_EDGE(link, which); 1.303 + fprintf(fp, 1.304 + "<a href='#%s'>%s (%1.2f%%)</a>\n", 1.305 + tmgraphnode_name(link->node), 1.306 + prettybig(edge->allocs.bytes.total, buf, sizeof buf), 1.307 + percent(edge->allocs.bytes.total, bytes.total)); 1.308 + } 1.309 + fputs("</td>", fp); 1.310 + } 1.311 +} 1.312 + 1.313 +static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, 1.314 + const char *title, FILE *fp) 1.315 +{ 1.316 + uint32_t i, count; 1.317 + tmgraphnode **table, *node; 1.318 + char *name; 1.319 + size_t namelen; 1.320 + char buf1[16], buf2[16], buf3[16], buf4[16]; 1.321 + 1.322 + count = hashtbl->nentries; 1.323 + table = (tmgraphnode**) malloc(count * sizeof(tmgraphnode*)); 1.324 + if (!table) { 1.325 + perror(program); 1.326 + exit(1); 1.327 + } 1.328 + PL_HashTableEnumerateEntries(hashtbl, tabulate_node, table); 1.329 + qsort(table, count, sizeof(tmgraphnode*), node_table_compare); 1.330 + for (i = 0; i < count; i++) 1.331 + table[i]->sort = i; 1.332 + 1.333 + if (js_mode) { 1.334 + fprintf(fp, 1.335 + "var %s = {\n name:'%s', title:'%s', nodes:[\n", 1.336 + varname, varname, title); 1.337 + } else { 1.338 + fprintf(fp, 1.339 + "<table border=1>\n" 1.340 + "<tr>" 1.341 + "<th>%s</th>" 1.342 + "<th>Down</th>" 1.343 + "<th>Next</th>" 1.344 + "<th>Total/Direct (percents)</th>" 1.345 + "<th>Allocations</th>" 1.346 + "<th>Fan-in</th>" 1.347 + "<th>Fan-out</th>" 1.348 + "</tr>\n", 1.349 + title); 1.350 + } 1.351 + 1.352 + for (i = 0; i < count; i++) { 1.353 + /* Don't bother with truly puny nodes. */ 1.354 + node = table[i]; 1.355 + if (node->allocs.bytes.total < min_subtotal) 1.356 + break; 1.357 + 1.358 + name = tmgraphnode_name(node); 1.359 + if (js_mode) { 1.360 + fprintf(fp, 1.361 + " {name:'%s', dbytes:%ld, tbytes:%ld," 1.362 + " dallocs:%ld, tallocs:%ld,\n", 1.363 + name, 1.364 + (long) node->allocs.bytes.direct, 1.365 + (long) node->allocs.bytes.total, 1.366 + (long) node->allocs.calls.direct, 1.367 + (long) node->allocs.calls.total); 1.368 + } else { 1.369 + namelen = strlen(name); 1.370 + fprintf(fp, 1.371 + "<tr>" 1.372 + "<td valign=top><a name='%s'>%.*s%s</a></td>", 1.373 + name, 1.374 + (namelen > 40) ? 40 : (int)namelen, name, 1.375 + (namelen > 40) ? "<i>...</i>" : ""); 1.376 + if (node->down) { 1.377 + fprintf(fp, 1.378 + "<td valign=top><a href='#%s'><i>down</i></a></td>", 1.379 + tmgraphnode_name(node->down)); 1.380 + } else { 1.381 + fputs("<td></td>", fp); 1.382 + } 1.383 + if (node->next) { 1.384 + fprintf(fp, 1.385 + "<td valign=top><a href='#%s'><i>next</i></a></td>", 1.386 + tmgraphnode_name(node->next)); 1.387 + } else { 1.388 + fputs("<td></td>", fp); 1.389 + } 1.390 + fprintf(fp, 1.391 + "<td valign=top>%s/%s (%1.2f%%/%1.2f%%)</td>" 1.392 + "<td valign=top>%s/%s (%1.2f%%/%1.2f%%)</td>", 1.393 + prettybig(node->allocs.bytes.total, buf1, sizeof buf1), 1.394 + prettybig(node->allocs.bytes.direct, buf2, sizeof buf2), 1.395 + percent(node->allocs.bytes.total, 1.396 + tmr->calltree_root.allocs.bytes.total), 1.397 + percent(node->allocs.bytes.direct, 1.398 + tmr->calltree_root.allocs.bytes.total), 1.399 + prettybig(node->allocs.calls.total, buf3, sizeof buf3), 1.400 + prettybig(node->allocs.calls.direct, buf4, sizeof buf4), 1.401 + percent(node->allocs.calls.total, 1.402 + tmr->calltree_root.allocs.calls.total), 1.403 + percent(node->allocs.calls.direct, 1.404 + tmr->calltree_root.allocs.calls.total)); 1.405 + } 1.406 + 1.407 + /* NB: we must use 'fin' because 'in' is a JS keyword! */ 1.408 + sort_graphlink_list(&node->in, TM_EDGE_IN_LINK); 1.409 + dump_graphlink_list(node->in, TM_EDGE_IN_LINK, "fin", fp); 1.410 + sort_graphlink_list(&node->out, TM_EDGE_OUT_LINK); 1.411 + dump_graphlink_list(node->out, TM_EDGE_OUT_LINK, "out", fp); 1.412 + 1.413 + if (js_mode) 1.414 + fputs(" },\n", fp); 1.415 + else 1.416 + fputs("</tr>\n", fp); 1.417 + } 1.418 + 1.419 + if (js_mode) { 1.420 + fputs("]};\n", fp); 1.421 + } else { 1.422 + fputs("</table>\n<hr>\n", fp); 1.423 + 1.424 + qsort(table, count, sizeof(tmgraphnode*), mean_size_compare); 1.425 + 1.426 + fprintf(fp, 1.427 + "<table border=1>\n" 1.428 + "<tr><th colspan=4>Direct Allocators</th></tr>\n" 1.429 + "<tr>" 1.430 + "<th>%s</th>" 1.431 + "<th>Mean Size</th>" 1.432 + "<th>StdDev</th>" 1.433 + "<th>Allocations<th>" 1.434 + "</tr>\n", 1.435 + title); 1.436 + 1.437 + for (i = 0; i < count; i++) { 1.438 + double allocs, bytes, mean, variance, sigma; 1.439 + 1.440 + node = table[i]; 1.441 + allocs = (double)node->allocs.calls.direct; 1.442 + if (!allocs) 1.443 + continue; 1.444 + 1.445 + /* Compute direct-size mean and standard deviation. */ 1.446 + bytes = (double)node->allocs.bytes.direct; 1.447 + mean = bytes / allocs; 1.448 + variance = allocs * node->sqsum - bytes * bytes; 1.449 + if (variance < 0 || allocs == 1) 1.450 + variance = 0; 1.451 + else 1.452 + variance /= allocs * (allocs - 1); 1.453 + sigma = sqrt(variance); 1.454 + 1.455 + name = tmgraphnode_name(node); 1.456 + namelen = strlen(name); 1.457 + fprintf(fp, 1.458 + "<tr>" 1.459 + "<td valign=top>%.*s%s</td>" 1.460 + "<td valign=top>%s</td>" 1.461 + "<td valign=top>%s</td>" 1.462 + "<td valign=top>%s</td>" 1.463 + "</tr>\n", 1.464 + (namelen > 65) ? 45 : (int)namelen, name, 1.465 + (namelen > 65) ? "<i>...</i>" : "", 1.466 + prettybig((uint32_t)mean, buf1, sizeof buf1), 1.467 + prettybig((uint32_t)sigma, buf2, sizeof buf2), 1.468 + prettybig(node->allocs.calls.direct, buf3, sizeof buf3)); 1.469 + } 1.470 + fputs("</table>\n", fp); 1.471 + } 1.472 + 1.473 + free((void*) table); 1.474 +} 1.475 + 1.476 +static void my_tmevent_handler(tmreader *tmr, tmevent *event) 1.477 +{ 1.478 + switch (event->type) { 1.479 + case TM_EVENT_STATS: 1.480 + if (js_mode) 1.481 + break; 1.482 + fprintf(stdout, 1.483 + "<p><table border=1>" 1.484 + "<tr><th>Counter</th><th>Value</th></tr>\n" 1.485 + "<tr><td>maximum actual stack depth</td><td align=right>%lu</td></tr>\n" 1.486 + "<tr><td>maximum callsite tree depth</td><td align=right>%lu</td></tr>\n" 1.487 + "<tr><td>number of parent callsites</td><td align=right>%lu</td></tr>\n" 1.488 + "<tr><td>maximum kids per parent</td><td align=right>%lu</td></tr>\n" 1.489 + "<tr><td>hits looking for a kid</td><td align=right>%lu</td></tr>\n" 1.490 + "<tr><td>misses looking for a kid</td><td align=right>%lu</td></tr>\n" 1.491 + "<tr><td>steps over other kids</td><td align=right>%lu</td></tr>\n" 1.492 + "<tr><td>callsite recurrences</td><td align=right>%lu</td></tr>\n" 1.493 + "<tr><td>number of stack backtraces</td><td align=right>%lu</td></tr>\n" 1.494 + "<tr><td>backtrace failures</td><td align=right>%lu</td></tr>\n" 1.495 + "<tr><td>backtrace malloc failures</td><td align=right>%lu</td></tr>\n" 1.496 + "<tr><td>backtrace dladdr failures</td><td align=right>%lu</td></tr>\n" 1.497 + "<tr><td>malloc calls</td><td align=right>%lu</td></tr>\n" 1.498 + "<tr><td>malloc failures</td><td align=right>%lu</td></tr>\n" 1.499 + "<tr><td>calloc calls</td><td align=right>%lu</td></tr>\n" 1.500 + "<tr><td>calloc failures</td><td align=right>%lu</td></tr>\n" 1.501 + "<tr><td>realloc calls</td><td align=right>%lu</td></tr>\n" 1.502 + "<tr><td>realloc failures</td><td align=right>%lu</td></tr>\n" 1.503 + "<tr><td>free calls</td><td align=right>%lu</td></tr>\n" 1.504 + "<tr><td>free(null) calls</td><td align=right>%lu</td></tr>\n" 1.505 + "</table>", 1.506 + (unsigned long) event->u.stats.tmstats.calltree_maxstack, 1.507 + (unsigned long) event->u.stats.tmstats.calltree_maxdepth, 1.508 + (unsigned long) event->u.stats.tmstats.calltree_parents, 1.509 + (unsigned long) event->u.stats.tmstats.calltree_maxkids, 1.510 + (unsigned long) event->u.stats.tmstats.calltree_kidhits, 1.511 + (unsigned long) event->u.stats.tmstats.calltree_kidmisses, 1.512 + (unsigned long) event->u.stats.tmstats.calltree_kidsteps, 1.513 + (unsigned long) event->u.stats.tmstats.callsite_recurrences, 1.514 + (unsigned long) event->u.stats.tmstats.backtrace_calls, 1.515 + (unsigned long) event->u.stats.tmstats.backtrace_failures, 1.516 + (unsigned long) event->u.stats.tmstats.btmalloc_failures, 1.517 + (unsigned long) event->u.stats.tmstats.dladdr_failures, 1.518 + (unsigned long) event->u.stats.tmstats.malloc_calls, 1.519 + (unsigned long) event->u.stats.tmstats.malloc_failures, 1.520 + (unsigned long) event->u.stats.tmstats.calloc_calls, 1.521 + (unsigned long) event->u.stats.tmstats.calloc_failures, 1.522 + (unsigned long) event->u.stats.tmstats.realloc_calls, 1.523 + (unsigned long) event->u.stats.tmstats.realloc_failures, 1.524 + (unsigned long) event->u.stats.tmstats.free_calls, 1.525 + (unsigned long) event->u.stats.tmstats.null_free_calls); 1.526 + 1.527 + if (event->u.stats.calltree_maxkids_parent) { 1.528 + tmcallsite *site = 1.529 + tmreader_callsite(tmr, event->u.stats.calltree_maxkids_parent); 1.530 + if (site && site->method) { 1.531 + fprintf(stdout, "<p>callsite with the most kids: %s</p>", 1.532 + tmmethodnode_name(site->method)); 1.533 + } 1.534 + } 1.535 + 1.536 + if (event->u.stats.calltree_maxstack_top) { 1.537 + tmcallsite *site = 1.538 + tmreader_callsite(tmr, event->u.stats.calltree_maxstack_top); 1.539 + fputs("<p>deepest callsite tree path:\n" 1.540 + "<table border=1>\n" 1.541 + "<tr><th>Method</th><th>Offset</th></tr>\n", 1.542 + stdout); 1.543 + while (site) { 1.544 + fprintf(stdout, 1.545 + "<tr><td>%s</td><td>0x%08lX</td></tr>\n", 1.546 + site->method ? tmmethodnode_name(site->method) : "???", 1.547 + (unsigned long) site->offset); 1.548 + site = site->parent; 1.549 + } 1.550 + fputs("</table>\n<hr>\n", stdout); 1.551 + } 1.552 + break; 1.553 + } 1.554 +} 1.555 + 1.556 +int main(int argc, char **argv) 1.557 +{ 1.558 + int c, i, j, rv; 1.559 + tmreader *tmr; 1.560 + FILE *fp; 1.561 + 1.562 + program = *argv; 1.563 + tmr = tmreader_new(program, NULL); 1.564 + if (!tmr) { 1.565 + perror(program); 1.566 + exit(1); 1.567 + } 1.568 + 1.569 + while ((c = getopt(argc, argv, "djtuf:m:")) != EOF) { 1.570 + switch (c) { 1.571 + case 'd': 1.572 + sort_by_direct = 1; 1.573 + break; 1.574 + case 'j': 1.575 + js_mode = 1; 1.576 + break; 1.577 + case 't': 1.578 + do_tree_dump = 1; 1.579 + break; 1.580 + case 'u': 1.581 + unified_output = 1; 1.582 + break; 1.583 + case 'f': 1.584 + function_dump = optarg; 1.585 + break; 1.586 + case 'm': 1.587 + min_subtotal = atoi(optarg); 1.588 + break; 1.589 + default: 1.590 + fprintf(stderr, 1.591 + "usage: %s [-dtu] [-f function-dump-filename] [-m min] [output.html]\n", 1.592 + program); 1.593 + exit(2); 1.594 + } 1.595 + } 1.596 + 1.597 + if (!js_mode) { 1.598 + time_t start = time(NULL); 1.599 + 1.600 + fprintf(stdout, 1.601 + "<script language=\"JavaScript\">\n" 1.602 + "function onload() {\n" 1.603 + " document.links[0].__proto__.onmouseover = new Function(" 1.604 + "\"window.status =" 1.605 + " this.href.substring(this.href.lastIndexOf('#') + 1)\");\n" 1.606 + "}\n" 1.607 + "</script>\n"); 1.608 + fprintf(stdout, "%s starting at %s", program, ctime(&start)); 1.609 + fflush(stdout); 1.610 + } 1.611 + 1.612 + argc -= optind; 1.613 + argv += optind; 1.614 + if (argc == 0) { 1.615 + if (tmreader_eventloop(tmr, "-", my_tmevent_handler) <= 0) 1.616 + exit(1); 1.617 + } else { 1.618 + for (i = j = 0; i < argc; i++) { 1.619 + fp = fopen(argv[i], "r"); 1.620 + if (!fp) { 1.621 + fprintf(stderr, "%s: can't open %s: %s\n", 1.622 + program, argv[i], strerror(errno)); 1.623 + exit(1); 1.624 + } 1.625 + rv = tmreader_eventloop(tmr, argv[i], my_tmevent_handler); 1.626 + if (rv < 0) 1.627 + exit(1); 1.628 + if (rv > 0) 1.629 + j++; 1.630 + fclose(fp); 1.631 + } 1.632 + if (j == 0) 1.633 + exit(1); 1.634 + } 1.635 + 1.636 + compute_callsite_totals(&tmr->calltree_root); 1.637 + walk_callsite_tree(&tmr->calltree_root, 0, 0, stdout); 1.638 + 1.639 + if (js_mode) { 1.640 + fprintf(stdout, 1.641 + "<script language='javascript'>\n" 1.642 + "// direct and total byte and allocator-call counts\n" 1.643 + "var dbytes = %ld, tbytes = %ld," 1.644 + " dallocs = %ld, tallocs = %ld;\n", 1.645 + (long) tmr->calltree_root.allocs.bytes.direct, 1.646 + (long) tmr->calltree_root.allocs.bytes.total, 1.647 + (long) tmr->calltree_root.allocs.calls.direct, 1.648 + (long) tmr->calltree_root.allocs.calls.total); 1.649 + } 1.650 + 1.651 + dump_graph(tmr, tmr->libraries, "libraries", "Library", stdout); 1.652 + if (!js_mode) 1.653 + fputs("<hr>\n", stdout); 1.654 + 1.655 + dump_graph(tmr, tmr->components, "classes", "Class or Component", stdout); 1.656 + if (js_mode || unified_output || function_dump) { 1.657 + if (js_mode || unified_output || strcmp(function_dump, "-") == 0) { 1.658 + fp = stdout; 1.659 + if (!js_mode) 1.660 + fputs("<hr>\n", fp); 1.661 + } else { 1.662 + struct stat sb, fsb; 1.663 + 1.664 + fstat(fileno(stdout), &sb); 1.665 + if (stat(function_dump, &fsb) == 0 && 1.666 + fsb.st_dev == sb.st_dev && fsb.st_ino == sb.st_ino) { 1.667 + fp = stdout; 1.668 + fputs("<hr>\n", fp); 1.669 + } else { 1.670 + fp = fopen(function_dump, "w"); 1.671 + if (!fp) { 1.672 + fprintf(stderr, "%s: can't open %s: %s\n", 1.673 + program, function_dump, strerror(errno)); 1.674 + exit(1); 1.675 + } 1.676 + } 1.677 + } 1.678 + 1.679 + dump_graph(tmr, tmr->methods, "methods", "Function or Method", fp); 1.680 + if (fp != stdout) 1.681 + fclose(fp); 1.682 + 1.683 + if (js_mode) { 1.684 + fputs("function viewnode(graph, index) {\n" 1.685 + " view.location = viewsrc();\n" 1.686 + "}\n" 1.687 + "function viewnodelink(graph, index) {\n" 1.688 + " var node = graph.nodes[index];\n" 1.689 + " return '<a href=\"javascript:viewnode('" 1.690 + " + graph.name.quote() + ', ' + node.sort" 1.691 + " + ')\" onmouseover=' + node.name.quote() + '>'" 1.692 + " + node.name + '</a>';\n" 1.693 + "}\n" 1.694 + "function search(expr) {\n" 1.695 + " var re = new RegExp(expr);\n" 1.696 + " var src = '';\n" 1.697 + " var graphs = [libraries, classes, methods]\n" 1.698 + " var nodes;\n" 1.699 + " for (var n = 0; n < (nodes = graphs[n].nodes).length; n++) {\n" 1.700 + " for (var i = 0; i < nodes.length; i++) {\n" 1.701 + " if (re.test(nodes[i].name))\n" 1.702 + " src += viewnodelink(graph, i) + '\\n';\n" 1.703 + " }\n" 1.704 + " }\n" 1.705 + " view.location = viewsrc();\n" 1.706 + "}\n" 1.707 + "function ctrlsrc() {\n" 1.708 + " return \"<form>\\n" 1.709 + "search: <input size=40 onchange='search(this.value)'>\\n" 1.710 + "</form>\\n\";\n" 1.711 + "}\n" 1.712 + "function viewsrc() {\n" 1.713 + " return 'hiiiii'\n" 1.714 + "}\n" 1.715 + "</script>\n" 1.716 + "<frameset rows='10%,*'>\n" 1.717 + " <frame name='ctrl' src='javascript:top.ctrlsrc()'>\n" 1.718 + " <frame name='view' src='javascript:top.viewsrc()'>\n" 1.719 + "</frameset>\n", 1.720 + stdout); 1.721 + } 1.722 + } 1.723 + 1.724 + exit(0); 1.725 +}