tools/trace-malloc/bloatblame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial