tools/trace-malloc/bloatblame.cpp

changeset 0
6474c204b198
     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&nbsp;(%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&nbsp;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 +}

mercurial