From 2d47b3e145d183d07159fc2c745e98a60c1ca13d Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Wed, 26 Jul 2000 00:24:08 +0000 Subject: [PATCH] - Remove include from bloatblame, it's no longer needed and the link(2) prototype it drags in was causing warnings whenever I used a local variable named 'link'. - Use uint32 for all quantities, and double for all differences, so that we can overflow without breaking deltas such as (busy == allocs - frees). This doesn't help sorting, however: if one of the sort keys has overflowed past 0 just a bit, while the other is a very large unsigned number, beware. - Separate graph link (half an edge, structurally speaking -- no per-edge stats) from graph edge, so that an edge is two links and some stats. This avoids bloat and copying in connect_nodes (which is soon to become generic and move to tmreader.[ch]). - Factor data structures better: we now have {allocs,frees} x {bytes,calls} x {direct, total}, declaring the second set as struct tmallcounts and the third as tmcounts. So, for example, total number of calls to allocators would be allocs.calls.total; bytes freed directly by a graphnode (library, component, or method) would be frees.bytes.direct. - Teach tmreader_eventloop about 'F' (TM_EVENT_FREE) events: it now updates the direct free byte and call counts for a method, its component, and its library when it reads the event. Of course, bloatblame ignores this info, because it is concerned only with bloat (total memory allocated). - Right-align numbers in the first (trace-malloc stats) table. git-svn-id: svn://10.0.0.236/trunk@74785 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/tools/trace-malloc/bloatblame.c | 232 +++++++++++++----------- mozilla/tools/trace-malloc/tmreader.c | 69 +++++-- mozilla/tools/trace-malloc/tmreader.h | 61 +++++-- mozilla/xpcom/base/bloatblame.c | 232 +++++++++++++----------- mozilla/xpcom/base/tmreader.c | 69 +++++-- mozilla/xpcom/base/tmreader.h | 61 +++++-- 6 files changed, 450 insertions(+), 274 deletions(-) diff --git a/mozilla/tools/trace-malloc/bloatblame.c b/mozilla/tools/trace-malloc/bloatblame.c index 7902b142a10..32383b0a27f 100644 --- a/mozilla/tools/trace-malloc/bloatblame.c +++ b/mozilla/tools/trace-malloc/bloatblame.c @@ -46,7 +46,6 @@ extern int optind; #endif #include #include -#include #include #include "prtypes.h" #include "prlog.h" @@ -61,54 +60,57 @@ static int js_mode = 0; static int do_tree_dump = 0; static int unified_output = 0; static char *function_dump = NULL; -static int32 min_subtotal = 0; +static uint32 min_subtotal = 0; static void connect_nodes(tmgraphnode *from, tmgraphnode *to, tmcallsite *site) { + tmgraphlink *link; tmgraphedge *edge; - for (edge = from->out; edge; edge = edge->next) { - if (edge->node == to) { + for (link = from->out; link; link = link->next) { + if (link->node == to) { /* * Say the stack looks like this: ... => JS => js => JS => js. * We must avoid overcounting JS=>js because the first edge total * includes the second JS=>js edge's total (which is because the * lower site's total includes all its kids' totals). */ + edge = TM_LINK_TO_EDGE(link, TM_EDGE_OUT_LINK); if (!to->low || to->low < from->low) { - edge[0].bytes.direct += site->bytes.direct; - edge[1].bytes.direct += site->bytes.direct; - edge[0].bytes.total += site->bytes.total; - edge[1].bytes.total += site->bytes.total; + /* Add the direct and total counts to edge->allocs. */ + edge->allocs.bytes.direct += site->allocs.bytes.direct; + edge->allocs.bytes.total += site->allocs.bytes.total; + edge->allocs.calls.direct += site->allocs.calls.direct; + edge->allocs.calls.total += site->allocs.calls.total; } return; } } - edge = (tmgraphedge*) malloc(2 * sizeof(tmgraphedge)); + + edge = (tmgraphedge*) malloc(sizeof(tmgraphedge)); if (!edge) { perror(program); exit(1); } - edge[0].node = to; - edge[0].next = from->out; - from->out = &edge[0]; - edge[1].node = from; - edge[1].next = to->in; - to->in = &edge[1]; - edge[0].bytes.direct = edge[1].bytes.direct = site->bytes.direct; - edge[0].bytes.total = edge[1].bytes.total = site->bytes.total; + edge->links[TM_EDGE_OUT_LINK].node = to; + edge->links[TM_EDGE_OUT_LINK].next = from->out; + from->out = &edge->links[TM_EDGE_OUT_LINK]; + edge->links[TM_EDGE_IN_LINK].node = from; + edge->links[TM_EDGE_IN_LINK].next = to->in; + to->in = &edge->links[TM_EDGE_IN_LINK]; + edge->allocs = site->allocs; } static void compute_callsite_totals(tmcallsite *site) { tmcallsite *kid; - site->bytes.total += site->bytes.direct; - site->allocs.total += site->allocs.direct; + site->allocs.bytes.total += site->allocs.bytes.direct; + site->allocs.calls.total += site->allocs.calls.direct; for (kid = site->kids; kid; kid = kid->siblings) { compute_callsite_totals(kid); - site->bytes.total += kid->bytes.total; - site->allocs.total += kid->allocs.total; + site->allocs.bytes.total += kid->allocs.bytes.total; + site->allocs.calls.total += kid->allocs.calls.total; } } @@ -127,8 +129,8 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp pmeth = parent->method; if (pmeth && pmeth != meth) { if (!meth->low) { - meth->bytes.total += site->bytes.total; - meth->allocs.total += site->allocs.total; + meth->allocs.bytes.total += site->allocs.bytes.total; + meth->allocs.calls.total += site->allocs.calls.total; } connect_nodes(pmeth, meth, site); @@ -137,8 +139,10 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp pcomp = pmeth->up; if (pcomp && pcomp != comp) { if (!comp->low) { - comp->bytes.total += site->bytes.total; - comp->allocs.total += site->allocs.total; + comp->allocs.bytes.total + += site->allocs.bytes.total; + comp->allocs.calls.total + += site->allocs.calls.total; } connect_nodes(pcomp, comp, site); @@ -147,8 +151,10 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp plib = pcomp->up; if (plib && plib != lib) { if (!lib->low) { - lib->bytes.total += site->bytes.total; - lib->allocs.total += site->allocs.total; + lib->allocs.bytes.total + += site->allocs.bytes.total; + lib->allocs.calls.total + += site->allocs.calls.total; } connect_nodes(plib, lib, site); } @@ -172,7 +178,8 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp fprintf(fp, "%c%*s%3d %3d %s %lu %ld\n", site->kids ? '+' : '-', level, "", level, kidnum, meth ? tmgraphnode_name(meth) : "???", - (unsigned long)site->bytes.direct, (long)site->bytes.total); + (unsigned long)site->allocs.bytes.direct, + (long)site->allocs.bytes.total); } nkids = 0; level++; @@ -195,8 +202,16 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp } } -/* Linked list bubble-sort (waterson and brendan went bald hacking this). */ -#define BUBBLE_SORT_LINKED_LIST(listp, nodetype) \ +/* + * Linked list bubble-sort (waterson and brendan went bald hacking this). + * + * Sort the list in non-increasing order, using the expression passed as the + * 'lessthan' formal macro parameter. This expression should use 'curr' as + * the pointer to the current node (of type nodetype) and 'next' as the next + * node pointer. It should return true if curr is less than next, and false + * otherwise. + */ +#define BUBBLE_SORT_LINKED_LIST(listp, nodetype, lessthan) \ PR_BEGIN_MACRO \ nodetype *curr, **currp, *next, **nextp, *tmp; \ \ @@ -204,7 +219,7 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp while ((curr = *currp) != NULL && curr->next) { \ nextp = &curr->next; \ while ((next = *nextp) != NULL) { \ - if (curr->bytes.total < next->bytes.total) { \ + if (lessthan) { \ tmp = curr->next; \ *currp = tmp; \ if (tmp == next) { \ @@ -234,7 +249,8 @@ static PRIntn tabulate_node(PLHashEntry *he, PRIntn i, void *arg) tmgraphnode **table = (tmgraphnode**) arg; table[i] = node; - BUBBLE_SORT_LINKED_LIST(&node->down, tmgraphnode); + BUBBLE_SORT_LINKED_LIST(&node->down, tmgraphnode, + (curr->allocs.bytes.total < next->allocs.bytes.total)); return HT_ENUMERATE_NEXT; } @@ -242,18 +258,18 @@ static PRIntn tabulate_node(PLHashEntry *he, PRIntn i, void *arg) static int node_table_compare(const void *p1, const void *p2) { const tmgraphnode *node1, *node2; - int32 key1, key2; + uint32 key1, key2; node1 = *(const tmgraphnode**) p1; node2 = *(const tmgraphnode**) p2; if (sort_by_direct) { - key1 = node1->bytes.direct; - key2 = node2->bytes.direct; + key1 = node1->allocs.bytes.direct; + key2 = node2->allocs.bytes.direct; } else { - key1 = node1->bytes.total; - key2 = node2->bytes.total; + key1 = node1->allocs.bytes.total; + key2 = node2->allocs.bytes.total; } - return key2 - key1; + return (key2 < key1) ? -1 : (key2 > key1) ? 1 : 0; } static int mean_size_compare(const void *p1, const void *p2) @@ -263,12 +279,12 @@ static int mean_size_compare(const void *p1, const void *p2) node1 = *(const tmgraphnode**) p1; node2 = *(const tmgraphnode**) p2; - div1 = (double)node1->allocs.direct; - div2 = (double)node2->allocs.direct; + div1 = (double)node1->allocs.calls.direct; + div2 = (double)node2->allocs.calls.direct; if (div1 == 0 || div2 == 0) return div2 - div1; - key1 = (double)node1->bytes.direct / div1; - key2 = (double)node2->bytes.direct / div2; + key1 = (double)node1->allocs.bytes.direct / div1; + key2 = (double)node2->allocs.bytes.direct / div2; if (key1 < key2) return 1; if (key1 > key2) @@ -289,50 +305,57 @@ static const char *prettybig(uint32 num, char *buf, size_t limit) return buf; } -static double percent(int32 num, int32 total) +static double percent(uint32 num, uint32 total) { if (num == 0) return 0.0; return ((double) num * 100) / (double) total; } -static void sort_graphedge_list(tmgraphedge **listp) +static void sort_graphlink_list(tmgraphlink **listp, int which) { - BUBBLE_SORT_LINKED_LIST(listp, tmgraphedge); + BUBBLE_SORT_LINKED_LIST(listp, tmgraphlink, + (TM_LINK_TO_EDGE(curr, which)->allocs.bytes.total + < TM_LINK_TO_EDGE(next, which)->allocs.bytes.total)); } -static void dump_graphedge_list(tmgraphedge *list, const char *name, FILE *fp) +static void dump_graphlink_list(tmgraphlink *list, int which, const char *name, + FILE *fp) { tmcounts bytes; + tmgraphlink *link; tmgraphedge *edge; char buf[16]; bytes.direct = bytes.total = 0; - for (edge = list; edge; edge = edge->next) { - bytes.direct += edge->bytes.direct; - bytes.total += edge->bytes.total; + for (link = list; link; link = link->next) { + edge = TM_LINK_TO_EDGE(link, which); + bytes.direct += edge->allocs.bytes.direct; + bytes.total += edge->allocs.bytes.total; } if (js_mode) { fprintf(fp, " %s:{dbytes:%ld, tbytes:%ld, edges:[\n", name, (long) bytes.direct, (long) bytes.total); - for (edge = list; edge; edge = edge->next) { + for (link = list; link; link = link->next) { + edge = TM_LINK_TO_EDGE(link, which); fprintf(fp, " {node:%d, dbytes:%ld, tbytes:%ld},\n", - edge->node->sort, - (long) edge->bytes.direct, - (long) edge->bytes.total); + link->node->sort, + (long) edge->allocs.bytes.direct, + (long) edge->allocs.bytes.total); } fputs(" ]},\n", fp); } else { fputs("", fp); - for (edge = list; edge; edge = edge->next) { + for (link = list; link; link = link->next) { + edge = TM_LINK_TO_EDGE(link, which); fprintf(fp, "%s (%1.2f%%)\n", - tmgraphnode_name(edge->node), - prettybig(edge->bytes.total, buf, sizeof buf), - percent(edge->bytes.total, bytes.total)); + tmgraphnode_name(link->node), + prettybig(edge->allocs.bytes.total, buf, sizeof buf), + percent(edge->allocs.bytes.total, bytes.total)); } fputs("", fp); } @@ -381,7 +404,7 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, for (i = 0; i < count; i++) { /* Don't bother with truly puny nodes. */ node = table[i]; - if (node->bytes.total < min_subtotal) + if (node->allocs.bytes.total < min_subtotal) break; name = tmgraphnode_name(node); @@ -390,16 +413,18 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, " {name:'%s', dbytes:%ld, tbytes:%ld," " dallocs:%ld, tallocs:%ld,\n", name, - (long) node->bytes.direct, (long) node->bytes.total, - (long) node->allocs.direct, (long) node->allocs.total); + (long) node->allocs.bytes.direct, + (long) node->allocs.bytes.total, + (long) node->allocs.calls.direct, + (long) node->allocs.calls.total); } else { namelen = strlen(name); fprintf(fp, "" "%.*s%s", name, - (namelen > 45) ? 45 : (int)namelen, name, - (namelen > 45) ? "..." : ""); + (namelen > 40) ? 40 : (int)namelen, name, + (namelen > 40) ? "..." : ""); if (node->down) { fprintf(fp, "down", @@ -417,20 +442,25 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, fprintf(fp, "%s/%s (%1.2f%%/%1.2f%%)" "%s/%s (%1.2f%%/%1.2f%%)", - prettybig(node->bytes.total, buf1, sizeof buf1), - prettybig(node->bytes.direct, buf2, sizeof buf2), - percent(node->bytes.total, tmr->calltree_root.bytes.total), - percent(node->bytes.direct, tmr->calltree_root.bytes.total), - prettybig(node->allocs.total, buf3, sizeof buf3), - prettybig(node->allocs.direct, buf4, sizeof buf4), - percent(node->allocs.total, tmr->calltree_root.allocs.total), - percent(node->allocs.direct, tmr->calltree_root.allocs.total)); + prettybig(node->allocs.bytes.total, buf1, sizeof buf1), + prettybig(node->allocs.bytes.direct, buf2, sizeof buf2), + percent(node->allocs.bytes.total, + tmr->calltree_root.allocs.bytes.total), + percent(node->allocs.bytes.direct, + tmr->calltree_root.allocs.bytes.total), + prettybig(node->allocs.calls.total, buf3, sizeof buf3), + prettybig(node->allocs.calls.direct, buf4, sizeof buf4), + percent(node->allocs.calls.total, + tmr->calltree_root.allocs.calls.total), + percent(node->allocs.calls.direct, + tmr->calltree_root.allocs.calls.total)); } - sort_graphedge_list(&node->in); - dump_graphedge_list(node->in, "fin", fp); /* 'in' is a JS keyword! */ - sort_graphedge_list(&node->out); - dump_graphedge_list(node->out, "out", fp); + /* NB: we must use 'fin' because 'in' is a JS keyword! */ + sort_graphlink_list(&node->in, TM_EDGE_IN_LINK); + dump_graphlink_list(node->in, TM_EDGE_IN_LINK, "fin", fp); + sort_graphlink_list(&node->out, TM_EDGE_OUT_LINK); + dump_graphlink_list(node->out, TM_EDGE_OUT_LINK, "out", fp); if (js_mode) fputs(" },\n", fp); @@ -460,12 +490,12 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, double allocs, bytes, mean, variance, sigma; node = table[i]; - allocs = (double)node->allocs.direct; + allocs = (double)node->allocs.calls.direct; if (!allocs) continue; /* Compute direct-size mean and standard deviation. */ - bytes = (double)node->bytes.direct; + bytes = (double)node->allocs.bytes.direct; mean = bytes / allocs; variance = allocs * node->sqsum - bytes * bytes; if (variance < 0 || allocs == 1) @@ -487,7 +517,7 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, (namelen > 65) ? "..." : "", prettybig((uint32)mean, buf1, sizeof buf1), prettybig((uint32)sigma, buf2, sizeof buf2), - prettybig(node->allocs.direct, buf3, sizeof buf3)); + prettybig(node->allocs.calls.direct, buf3, sizeof buf3)); } fputs("\n", fp); } @@ -504,26 +534,26 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event) fprintf(stdout, "

" "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" "
CounterValue
maximum actual stack depth%lu
maximum callsite tree depth%lu
number of parent callsites%lu
maximum kids per parent%lu
hits looking for a kid%lu
misses looking for a kid%lu
steps over other kids%lu
callsite recurrences%lu
number of stack backtraces%lu
backtrace failures%lu
backtrace malloc failures%lu
backtrace dladdr failures%lu
malloc calls%lu
malloc failures%lu
calloc calls%lu
calloc failures%lu
realloc calls%lu
realloc failures%lu
free calls%lu
free(null) calls%lu
maximum actual stack depth%lu
maximum callsite tree depth%lu
number of parent callsites%lu
maximum kids per parent%lu
hits looking for a kid%lu
misses looking for a kid%lu
steps over other kids%lu
callsite recurrences%lu
number of stack backtraces%lu
backtrace failures%lu
backtrace malloc failures%lu
backtrace dladdr failures%lu
malloc calls%lu
malloc failures%lu
calloc calls%lu
calloc failures%lu
realloc calls%lu
realloc failures%lu
free calls%lu
free(null) calls%lu
", (unsigned long) event->u.stats.tmstats.calltree_maxstack, (unsigned long) event->u.stats.tmstats.calltree_maxdepth, @@ -664,10 +694,10 @@ int main(int argc, char **argv) "// direct and total byte and allocator-call counts\n" "var dbytes = %ld, tbytes = %ld," " dallocs = %ld, tallocs = %ld;\n", - (long) tmr->calltree_root.bytes.direct, - (long) tmr->calltree_root.bytes.total, - (long) tmr->calltree_root.allocs.direct, - (long) tmr->calltree_root.allocs.total); + (long) tmr->calltree_root.allocs.bytes.direct, + (long) tmr->calltree_root.allocs.bytes.total, + (long) tmr->calltree_root.allocs.calls.direct, + (long) tmr->calltree_root.allocs.calls.total); } dump_graph(tmr, tmr->libraries, "libraries", "Library", stdout); diff --git a/mozilla/tools/trace-malloc/tmreader.c b/mozilla/tools/trace-malloc/tmreader.c index 4e99bdabef9..e4ed543d008 100644 --- a/mozilla/tools/trace-malloc/tmreader.c +++ b/mozilla/tools/trace-malloc/tmreader.c @@ -248,8 +248,10 @@ static PLHashEntry *graphnode_allocentry(void *pool, const void *key) node->in = node->out = NULL; node->up = node->down = node->next = NULL; node->low = 0; - node->bytes.direct = node->bytes.total = 0; - node->allocs.direct = node->allocs.total = 0; + node->allocs.bytes.direct = node->allocs.bytes.total = 0; + node->allocs.calls.direct = node->allocs.calls.total = 0; + node->frees.bytes.direct = node->frees.bytes.total = 0; + node->frees.calls.direct = node->frees.calls.total = 0; node->sqsum = 0; node->sort = -1; return &node->entry; @@ -508,8 +510,10 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, *PL_HashTableRawLookup(tmr->methods, mhash, mkey); site->method = meth; site->offset = event.u.site.offset; - site->bytes.direct = site->bytes.total = 0; - site->allocs.direct = site->allocs.total = 0; + site->allocs.bytes.direct = site->allocs.bytes.total = 0; + site->allocs.calls.direct = site->allocs.calls.total = 0; + site->frees.bytes.direct = site->frees.bytes.total = 0; + site->frees.calls.direct = site->frees.calls.total = 0; break; } @@ -517,9 +521,9 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, case TM_EVENT_CALLOC: case TM_EVENT_REALLOC: { tmcallsite *site; - int32 size, oldsize, delta; + uint32 size, oldsize; + double delta, sqdelta, sqszdelta; tmgraphnode *meth, *comp, *lib; - double sqdelta, sqszdelta; site = tmreader_callsite(tmr, event.serial); if (!site) { @@ -528,15 +532,15 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, continue; } - size = (int32)event.u.alloc.size; - oldsize = (int32)event.u.alloc.oldsize; - delta = size - oldsize; - site->bytes.direct += delta; + size = event.u.alloc.size; + oldsize = event.u.alloc.oldsize; + delta = (double)size - (double)oldsize; + site->allocs.bytes.direct += delta; if (event.type != TM_EVENT_REALLOC) - site->allocs.direct++; + site->allocs.calls.direct++; meth = site->method; if (meth) { - meth->bytes.direct += delta; + meth->allocs.bytes.direct += delta; sqdelta = delta * delta; if (event.type == TM_EVENT_REALLOC) { sqszdelta = ((double)size * size) @@ -544,25 +548,25 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, meth->sqsum += sqszdelta; } else { meth->sqsum += sqdelta; - meth->allocs.direct++; + meth->allocs.calls.direct++; } comp = meth->up; if (comp) { - comp->bytes.direct += delta; + comp->allocs.bytes.direct += delta; if (event.type == TM_EVENT_REALLOC) { comp->sqsum += sqszdelta; } else { comp->sqsum += sqdelta; - comp->allocs.direct++; + comp->allocs.calls.direct++; } lib = comp->up; if (lib) { - lib->bytes.direct += delta; + lib->allocs.bytes.direct += delta; if (event.type == TM_EVENT_REALLOC) { lib->sqsum += sqszdelta; } else { lib->sqsum += sqdelta; - lib->allocs.direct++; + lib->allocs.calls.direct++; } } } @@ -570,8 +574,37 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, break; } - case TM_EVENT_FREE: + case TM_EVENT_FREE: { + tmcallsite *site; + uint32 size; + tmgraphnode *meth, *comp, *lib; + + site = tmreader_callsite(tmr, event.serial); + if (!site) { + fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n", + tmr->program, event.type, (unsigned long) event.serial); + continue; + } + size = event.u.alloc.size; + site->frees.bytes.direct += size; + site->frees.calls.direct++; + meth = site->method; + if (meth) { + meth->frees.bytes.direct += size; + meth->frees.calls.direct++; + comp = meth->up; + if (comp) { + comp->frees.bytes.direct += size; + comp->frees.calls.direct++; + lib = comp->up; + if (lib) { + lib->frees.bytes.direct += size; + lib->frees.calls.direct++; + } + } + } break; + } case TM_EVENT_STATS: break; diff --git a/mozilla/tools/trace-malloc/tmreader.h b/mozilla/tools/trace-malloc/tmreader.h index f2489186079..d33bc711819 100644 --- a/mozilla/tools/trace-malloc/tmreader.h +++ b/mozilla/tools/trace-malloc/tmreader.h @@ -44,6 +44,8 @@ PR_BEGIN_EXTERN_C typedef struct tmreader tmreader; typedef struct tmevent tmevent; typedef struct tmcounts tmcounts; +typedef struct tmallcounts tmallcounts; +typedef struct tmgraphlink tmgraphlink; typedef struct tmgraphedge tmgraphedge; typedef struct tmgraphnode tmgraphnode; typedef struct tmcallsite tmcallsite; @@ -75,20 +77,25 @@ struct tmevent { }; struct tmcounts { - int32 direct; /* things allocated by this node's code */ - int32 total; /* direct + things from all descendents */ + uint32 direct; /* things allocated by this node's code */ + uint32 total; /* direct + things from all descendents */ +}; + +struct tmallcounts { + tmcounts bytes; + tmcounts calls; }; struct tmgraphnode { PLHashEntry entry; /* key is serial or name, value must be name */ - tmgraphedge *in; - tmgraphedge *out; + tmgraphlink *in; + tmgraphlink *out; tmgraphnode *up; /* parent in supergraph, e.g., JS for JS_*() */ tmgraphnode *down; /* subgraph kids, declining bytes.total order */ tmgraphnode *next; /* next kid in supergraph node's down list */ int low; /* 0 or lowest current tree walk level */ - tmcounts bytes; /* bytes (direct and total) allocated */ - tmcounts allocs; /* number of allocations */ + tmallcounts allocs; + tmallcounts frees; double sqsum; /* sum of squared bytes.direct */ int sort; /* sorted index in node table, -1 if no table */ }; @@ -98,21 +105,39 @@ struct tmgraphnode { #define tmlibrary_serial(lib) ((uint32) (lib)->entry.key) #define tmcomponent_name(comp) ((const char*) (comp)->entry.key) -struct tmgraphedge { - tmgraphedge *next; - tmgraphnode *node; - tmcounts bytes; +/* Half a graphedge, not including per-edge allocation stats. */ +struct tmgraphlink { + tmgraphlink *next; /* next fanning out from or into a node */ + tmgraphnode *node; /* the other node (to if OUT, from if IN) */ }; +/* + * It's safe to downcast a "from" tmgraphlink (one linked from a node's out + * pointer) to tmgraphedge. To go from an "out" (linked via tmgraphedge.from) + * or "in" (linked via tmgraphedge.to) list link to its containing edge, use + * TM_LINK_TO_EDGE(link, which). + */ +struct tmgraphedge { + tmgraphlink links[2]; + tmallcounts allocs; + tmallcounts frees; +}; + +/* Indices into tmgraphedge.links -- out must come first. */ +#define TM_EDGE_OUT_LINK 0 +#define TM_EDGE_IN_LINK 1 + +#define TM_LINK_TO_EDGE(link,which) ((tmgraphedge*) &(link)[-(which)]) + struct tmcallsite { - PLHashEntry entry; - tmcallsite *parent; - tmcallsite *siblings; - tmcallsite *kids; - tmgraphnode *method; - uint32 offset; - tmcounts bytes; - tmcounts allocs; + PLHashEntry entry; /* key is site serial number */ + tmcallsite *parent; /* calling site */ + tmcallsite *siblings; /* other sites reached from parent */ + tmcallsite *kids; /* sites reached from here */ + tmgraphnode *method; /* method node in tmr->methods graph */ + uint32 offset; /* pc offset from start of method */ + tmallcounts allocs; + tmallcounts frees; }; struct tmreader { diff --git a/mozilla/xpcom/base/bloatblame.c b/mozilla/xpcom/base/bloatblame.c index 7902b142a10..32383b0a27f 100644 --- a/mozilla/xpcom/base/bloatblame.c +++ b/mozilla/xpcom/base/bloatblame.c @@ -46,7 +46,6 @@ extern int optind; #endif #include #include -#include #include #include "prtypes.h" #include "prlog.h" @@ -61,54 +60,57 @@ static int js_mode = 0; static int do_tree_dump = 0; static int unified_output = 0; static char *function_dump = NULL; -static int32 min_subtotal = 0; +static uint32 min_subtotal = 0; static void connect_nodes(tmgraphnode *from, tmgraphnode *to, tmcallsite *site) { + tmgraphlink *link; tmgraphedge *edge; - for (edge = from->out; edge; edge = edge->next) { - if (edge->node == to) { + for (link = from->out; link; link = link->next) { + if (link->node == to) { /* * Say the stack looks like this: ... => JS => js => JS => js. * We must avoid overcounting JS=>js because the first edge total * includes the second JS=>js edge's total (which is because the * lower site's total includes all its kids' totals). */ + edge = TM_LINK_TO_EDGE(link, TM_EDGE_OUT_LINK); if (!to->low || to->low < from->low) { - edge[0].bytes.direct += site->bytes.direct; - edge[1].bytes.direct += site->bytes.direct; - edge[0].bytes.total += site->bytes.total; - edge[1].bytes.total += site->bytes.total; + /* Add the direct and total counts to edge->allocs. */ + edge->allocs.bytes.direct += site->allocs.bytes.direct; + edge->allocs.bytes.total += site->allocs.bytes.total; + edge->allocs.calls.direct += site->allocs.calls.direct; + edge->allocs.calls.total += site->allocs.calls.total; } return; } } - edge = (tmgraphedge*) malloc(2 * sizeof(tmgraphedge)); + + edge = (tmgraphedge*) malloc(sizeof(tmgraphedge)); if (!edge) { perror(program); exit(1); } - edge[0].node = to; - edge[0].next = from->out; - from->out = &edge[0]; - edge[1].node = from; - edge[1].next = to->in; - to->in = &edge[1]; - edge[0].bytes.direct = edge[1].bytes.direct = site->bytes.direct; - edge[0].bytes.total = edge[1].bytes.total = site->bytes.total; + edge->links[TM_EDGE_OUT_LINK].node = to; + edge->links[TM_EDGE_OUT_LINK].next = from->out; + from->out = &edge->links[TM_EDGE_OUT_LINK]; + edge->links[TM_EDGE_IN_LINK].node = from; + edge->links[TM_EDGE_IN_LINK].next = to->in; + to->in = &edge->links[TM_EDGE_IN_LINK]; + edge->allocs = site->allocs; } static void compute_callsite_totals(tmcallsite *site) { tmcallsite *kid; - site->bytes.total += site->bytes.direct; - site->allocs.total += site->allocs.direct; + site->allocs.bytes.total += site->allocs.bytes.direct; + site->allocs.calls.total += site->allocs.calls.direct; for (kid = site->kids; kid; kid = kid->siblings) { compute_callsite_totals(kid); - site->bytes.total += kid->bytes.total; - site->allocs.total += kid->allocs.total; + site->allocs.bytes.total += kid->allocs.bytes.total; + site->allocs.calls.total += kid->allocs.calls.total; } } @@ -127,8 +129,8 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp pmeth = parent->method; if (pmeth && pmeth != meth) { if (!meth->low) { - meth->bytes.total += site->bytes.total; - meth->allocs.total += site->allocs.total; + meth->allocs.bytes.total += site->allocs.bytes.total; + meth->allocs.calls.total += site->allocs.calls.total; } connect_nodes(pmeth, meth, site); @@ -137,8 +139,10 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp pcomp = pmeth->up; if (pcomp && pcomp != comp) { if (!comp->low) { - comp->bytes.total += site->bytes.total; - comp->allocs.total += site->allocs.total; + comp->allocs.bytes.total + += site->allocs.bytes.total; + comp->allocs.calls.total + += site->allocs.calls.total; } connect_nodes(pcomp, comp, site); @@ -147,8 +151,10 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp plib = pcomp->up; if (plib && plib != lib) { if (!lib->low) { - lib->bytes.total += site->bytes.total; - lib->allocs.total += site->allocs.total; + lib->allocs.bytes.total + += site->allocs.bytes.total; + lib->allocs.calls.total + += site->allocs.calls.total; } connect_nodes(plib, lib, site); } @@ -172,7 +178,8 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp fprintf(fp, "%c%*s%3d %3d %s %lu %ld\n", site->kids ? '+' : '-', level, "", level, kidnum, meth ? tmgraphnode_name(meth) : "???", - (unsigned long)site->bytes.direct, (long)site->bytes.total); + (unsigned long)site->allocs.bytes.direct, + (long)site->allocs.bytes.total); } nkids = 0; level++; @@ -195,8 +202,16 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp } } -/* Linked list bubble-sort (waterson and brendan went bald hacking this). */ -#define BUBBLE_SORT_LINKED_LIST(listp, nodetype) \ +/* + * Linked list bubble-sort (waterson and brendan went bald hacking this). + * + * Sort the list in non-increasing order, using the expression passed as the + * 'lessthan' formal macro parameter. This expression should use 'curr' as + * the pointer to the current node (of type nodetype) and 'next' as the next + * node pointer. It should return true if curr is less than next, and false + * otherwise. + */ +#define BUBBLE_SORT_LINKED_LIST(listp, nodetype, lessthan) \ PR_BEGIN_MACRO \ nodetype *curr, **currp, *next, **nextp, *tmp; \ \ @@ -204,7 +219,7 @@ static void walk_callsite_tree(tmcallsite *site, int level, int kidnum, FILE *fp while ((curr = *currp) != NULL && curr->next) { \ nextp = &curr->next; \ while ((next = *nextp) != NULL) { \ - if (curr->bytes.total < next->bytes.total) { \ + if (lessthan) { \ tmp = curr->next; \ *currp = tmp; \ if (tmp == next) { \ @@ -234,7 +249,8 @@ static PRIntn tabulate_node(PLHashEntry *he, PRIntn i, void *arg) tmgraphnode **table = (tmgraphnode**) arg; table[i] = node; - BUBBLE_SORT_LINKED_LIST(&node->down, tmgraphnode); + BUBBLE_SORT_LINKED_LIST(&node->down, tmgraphnode, + (curr->allocs.bytes.total < next->allocs.bytes.total)); return HT_ENUMERATE_NEXT; } @@ -242,18 +258,18 @@ static PRIntn tabulate_node(PLHashEntry *he, PRIntn i, void *arg) static int node_table_compare(const void *p1, const void *p2) { const tmgraphnode *node1, *node2; - int32 key1, key2; + uint32 key1, key2; node1 = *(const tmgraphnode**) p1; node2 = *(const tmgraphnode**) p2; if (sort_by_direct) { - key1 = node1->bytes.direct; - key2 = node2->bytes.direct; + key1 = node1->allocs.bytes.direct; + key2 = node2->allocs.bytes.direct; } else { - key1 = node1->bytes.total; - key2 = node2->bytes.total; + key1 = node1->allocs.bytes.total; + key2 = node2->allocs.bytes.total; } - return key2 - key1; + return (key2 < key1) ? -1 : (key2 > key1) ? 1 : 0; } static int mean_size_compare(const void *p1, const void *p2) @@ -263,12 +279,12 @@ static int mean_size_compare(const void *p1, const void *p2) node1 = *(const tmgraphnode**) p1; node2 = *(const tmgraphnode**) p2; - div1 = (double)node1->allocs.direct; - div2 = (double)node2->allocs.direct; + div1 = (double)node1->allocs.calls.direct; + div2 = (double)node2->allocs.calls.direct; if (div1 == 0 || div2 == 0) return div2 - div1; - key1 = (double)node1->bytes.direct / div1; - key2 = (double)node2->bytes.direct / div2; + key1 = (double)node1->allocs.bytes.direct / div1; + key2 = (double)node2->allocs.bytes.direct / div2; if (key1 < key2) return 1; if (key1 > key2) @@ -289,50 +305,57 @@ static const char *prettybig(uint32 num, char *buf, size_t limit) return buf; } -static double percent(int32 num, int32 total) +static double percent(uint32 num, uint32 total) { if (num == 0) return 0.0; return ((double) num * 100) / (double) total; } -static void sort_graphedge_list(tmgraphedge **listp) +static void sort_graphlink_list(tmgraphlink **listp, int which) { - BUBBLE_SORT_LINKED_LIST(listp, tmgraphedge); + BUBBLE_SORT_LINKED_LIST(listp, tmgraphlink, + (TM_LINK_TO_EDGE(curr, which)->allocs.bytes.total + < TM_LINK_TO_EDGE(next, which)->allocs.bytes.total)); } -static void dump_graphedge_list(tmgraphedge *list, const char *name, FILE *fp) +static void dump_graphlink_list(tmgraphlink *list, int which, const char *name, + FILE *fp) { tmcounts bytes; + tmgraphlink *link; tmgraphedge *edge; char buf[16]; bytes.direct = bytes.total = 0; - for (edge = list; edge; edge = edge->next) { - bytes.direct += edge->bytes.direct; - bytes.total += edge->bytes.total; + for (link = list; link; link = link->next) { + edge = TM_LINK_TO_EDGE(link, which); + bytes.direct += edge->allocs.bytes.direct; + bytes.total += edge->allocs.bytes.total; } if (js_mode) { fprintf(fp, " %s:{dbytes:%ld, tbytes:%ld, edges:[\n", name, (long) bytes.direct, (long) bytes.total); - for (edge = list; edge; edge = edge->next) { + for (link = list; link; link = link->next) { + edge = TM_LINK_TO_EDGE(link, which); fprintf(fp, " {node:%d, dbytes:%ld, tbytes:%ld},\n", - edge->node->sort, - (long) edge->bytes.direct, - (long) edge->bytes.total); + link->node->sort, + (long) edge->allocs.bytes.direct, + (long) edge->allocs.bytes.total); } fputs(" ]},\n", fp); } else { fputs("", fp); - for (edge = list; edge; edge = edge->next) { + for (link = list; link; link = link->next) { + edge = TM_LINK_TO_EDGE(link, which); fprintf(fp, "%s (%1.2f%%)\n", - tmgraphnode_name(edge->node), - prettybig(edge->bytes.total, buf, sizeof buf), - percent(edge->bytes.total, bytes.total)); + tmgraphnode_name(link->node), + prettybig(edge->allocs.bytes.total, buf, sizeof buf), + percent(edge->allocs.bytes.total, bytes.total)); } fputs("", fp); } @@ -381,7 +404,7 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, for (i = 0; i < count; i++) { /* Don't bother with truly puny nodes. */ node = table[i]; - if (node->bytes.total < min_subtotal) + if (node->allocs.bytes.total < min_subtotal) break; name = tmgraphnode_name(node); @@ -390,16 +413,18 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, " {name:'%s', dbytes:%ld, tbytes:%ld," " dallocs:%ld, tallocs:%ld,\n", name, - (long) node->bytes.direct, (long) node->bytes.total, - (long) node->allocs.direct, (long) node->allocs.total); + (long) node->allocs.bytes.direct, + (long) node->allocs.bytes.total, + (long) node->allocs.calls.direct, + (long) node->allocs.calls.total); } else { namelen = strlen(name); fprintf(fp, "" "%.*s%s", name, - (namelen > 45) ? 45 : (int)namelen, name, - (namelen > 45) ? "..." : ""); + (namelen > 40) ? 40 : (int)namelen, name, + (namelen > 40) ? "..." : ""); if (node->down) { fprintf(fp, "down", @@ -417,20 +442,25 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, fprintf(fp, "%s/%s (%1.2f%%/%1.2f%%)" "%s/%s (%1.2f%%/%1.2f%%)", - prettybig(node->bytes.total, buf1, sizeof buf1), - prettybig(node->bytes.direct, buf2, sizeof buf2), - percent(node->bytes.total, tmr->calltree_root.bytes.total), - percent(node->bytes.direct, tmr->calltree_root.bytes.total), - prettybig(node->allocs.total, buf3, sizeof buf3), - prettybig(node->allocs.direct, buf4, sizeof buf4), - percent(node->allocs.total, tmr->calltree_root.allocs.total), - percent(node->allocs.direct, tmr->calltree_root.allocs.total)); + prettybig(node->allocs.bytes.total, buf1, sizeof buf1), + prettybig(node->allocs.bytes.direct, buf2, sizeof buf2), + percent(node->allocs.bytes.total, + tmr->calltree_root.allocs.bytes.total), + percent(node->allocs.bytes.direct, + tmr->calltree_root.allocs.bytes.total), + prettybig(node->allocs.calls.total, buf3, sizeof buf3), + prettybig(node->allocs.calls.direct, buf4, sizeof buf4), + percent(node->allocs.calls.total, + tmr->calltree_root.allocs.calls.total), + percent(node->allocs.calls.direct, + tmr->calltree_root.allocs.calls.total)); } - sort_graphedge_list(&node->in); - dump_graphedge_list(node->in, "fin", fp); /* 'in' is a JS keyword! */ - sort_graphedge_list(&node->out); - dump_graphedge_list(node->out, "out", fp); + /* NB: we must use 'fin' because 'in' is a JS keyword! */ + sort_graphlink_list(&node->in, TM_EDGE_IN_LINK); + dump_graphlink_list(node->in, TM_EDGE_IN_LINK, "fin", fp); + sort_graphlink_list(&node->out, TM_EDGE_OUT_LINK); + dump_graphlink_list(node->out, TM_EDGE_OUT_LINK, "out", fp); if (js_mode) fputs(" },\n", fp); @@ -460,12 +490,12 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, double allocs, bytes, mean, variance, sigma; node = table[i]; - allocs = (double)node->allocs.direct; + allocs = (double)node->allocs.calls.direct; if (!allocs) continue; /* Compute direct-size mean and standard deviation. */ - bytes = (double)node->bytes.direct; + bytes = (double)node->allocs.bytes.direct; mean = bytes / allocs; variance = allocs * node->sqsum - bytes * bytes; if (variance < 0 || allocs == 1) @@ -487,7 +517,7 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname, (namelen > 65) ? "..." : "", prettybig((uint32)mean, buf1, sizeof buf1), prettybig((uint32)sigma, buf2, sizeof buf2), - prettybig(node->allocs.direct, buf3, sizeof buf3)); + prettybig(node->allocs.calls.direct, buf3, sizeof buf3)); } fputs("\n", fp); } @@ -504,26 +534,26 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event) fprintf(stdout, "

" "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" - "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" "
CounterValue
maximum actual stack depth%lu
maximum callsite tree depth%lu
number of parent callsites%lu
maximum kids per parent%lu
hits looking for a kid%lu
misses looking for a kid%lu
steps over other kids%lu
callsite recurrences%lu
number of stack backtraces%lu
backtrace failures%lu
backtrace malloc failures%lu
backtrace dladdr failures%lu
malloc calls%lu
malloc failures%lu
calloc calls%lu
calloc failures%lu
realloc calls%lu
realloc failures%lu
free calls%lu
free(null) calls%lu
maximum actual stack depth%lu
maximum callsite tree depth%lu
number of parent callsites%lu
maximum kids per parent%lu
hits looking for a kid%lu
misses looking for a kid%lu
steps over other kids%lu
callsite recurrences%lu
number of stack backtraces%lu
backtrace failures%lu
backtrace malloc failures%lu
backtrace dladdr failures%lu
malloc calls%lu
malloc failures%lu
calloc calls%lu
calloc failures%lu
realloc calls%lu
realloc failures%lu
free calls%lu
free(null) calls%lu
", (unsigned long) event->u.stats.tmstats.calltree_maxstack, (unsigned long) event->u.stats.tmstats.calltree_maxdepth, @@ -664,10 +694,10 @@ int main(int argc, char **argv) "// direct and total byte and allocator-call counts\n" "var dbytes = %ld, tbytes = %ld," " dallocs = %ld, tallocs = %ld;\n", - (long) tmr->calltree_root.bytes.direct, - (long) tmr->calltree_root.bytes.total, - (long) tmr->calltree_root.allocs.direct, - (long) tmr->calltree_root.allocs.total); + (long) tmr->calltree_root.allocs.bytes.direct, + (long) tmr->calltree_root.allocs.bytes.total, + (long) tmr->calltree_root.allocs.calls.direct, + (long) tmr->calltree_root.allocs.calls.total); } dump_graph(tmr, tmr->libraries, "libraries", "Library", stdout); diff --git a/mozilla/xpcom/base/tmreader.c b/mozilla/xpcom/base/tmreader.c index 4e99bdabef9..e4ed543d008 100644 --- a/mozilla/xpcom/base/tmreader.c +++ b/mozilla/xpcom/base/tmreader.c @@ -248,8 +248,10 @@ static PLHashEntry *graphnode_allocentry(void *pool, const void *key) node->in = node->out = NULL; node->up = node->down = node->next = NULL; node->low = 0; - node->bytes.direct = node->bytes.total = 0; - node->allocs.direct = node->allocs.total = 0; + node->allocs.bytes.direct = node->allocs.bytes.total = 0; + node->allocs.calls.direct = node->allocs.calls.total = 0; + node->frees.bytes.direct = node->frees.bytes.total = 0; + node->frees.calls.direct = node->frees.calls.total = 0; node->sqsum = 0; node->sort = -1; return &node->entry; @@ -508,8 +510,10 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, *PL_HashTableRawLookup(tmr->methods, mhash, mkey); site->method = meth; site->offset = event.u.site.offset; - site->bytes.direct = site->bytes.total = 0; - site->allocs.direct = site->allocs.total = 0; + site->allocs.bytes.direct = site->allocs.bytes.total = 0; + site->allocs.calls.direct = site->allocs.calls.total = 0; + site->frees.bytes.direct = site->frees.bytes.total = 0; + site->frees.calls.direct = site->frees.calls.total = 0; break; } @@ -517,9 +521,9 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, case TM_EVENT_CALLOC: case TM_EVENT_REALLOC: { tmcallsite *site; - int32 size, oldsize, delta; + uint32 size, oldsize; + double delta, sqdelta, sqszdelta; tmgraphnode *meth, *comp, *lib; - double sqdelta, sqszdelta; site = tmreader_callsite(tmr, event.serial); if (!site) { @@ -528,15 +532,15 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, continue; } - size = (int32)event.u.alloc.size; - oldsize = (int32)event.u.alloc.oldsize; - delta = size - oldsize; - site->bytes.direct += delta; + size = event.u.alloc.size; + oldsize = event.u.alloc.oldsize; + delta = (double)size - (double)oldsize; + site->allocs.bytes.direct += delta; if (event.type != TM_EVENT_REALLOC) - site->allocs.direct++; + site->allocs.calls.direct++; meth = site->method; if (meth) { - meth->bytes.direct += delta; + meth->allocs.bytes.direct += delta; sqdelta = delta * delta; if (event.type == TM_EVENT_REALLOC) { sqszdelta = ((double)size * size) @@ -544,25 +548,25 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, meth->sqsum += sqszdelta; } else { meth->sqsum += sqdelta; - meth->allocs.direct++; + meth->allocs.calls.direct++; } comp = meth->up; if (comp) { - comp->bytes.direct += delta; + comp->allocs.bytes.direct += delta; if (event.type == TM_EVENT_REALLOC) { comp->sqsum += sqszdelta; } else { comp->sqsum += sqdelta; - comp->allocs.direct++; + comp->allocs.calls.direct++; } lib = comp->up; if (lib) { - lib->bytes.direct += delta; + lib->allocs.bytes.direct += delta; if (event.type == TM_EVENT_REALLOC) { lib->sqsum += sqszdelta; } else { lib->sqsum += sqdelta; - lib->allocs.direct++; + lib->allocs.calls.direct++; } } } @@ -570,8 +574,37 @@ int tmreader_eventloop(tmreader *tmr, const char *filename, break; } - case TM_EVENT_FREE: + case TM_EVENT_FREE: { + tmcallsite *site; + uint32 size; + tmgraphnode *meth, *comp, *lib; + + site = tmreader_callsite(tmr, event.serial); + if (!site) { + fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n", + tmr->program, event.type, (unsigned long) event.serial); + continue; + } + size = event.u.alloc.size; + site->frees.bytes.direct += size; + site->frees.calls.direct++; + meth = site->method; + if (meth) { + meth->frees.bytes.direct += size; + meth->frees.calls.direct++; + comp = meth->up; + if (comp) { + comp->frees.bytes.direct += size; + comp->frees.calls.direct++; + lib = comp->up; + if (lib) { + lib->frees.bytes.direct += size; + lib->frees.calls.direct++; + } + } + } break; + } case TM_EVENT_STATS: break; diff --git a/mozilla/xpcom/base/tmreader.h b/mozilla/xpcom/base/tmreader.h index f2489186079..d33bc711819 100644 --- a/mozilla/xpcom/base/tmreader.h +++ b/mozilla/xpcom/base/tmreader.h @@ -44,6 +44,8 @@ PR_BEGIN_EXTERN_C typedef struct tmreader tmreader; typedef struct tmevent tmevent; typedef struct tmcounts tmcounts; +typedef struct tmallcounts tmallcounts; +typedef struct tmgraphlink tmgraphlink; typedef struct tmgraphedge tmgraphedge; typedef struct tmgraphnode tmgraphnode; typedef struct tmcallsite tmcallsite; @@ -75,20 +77,25 @@ struct tmevent { }; struct tmcounts { - int32 direct; /* things allocated by this node's code */ - int32 total; /* direct + things from all descendents */ + uint32 direct; /* things allocated by this node's code */ + uint32 total; /* direct + things from all descendents */ +}; + +struct tmallcounts { + tmcounts bytes; + tmcounts calls; }; struct tmgraphnode { PLHashEntry entry; /* key is serial or name, value must be name */ - tmgraphedge *in; - tmgraphedge *out; + tmgraphlink *in; + tmgraphlink *out; tmgraphnode *up; /* parent in supergraph, e.g., JS for JS_*() */ tmgraphnode *down; /* subgraph kids, declining bytes.total order */ tmgraphnode *next; /* next kid in supergraph node's down list */ int low; /* 0 or lowest current tree walk level */ - tmcounts bytes; /* bytes (direct and total) allocated */ - tmcounts allocs; /* number of allocations */ + tmallcounts allocs; + tmallcounts frees; double sqsum; /* sum of squared bytes.direct */ int sort; /* sorted index in node table, -1 if no table */ }; @@ -98,21 +105,39 @@ struct tmgraphnode { #define tmlibrary_serial(lib) ((uint32) (lib)->entry.key) #define tmcomponent_name(comp) ((const char*) (comp)->entry.key) -struct tmgraphedge { - tmgraphedge *next; - tmgraphnode *node; - tmcounts bytes; +/* Half a graphedge, not including per-edge allocation stats. */ +struct tmgraphlink { + tmgraphlink *next; /* next fanning out from or into a node */ + tmgraphnode *node; /* the other node (to if OUT, from if IN) */ }; +/* + * It's safe to downcast a "from" tmgraphlink (one linked from a node's out + * pointer) to tmgraphedge. To go from an "out" (linked via tmgraphedge.from) + * or "in" (linked via tmgraphedge.to) list link to its containing edge, use + * TM_LINK_TO_EDGE(link, which). + */ +struct tmgraphedge { + tmgraphlink links[2]; + tmallcounts allocs; + tmallcounts frees; +}; + +/* Indices into tmgraphedge.links -- out must come first. */ +#define TM_EDGE_OUT_LINK 0 +#define TM_EDGE_IN_LINK 1 + +#define TM_LINK_TO_EDGE(link,which) ((tmgraphedge*) &(link)[-(which)]) + struct tmcallsite { - PLHashEntry entry; - tmcallsite *parent; - tmcallsite *siblings; - tmcallsite *kids; - tmgraphnode *method; - uint32 offset; - tmcounts bytes; - tmcounts allocs; + PLHashEntry entry; /* key is site serial number */ + tmcallsite *parent; /* calling site */ + tmcallsite *siblings; /* other sites reached from parent */ + tmcallsite *kids; /* sites reached from here */ + tmgraphnode *method; /* method node in tmr->methods graph */ + uint32 offset; /* pc offset from start of method */ + tmallcounts allocs; + tmallcounts frees; }; struct tmreader {