tools/trace-malloc/bloatblame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 #include <stdio.h>
michael@0 7 #include <stdlib.h>
michael@0 8 #include <string.h>
michael@0 9 #include <ctype.h>
michael@0 10 #include <errno.h>
michael@0 11 #ifdef HAVE_GETOPT_H
michael@0 12 #include <getopt.h>
michael@0 13 #else
michael@0 14 #ifdef XP_WIN
michael@0 15 #include "getopt.c"
michael@0 16 #else
michael@0 17 extern int getopt(int argc, char *const *argv, const char *shortopts);
michael@0 18 extern char *optarg;
michael@0 19 extern int optind;
michael@0 20 #endif
michael@0 21 #endif
michael@0 22 #include <math.h>
michael@0 23 #include <time.h>
michael@0 24 #include <sys/stat.h>
michael@0 25 #include "prlog.h"
michael@0 26 #include "prprf.h"
michael@0 27 #include "plhash.h"
michael@0 28 #include "pldhash.h"
michael@0 29 #include "nsTraceMalloc.h"
michael@0 30 #include "tmreader.h"
michael@0 31
michael@0 32 static char *program;
michael@0 33 static int sort_by_direct = 0;
michael@0 34 static int js_mode = 0;
michael@0 35 static int do_tree_dump = 0;
michael@0 36 static int unified_output = 0;
michael@0 37 static char *function_dump = NULL;
michael@0 38 static uint32_t min_subtotal = 0;
michael@0 39
michael@0 40 static void compute_callsite_totals(tmcallsite *site)
michael@0 41 {
michael@0 42 tmcallsite *kid;
michael@0 43
michael@0 44 site->allocs.bytes.total += site->allocs.bytes.direct;
michael@0 45 site->allocs.calls.total += site->allocs.calls.direct;
michael@0 46 for (kid = site->kids; kid; kid = kid->siblings) {
michael@0 47 compute_callsite_totals(kid);
michael@0 48 site->allocs.bytes.total += kid->allocs.bytes.total;
michael@0 49 site->allocs.calls.total += kid->allocs.calls.total;
michael@0 50 }
michael@0 51 }
michael@0 52
michael@0 53 static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp)
michael@0 54 {
michael@0 55 tmcallsite *parent;
michael@0 56 tmgraphnode *comp, *pcomp, *lib, *plib;
michael@0 57 tmmethodnode *meth, *pmeth;
michael@0 58 int old_meth_low, old_comp_low, old_lib_low, nkids;
michael@0 59 tmcallsite *kid;
michael@0 60
michael@0 61 parent = site->parent;
michael@0 62 comp = lib = NULL;
michael@0 63 meth = NULL;
michael@0 64 if (parent) {
michael@0 65 meth = site->method;
michael@0 66 if (meth) {
michael@0 67 pmeth = parent->method;
michael@0 68 if (pmeth && pmeth != meth) {
michael@0 69 if (!meth->graphnode.low) {
michael@0 70 meth->graphnode.allocs.bytes.total += site->allocs.bytes.total;
michael@0 71 meth->graphnode.allocs.calls.total += site->allocs.calls.total;
michael@0 72 }
michael@0 73 if (!tmgraphnode_connect(&(pmeth->graphnode), &(meth->graphnode), site))
michael@0 74 goto bad;
michael@0 75
michael@0 76 comp = meth->graphnode.up;
michael@0 77 if (comp) {
michael@0 78 pcomp = pmeth->graphnode.up;
michael@0 79 if (pcomp && pcomp != comp) {
michael@0 80 if (!comp->low) {
michael@0 81 comp->allocs.bytes.total
michael@0 82 += site->allocs.bytes.total;
michael@0 83 comp->allocs.calls.total
michael@0 84 += site->allocs.calls.total;
michael@0 85 }
michael@0 86 if (!tmgraphnode_connect(pcomp, comp, site))
michael@0 87 goto bad;
michael@0 88
michael@0 89 lib = comp->up;
michael@0 90 if (lib) {
michael@0 91 plib = pcomp->up;
michael@0 92 if (plib && plib != lib) {
michael@0 93 if (!lib->low) {
michael@0 94 lib->allocs.bytes.total
michael@0 95 += site->allocs.bytes.total;
michael@0 96 lib->allocs.calls.total
michael@0 97 += site->allocs.calls.total;
michael@0 98 }
michael@0 99 if (!tmgraphnode_connect(plib, lib, site))
michael@0 100 goto bad;
michael@0 101 }
michael@0 102 old_lib_low = lib->low;
michael@0 103 if (!old_lib_low)
michael@0 104 lib->low = level;
michael@0 105 }
michael@0 106 }
michael@0 107 old_comp_low = comp->low;
michael@0 108 if (!old_comp_low)
michael@0 109 comp->low = level;
michael@0 110 }
michael@0 111 }
michael@0 112 old_meth_low = meth->graphnode.low;
michael@0 113 if (!old_meth_low)
michael@0 114 meth->graphnode.low = level;
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 if (do_tree_dump) {
michael@0 119 fprintf(fp, "%c%*s%3d %3d %s %lu %ld\n",
michael@0 120 site->kids ? '+' : '-', level, "", level, kidnum,
michael@0 121 meth ? tmmethodnode_name(meth) : "???",
michael@0 122 (unsigned long)site->allocs.bytes.direct,
michael@0 123 (long)site->allocs.bytes.total);
michael@0 124 }
michael@0 125 nkids = 0;
michael@0 126 level++;
michael@0 127 for (kid = site->kids; kid; kid = kid->siblings) {
michael@0 128 walk_callsite_tree(kid, level, nkids, fp);
michael@0 129 nkids++;
michael@0 130 }
michael@0 131
michael@0 132 if (meth) {
michael@0 133 if (!old_meth_low)
michael@0 134 meth->graphnode.low = 0;
michael@0 135 if (comp) {
michael@0 136 if (!old_comp_low)
michael@0 137 comp->low = 0;
michael@0 138 if (lib) {
michael@0 139 if (!old_lib_low)
michael@0 140 lib->low = 0;
michael@0 141 }
michael@0 142 }
michael@0 143 }
michael@0 144 return;
michael@0 145
michael@0 146 bad:
michael@0 147 perror(program);
michael@0 148 exit(1);
michael@0 149 }
michael@0 150
michael@0 151 /*
michael@0 152 * Linked list bubble-sort (waterson and brendan went bald hacking this).
michael@0 153 *
michael@0 154 * Sort the list in non-increasing order, using the expression passed as the
michael@0 155 * 'lessthan' formal macro parameter. This expression should use 'curr' as
michael@0 156 * the pointer to the current node (of type nodetype) and 'next' as the next
michael@0 157 * node pointer. It should return true if curr is less than next, and false
michael@0 158 * otherwise.
michael@0 159 */
michael@0 160 #define BUBBLE_SORT_LINKED_LIST(listp, nodetype, lessthan) \
michael@0 161 PR_BEGIN_MACRO \
michael@0 162 nodetype *curr, **currp, *next, **nextp, *tmp; \
michael@0 163 \
michael@0 164 currp = listp; \
michael@0 165 while ((curr = *currp) != NULL && curr->next) { \
michael@0 166 nextp = &curr->next; \
michael@0 167 while ((next = *nextp) != NULL) { \
michael@0 168 if (lessthan) { \
michael@0 169 tmp = curr->next; \
michael@0 170 *currp = tmp; \
michael@0 171 if (tmp == next) { \
michael@0 172 PR_ASSERT(nextp == &curr->next); \
michael@0 173 curr->next = next->next; \
michael@0 174 next->next = curr; \
michael@0 175 } else { \
michael@0 176 *nextp = next->next; \
michael@0 177 curr->next = next->next; \
michael@0 178 next->next = tmp; \
michael@0 179 *currp = next; \
michael@0 180 *nextp = curr; \
michael@0 181 nextp = &curr->next; \
michael@0 182 } \
michael@0 183 curr = next; \
michael@0 184 continue; \
michael@0 185 } \
michael@0 186 nextp = &next->next; \
michael@0 187 } \
michael@0 188 currp = &curr->next; \
michael@0 189 } \
michael@0 190 PR_END_MACRO
michael@0 191
michael@0 192 static int tabulate_node(PLHashEntry *he, int i, void *arg)
michael@0 193 {
michael@0 194 tmgraphnode *node = (tmgraphnode*) he;
michael@0 195 tmgraphnode **table = (tmgraphnode**) arg;
michael@0 196
michael@0 197 table[i] = node;
michael@0 198 BUBBLE_SORT_LINKED_LIST(&node->down, tmgraphnode,
michael@0 199 (curr->allocs.bytes.total < next->allocs.bytes.total));
michael@0 200 return HT_ENUMERATE_NEXT;
michael@0 201 }
michael@0 202
michael@0 203 /* Sort in reverse size order, so biggest node comes first. */
michael@0 204 static int node_table_compare(const void *p1, const void *p2)
michael@0 205 {
michael@0 206 const tmgraphnode *node1, *node2;
michael@0 207 uint32_t key1, key2;
michael@0 208
michael@0 209 node1 = *(const tmgraphnode**) p1;
michael@0 210 node2 = *(const tmgraphnode**) p2;
michael@0 211 if (sort_by_direct) {
michael@0 212 key1 = node1->allocs.bytes.direct;
michael@0 213 key2 = node2->allocs.bytes.direct;
michael@0 214 } else {
michael@0 215 key1 = node1->allocs.bytes.total;
michael@0 216 key2 = node2->allocs.bytes.total;
michael@0 217 }
michael@0 218 return (key2 < key1) ? -1 : (key2 > key1) ? 1 : 0;
michael@0 219 }
michael@0 220
michael@0 221 static int mean_size_compare(const void *p1, const void *p2)
michael@0 222 {
michael@0 223 const tmgraphnode *node1, *node2;
michael@0 224 double div1, div2, key1, key2;
michael@0 225
michael@0 226 node1 = *(const tmgraphnode**) p1;
michael@0 227 node2 = *(const tmgraphnode**) p2;
michael@0 228 div1 = (double)node1->allocs.calls.direct;
michael@0 229 div2 = (double)node2->allocs.calls.direct;
michael@0 230 if (div1 == 0 || div2 == 0)
michael@0 231 return (int)(div2 - div1);
michael@0 232 key1 = (double)node1->allocs.bytes.direct / div1;
michael@0 233 key2 = (double)node2->allocs.bytes.direct / div2;
michael@0 234 if (key1 < key2)
michael@0 235 return 1;
michael@0 236 if (key1 > key2)
michael@0 237 return -1;
michael@0 238 return 0;
michael@0 239 }
michael@0 240
michael@0 241 static const char *prettybig(uint32_t num, char *buf, size_t limit)
michael@0 242 {
michael@0 243 if (num >= 1000000000)
michael@0 244 PR_snprintf(buf, limit, "%1.2fG", (double) num / 1e9);
michael@0 245 else if (num >= 1000000)
michael@0 246 PR_snprintf(buf, limit, "%1.2fM", (double) num / 1e6);
michael@0 247 else if (num >= 1000)
michael@0 248 PR_snprintf(buf, limit, "%1.2fK", (double) num / 1e3);
michael@0 249 else
michael@0 250 PR_snprintf(buf, limit, "%lu", (unsigned long) num);
michael@0 251 return buf;
michael@0 252 }
michael@0 253
michael@0 254 static double percent(uint32_t num, uint32_t total)
michael@0 255 {
michael@0 256 if (num == 0)
michael@0 257 return 0.0;
michael@0 258 return ((double) num * 100) / (double) total;
michael@0 259 }
michael@0 260
michael@0 261 static void sort_graphlink_list(tmgraphlink **listp, int which)
michael@0 262 {
michael@0 263 BUBBLE_SORT_LINKED_LIST(listp, tmgraphlink,
michael@0 264 (TM_LINK_TO_EDGE(curr, which)->allocs.bytes.total
michael@0 265 < TM_LINK_TO_EDGE(next, which)->allocs.bytes.total));
michael@0 266 }
michael@0 267
michael@0 268 static void dump_graphlink_list(tmgraphlink *list, int which, const char *name,
michael@0 269 FILE *fp)
michael@0 270 {
michael@0 271 tmcounts bytes;
michael@0 272 tmgraphlink *link;
michael@0 273 tmgraphedge *edge;
michael@0 274 char buf[16];
michael@0 275
michael@0 276 bytes.direct = bytes.total = 0;
michael@0 277 for (link = list; link; link = link->next) {
michael@0 278 edge = TM_LINK_TO_EDGE(link, which);
michael@0 279 bytes.direct += edge->allocs.bytes.direct;
michael@0 280 bytes.total += edge->allocs.bytes.total;
michael@0 281 }
michael@0 282
michael@0 283 if (js_mode) {
michael@0 284 fprintf(fp,
michael@0 285 " %s:{dbytes:%ld, tbytes:%ld, edges:[\n",
michael@0 286 name, (long) bytes.direct, (long) bytes.total);
michael@0 287 for (link = list; link; link = link->next) {
michael@0 288 edge = TM_LINK_TO_EDGE(link, which);
michael@0 289 fprintf(fp,
michael@0 290 " {node:%d, dbytes:%ld, tbytes:%ld},\n",
michael@0 291 link->node->sort,
michael@0 292 (long) edge->allocs.bytes.direct,
michael@0 293 (long) edge->allocs.bytes.total);
michael@0 294 }
michael@0 295 fputs(" ]},\n", fp);
michael@0 296 } else {
michael@0 297 fputs("<td valign=top>", fp);
michael@0 298 for (link = list; link; link = link->next) {
michael@0 299 edge = TM_LINK_TO_EDGE(link, which);
michael@0 300 fprintf(fp,
michael@0 301 "<a href='#%s'>%s&nbsp;(%1.2f%%)</a>\n",
michael@0 302 tmgraphnode_name(link->node),
michael@0 303 prettybig(edge->allocs.bytes.total, buf, sizeof buf),
michael@0 304 percent(edge->allocs.bytes.total, bytes.total));
michael@0 305 }
michael@0 306 fputs("</td>", fp);
michael@0 307 }
michael@0 308 }
michael@0 309
michael@0 310 static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname,
michael@0 311 const char *title, FILE *fp)
michael@0 312 {
michael@0 313 uint32_t i, count;
michael@0 314 tmgraphnode **table, *node;
michael@0 315 char *name;
michael@0 316 size_t namelen;
michael@0 317 char buf1[16], buf2[16], buf3[16], buf4[16];
michael@0 318
michael@0 319 count = hashtbl->nentries;
michael@0 320 table = (tmgraphnode**) malloc(count * sizeof(tmgraphnode*));
michael@0 321 if (!table) {
michael@0 322 perror(program);
michael@0 323 exit(1);
michael@0 324 }
michael@0 325 PL_HashTableEnumerateEntries(hashtbl, tabulate_node, table);
michael@0 326 qsort(table, count, sizeof(tmgraphnode*), node_table_compare);
michael@0 327 for (i = 0; i < count; i++)
michael@0 328 table[i]->sort = i;
michael@0 329
michael@0 330 if (js_mode) {
michael@0 331 fprintf(fp,
michael@0 332 "var %s = {\n name:'%s', title:'%s', nodes:[\n",
michael@0 333 varname, varname, title);
michael@0 334 } else {
michael@0 335 fprintf(fp,
michael@0 336 "<table border=1>\n"
michael@0 337 "<tr>"
michael@0 338 "<th>%s</th>"
michael@0 339 "<th>Down</th>"
michael@0 340 "<th>Next</th>"
michael@0 341 "<th>Total/Direct (percents)</th>"
michael@0 342 "<th>Allocations</th>"
michael@0 343 "<th>Fan-in</th>"
michael@0 344 "<th>Fan-out</th>"
michael@0 345 "</tr>\n",
michael@0 346 title);
michael@0 347 }
michael@0 348
michael@0 349 for (i = 0; i < count; i++) {
michael@0 350 /* Don't bother with truly puny nodes. */
michael@0 351 node = table[i];
michael@0 352 if (node->allocs.bytes.total < min_subtotal)
michael@0 353 break;
michael@0 354
michael@0 355 name = tmgraphnode_name(node);
michael@0 356 if (js_mode) {
michael@0 357 fprintf(fp,
michael@0 358 " {name:'%s', dbytes:%ld, tbytes:%ld,"
michael@0 359 " dallocs:%ld, tallocs:%ld,\n",
michael@0 360 name,
michael@0 361 (long) node->allocs.bytes.direct,
michael@0 362 (long) node->allocs.bytes.total,
michael@0 363 (long) node->allocs.calls.direct,
michael@0 364 (long) node->allocs.calls.total);
michael@0 365 } else {
michael@0 366 namelen = strlen(name);
michael@0 367 fprintf(fp,
michael@0 368 "<tr>"
michael@0 369 "<td valign=top><a name='%s'>%.*s%s</a></td>",
michael@0 370 name,
michael@0 371 (namelen > 40) ? 40 : (int)namelen, name,
michael@0 372 (namelen > 40) ? "<i>...</i>" : "");
michael@0 373 if (node->down) {
michael@0 374 fprintf(fp,
michael@0 375 "<td valign=top><a href='#%s'><i>down</i></a></td>",
michael@0 376 tmgraphnode_name(node->down));
michael@0 377 } else {
michael@0 378 fputs("<td></td>", fp);
michael@0 379 }
michael@0 380 if (node->next) {
michael@0 381 fprintf(fp,
michael@0 382 "<td valign=top><a href='#%s'><i>next</i></a></td>",
michael@0 383 tmgraphnode_name(node->next));
michael@0 384 } else {
michael@0 385 fputs("<td></td>", fp);
michael@0 386 }
michael@0 387 fprintf(fp,
michael@0 388 "<td valign=top>%s/%s (%1.2f%%/%1.2f%%)</td>"
michael@0 389 "<td valign=top>%s/%s (%1.2f%%/%1.2f%%)</td>",
michael@0 390 prettybig(node->allocs.bytes.total, buf1, sizeof buf1),
michael@0 391 prettybig(node->allocs.bytes.direct, buf2, sizeof buf2),
michael@0 392 percent(node->allocs.bytes.total,
michael@0 393 tmr->calltree_root.allocs.bytes.total),
michael@0 394 percent(node->allocs.bytes.direct,
michael@0 395 tmr->calltree_root.allocs.bytes.total),
michael@0 396 prettybig(node->allocs.calls.total, buf3, sizeof buf3),
michael@0 397 prettybig(node->allocs.calls.direct, buf4, sizeof buf4),
michael@0 398 percent(node->allocs.calls.total,
michael@0 399 tmr->calltree_root.allocs.calls.total),
michael@0 400 percent(node->allocs.calls.direct,
michael@0 401 tmr->calltree_root.allocs.calls.total));
michael@0 402 }
michael@0 403
michael@0 404 /* NB: we must use 'fin' because 'in' is a JS keyword! */
michael@0 405 sort_graphlink_list(&node->in, TM_EDGE_IN_LINK);
michael@0 406 dump_graphlink_list(node->in, TM_EDGE_IN_LINK, "fin", fp);
michael@0 407 sort_graphlink_list(&node->out, TM_EDGE_OUT_LINK);
michael@0 408 dump_graphlink_list(node->out, TM_EDGE_OUT_LINK, "out", fp);
michael@0 409
michael@0 410 if (js_mode)
michael@0 411 fputs(" },\n", fp);
michael@0 412 else
michael@0 413 fputs("</tr>\n", fp);
michael@0 414 }
michael@0 415
michael@0 416 if (js_mode) {
michael@0 417 fputs("]};\n", fp);
michael@0 418 } else {
michael@0 419 fputs("</table>\n<hr>\n", fp);
michael@0 420
michael@0 421 qsort(table, count, sizeof(tmgraphnode*), mean_size_compare);
michael@0 422
michael@0 423 fprintf(fp,
michael@0 424 "<table border=1>\n"
michael@0 425 "<tr><th colspan=4>Direct Allocators</th></tr>\n"
michael@0 426 "<tr>"
michael@0 427 "<th>%s</th>"
michael@0 428 "<th>Mean&nbsp;Size</th>"
michael@0 429 "<th>StdDev</th>"
michael@0 430 "<th>Allocations<th>"
michael@0 431 "</tr>\n",
michael@0 432 title);
michael@0 433
michael@0 434 for (i = 0; i < count; i++) {
michael@0 435 double allocs, bytes, mean, variance, sigma;
michael@0 436
michael@0 437 node = table[i];
michael@0 438 allocs = (double)node->allocs.calls.direct;
michael@0 439 if (!allocs)
michael@0 440 continue;
michael@0 441
michael@0 442 /* Compute direct-size mean and standard deviation. */
michael@0 443 bytes = (double)node->allocs.bytes.direct;
michael@0 444 mean = bytes / allocs;
michael@0 445 variance = allocs * node->sqsum - bytes * bytes;
michael@0 446 if (variance < 0 || allocs == 1)
michael@0 447 variance = 0;
michael@0 448 else
michael@0 449 variance /= allocs * (allocs - 1);
michael@0 450 sigma = sqrt(variance);
michael@0 451
michael@0 452 name = tmgraphnode_name(node);
michael@0 453 namelen = strlen(name);
michael@0 454 fprintf(fp,
michael@0 455 "<tr>"
michael@0 456 "<td valign=top>%.*s%s</td>"
michael@0 457 "<td valign=top>%s</td>"
michael@0 458 "<td valign=top>%s</td>"
michael@0 459 "<td valign=top>%s</td>"
michael@0 460 "</tr>\n",
michael@0 461 (namelen > 65) ? 45 : (int)namelen, name,
michael@0 462 (namelen > 65) ? "<i>...</i>" : "",
michael@0 463 prettybig((uint32_t)mean, buf1, sizeof buf1),
michael@0 464 prettybig((uint32_t)sigma, buf2, sizeof buf2),
michael@0 465 prettybig(node->allocs.calls.direct, buf3, sizeof buf3));
michael@0 466 }
michael@0 467 fputs("</table>\n", fp);
michael@0 468 }
michael@0 469
michael@0 470 free((void*) table);
michael@0 471 }
michael@0 472
michael@0 473 static void my_tmevent_handler(tmreader *tmr, tmevent *event)
michael@0 474 {
michael@0 475 switch (event->type) {
michael@0 476 case TM_EVENT_STATS:
michael@0 477 if (js_mode)
michael@0 478 break;
michael@0 479 fprintf(stdout,
michael@0 480 "<p><table border=1>"
michael@0 481 "<tr><th>Counter</th><th>Value</th></tr>\n"
michael@0 482 "<tr><td>maximum actual stack depth</td><td align=right>%lu</td></tr>\n"
michael@0 483 "<tr><td>maximum callsite tree depth</td><td align=right>%lu</td></tr>\n"
michael@0 484 "<tr><td>number of parent callsites</td><td align=right>%lu</td></tr>\n"
michael@0 485 "<tr><td>maximum kids per parent</td><td align=right>%lu</td></tr>\n"
michael@0 486 "<tr><td>hits looking for a kid</td><td align=right>%lu</td></tr>\n"
michael@0 487 "<tr><td>misses looking for a kid</td><td align=right>%lu</td></tr>\n"
michael@0 488 "<tr><td>steps over other kids</td><td align=right>%lu</td></tr>\n"
michael@0 489 "<tr><td>callsite recurrences</td><td align=right>%lu</td></tr>\n"
michael@0 490 "<tr><td>number of stack backtraces</td><td align=right>%lu</td></tr>\n"
michael@0 491 "<tr><td>backtrace failures</td><td align=right>%lu</td></tr>\n"
michael@0 492 "<tr><td>backtrace malloc failures</td><td align=right>%lu</td></tr>\n"
michael@0 493 "<tr><td>backtrace dladdr failures</td><td align=right>%lu</td></tr>\n"
michael@0 494 "<tr><td>malloc calls</td><td align=right>%lu</td></tr>\n"
michael@0 495 "<tr><td>malloc failures</td><td align=right>%lu</td></tr>\n"
michael@0 496 "<tr><td>calloc calls</td><td align=right>%lu</td></tr>\n"
michael@0 497 "<tr><td>calloc failures</td><td align=right>%lu</td></tr>\n"
michael@0 498 "<tr><td>realloc calls</td><td align=right>%lu</td></tr>\n"
michael@0 499 "<tr><td>realloc failures</td><td align=right>%lu</td></tr>\n"
michael@0 500 "<tr><td>free calls</td><td align=right>%lu</td></tr>\n"
michael@0 501 "<tr><td>free(null) calls</td><td align=right>%lu</td></tr>\n"
michael@0 502 "</table>",
michael@0 503 (unsigned long) event->u.stats.tmstats.calltree_maxstack,
michael@0 504 (unsigned long) event->u.stats.tmstats.calltree_maxdepth,
michael@0 505 (unsigned long) event->u.stats.tmstats.calltree_parents,
michael@0 506 (unsigned long) event->u.stats.tmstats.calltree_maxkids,
michael@0 507 (unsigned long) event->u.stats.tmstats.calltree_kidhits,
michael@0 508 (unsigned long) event->u.stats.tmstats.calltree_kidmisses,
michael@0 509 (unsigned long) event->u.stats.tmstats.calltree_kidsteps,
michael@0 510 (unsigned long) event->u.stats.tmstats.callsite_recurrences,
michael@0 511 (unsigned long) event->u.stats.tmstats.backtrace_calls,
michael@0 512 (unsigned long) event->u.stats.tmstats.backtrace_failures,
michael@0 513 (unsigned long) event->u.stats.tmstats.btmalloc_failures,
michael@0 514 (unsigned long) event->u.stats.tmstats.dladdr_failures,
michael@0 515 (unsigned long) event->u.stats.tmstats.malloc_calls,
michael@0 516 (unsigned long) event->u.stats.tmstats.malloc_failures,
michael@0 517 (unsigned long) event->u.stats.tmstats.calloc_calls,
michael@0 518 (unsigned long) event->u.stats.tmstats.calloc_failures,
michael@0 519 (unsigned long) event->u.stats.tmstats.realloc_calls,
michael@0 520 (unsigned long) event->u.stats.tmstats.realloc_failures,
michael@0 521 (unsigned long) event->u.stats.tmstats.free_calls,
michael@0 522 (unsigned long) event->u.stats.tmstats.null_free_calls);
michael@0 523
michael@0 524 if (event->u.stats.calltree_maxkids_parent) {
michael@0 525 tmcallsite *site =
michael@0 526 tmreader_callsite(tmr, event->u.stats.calltree_maxkids_parent);
michael@0 527 if (site && site->method) {
michael@0 528 fprintf(stdout, "<p>callsite with the most kids: %s</p>",
michael@0 529 tmmethodnode_name(site->method));
michael@0 530 }
michael@0 531 }
michael@0 532
michael@0 533 if (event->u.stats.calltree_maxstack_top) {
michael@0 534 tmcallsite *site =
michael@0 535 tmreader_callsite(tmr, event->u.stats.calltree_maxstack_top);
michael@0 536 fputs("<p>deepest callsite tree path:\n"
michael@0 537 "<table border=1>\n"
michael@0 538 "<tr><th>Method</th><th>Offset</th></tr>\n",
michael@0 539 stdout);
michael@0 540 while (site) {
michael@0 541 fprintf(stdout,
michael@0 542 "<tr><td>%s</td><td>0x%08lX</td></tr>\n",
michael@0 543 site->method ? tmmethodnode_name(site->method) : "???",
michael@0 544 (unsigned long) site->offset);
michael@0 545 site = site->parent;
michael@0 546 }
michael@0 547 fputs("</table>\n<hr>\n", stdout);
michael@0 548 }
michael@0 549 break;
michael@0 550 }
michael@0 551 }
michael@0 552
michael@0 553 int main(int argc, char **argv)
michael@0 554 {
michael@0 555 int c, i, j, rv;
michael@0 556 tmreader *tmr;
michael@0 557 FILE *fp;
michael@0 558
michael@0 559 program = *argv;
michael@0 560 tmr = tmreader_new(program, NULL);
michael@0 561 if (!tmr) {
michael@0 562 perror(program);
michael@0 563 exit(1);
michael@0 564 }
michael@0 565
michael@0 566 while ((c = getopt(argc, argv, "djtuf:m:")) != EOF) {
michael@0 567 switch (c) {
michael@0 568 case 'd':
michael@0 569 sort_by_direct = 1;
michael@0 570 break;
michael@0 571 case 'j':
michael@0 572 js_mode = 1;
michael@0 573 break;
michael@0 574 case 't':
michael@0 575 do_tree_dump = 1;
michael@0 576 break;
michael@0 577 case 'u':
michael@0 578 unified_output = 1;
michael@0 579 break;
michael@0 580 case 'f':
michael@0 581 function_dump = optarg;
michael@0 582 break;
michael@0 583 case 'm':
michael@0 584 min_subtotal = atoi(optarg);
michael@0 585 break;
michael@0 586 default:
michael@0 587 fprintf(stderr,
michael@0 588 "usage: %s [-dtu] [-f function-dump-filename] [-m min] [output.html]\n",
michael@0 589 program);
michael@0 590 exit(2);
michael@0 591 }
michael@0 592 }
michael@0 593
michael@0 594 if (!js_mode) {
michael@0 595 time_t start = time(NULL);
michael@0 596
michael@0 597 fprintf(stdout,
michael@0 598 "<script language=\"JavaScript\">\n"
michael@0 599 "function onload() {\n"
michael@0 600 " document.links[0].__proto__.onmouseover = new Function("
michael@0 601 "\"window.status ="
michael@0 602 " this.href.substring(this.href.lastIndexOf('#') + 1)\");\n"
michael@0 603 "}\n"
michael@0 604 "</script>\n");
michael@0 605 fprintf(stdout, "%s starting at %s", program, ctime(&start));
michael@0 606 fflush(stdout);
michael@0 607 }
michael@0 608
michael@0 609 argc -= optind;
michael@0 610 argv += optind;
michael@0 611 if (argc == 0) {
michael@0 612 if (tmreader_eventloop(tmr, "-", my_tmevent_handler) <= 0)
michael@0 613 exit(1);
michael@0 614 } else {
michael@0 615 for (i = j = 0; i < argc; i++) {
michael@0 616 fp = fopen(argv[i], "r");
michael@0 617 if (!fp) {
michael@0 618 fprintf(stderr, "%s: can't open %s: %s\n",
michael@0 619 program, argv[i], strerror(errno));
michael@0 620 exit(1);
michael@0 621 }
michael@0 622 rv = tmreader_eventloop(tmr, argv[i], my_tmevent_handler);
michael@0 623 if (rv < 0)
michael@0 624 exit(1);
michael@0 625 if (rv > 0)
michael@0 626 j++;
michael@0 627 fclose(fp);
michael@0 628 }
michael@0 629 if (j == 0)
michael@0 630 exit(1);
michael@0 631 }
michael@0 632
michael@0 633 compute_callsite_totals(&tmr->calltree_root);
michael@0 634 walk_callsite_tree(&tmr->calltree_root, 0, 0, stdout);
michael@0 635
michael@0 636 if (js_mode) {
michael@0 637 fprintf(stdout,
michael@0 638 "<script language='javascript'>\n"
michael@0 639 "// direct and total byte and allocator-call counts\n"
michael@0 640 "var dbytes = %ld, tbytes = %ld,"
michael@0 641 " dallocs = %ld, tallocs = %ld;\n",
michael@0 642 (long) tmr->calltree_root.allocs.bytes.direct,
michael@0 643 (long) tmr->calltree_root.allocs.bytes.total,
michael@0 644 (long) tmr->calltree_root.allocs.calls.direct,
michael@0 645 (long) tmr->calltree_root.allocs.calls.total);
michael@0 646 }
michael@0 647
michael@0 648 dump_graph(tmr, tmr->libraries, "libraries", "Library", stdout);
michael@0 649 if (!js_mode)
michael@0 650 fputs("<hr>\n", stdout);
michael@0 651
michael@0 652 dump_graph(tmr, tmr->components, "classes", "Class or Component", stdout);
michael@0 653 if (js_mode || unified_output || function_dump) {
michael@0 654 if (js_mode || unified_output || strcmp(function_dump, "-") == 0) {
michael@0 655 fp = stdout;
michael@0 656 if (!js_mode)
michael@0 657 fputs("<hr>\n", fp);
michael@0 658 } else {
michael@0 659 struct stat sb, fsb;
michael@0 660
michael@0 661 fstat(fileno(stdout), &sb);
michael@0 662 if (stat(function_dump, &fsb) == 0 &&
michael@0 663 fsb.st_dev == sb.st_dev && fsb.st_ino == sb.st_ino) {
michael@0 664 fp = stdout;
michael@0 665 fputs("<hr>\n", fp);
michael@0 666 } else {
michael@0 667 fp = fopen(function_dump, "w");
michael@0 668 if (!fp) {
michael@0 669 fprintf(stderr, "%s: can't open %s: %s\n",
michael@0 670 program, function_dump, strerror(errno));
michael@0 671 exit(1);
michael@0 672 }
michael@0 673 }
michael@0 674 }
michael@0 675
michael@0 676 dump_graph(tmr, tmr->methods, "methods", "Function or Method", fp);
michael@0 677 if (fp != stdout)
michael@0 678 fclose(fp);
michael@0 679
michael@0 680 if (js_mode) {
michael@0 681 fputs("function viewnode(graph, index) {\n"
michael@0 682 " view.location = viewsrc();\n"
michael@0 683 "}\n"
michael@0 684 "function viewnodelink(graph, index) {\n"
michael@0 685 " var node = graph.nodes[index];\n"
michael@0 686 " return '<a href=\"javascript:viewnode('"
michael@0 687 " + graph.name.quote() + ', ' + node.sort"
michael@0 688 " + ')\" onmouseover=' + node.name.quote() + '>'"
michael@0 689 " + node.name + '</a>';\n"
michael@0 690 "}\n"
michael@0 691 "function search(expr) {\n"
michael@0 692 " var re = new RegExp(expr);\n"
michael@0 693 " var src = '';\n"
michael@0 694 " var graphs = [libraries, classes, methods]\n"
michael@0 695 " var nodes;\n"
michael@0 696 " for (var n = 0; n < (nodes = graphs[n].nodes).length; n++) {\n"
michael@0 697 " for (var i = 0; i < nodes.length; i++) {\n"
michael@0 698 " if (re.test(nodes[i].name))\n"
michael@0 699 " src += viewnodelink(graph, i) + '\\n';\n"
michael@0 700 " }\n"
michael@0 701 " }\n"
michael@0 702 " view.location = viewsrc();\n"
michael@0 703 "}\n"
michael@0 704 "function ctrlsrc() {\n"
michael@0 705 " return \"<form>\\n"
michael@0 706 "search: <input size=40 onchange='search(this.value)'>\\n"
michael@0 707 "</form>\\n\";\n"
michael@0 708 "}\n"
michael@0 709 "function viewsrc() {\n"
michael@0 710 " return 'hiiiii'\n"
michael@0 711 "}\n"
michael@0 712 "</script>\n"
michael@0 713 "<frameset rows='10%,*'>\n"
michael@0 714 " <frame name='ctrl' src='javascript:top.ctrlsrc()'>\n"
michael@0 715 " <frame name='view' src='javascript:top.viewsrc()'>\n"
michael@0 716 "</frameset>\n",
michael@0 717 stdout);
michael@0 718 }
michael@0 719 }
michael@0 720
michael@0 721 exit(0);
michael@0 722 }

mercurial