michael@0: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #ifndef tmreader_h___ michael@0: #define tmreader_h___ michael@0: michael@0: #include "plhash.h" michael@0: #include "nsTraceMalloc.h" michael@0: #include "plarena.h" michael@0: michael@0: #ifdef __cplusplus michael@0: extern "C" { michael@0: #endif michael@0: michael@0: typedef struct tmreader tmreader; michael@0: typedef struct tmevent tmevent; michael@0: typedef struct tmcounts tmcounts; michael@0: typedef struct tmallcounts tmallcounts; michael@0: typedef struct tmgraphlink tmgraphlink; michael@0: typedef struct tmgraphedge tmgraphedge; michael@0: typedef struct tmgraphnode tmgraphnode; michael@0: typedef struct tmcallsite tmcallsite; michael@0: typedef struct tmmethodnode tmmethodnode; michael@0: michael@0: struct tmevent { michael@0: char type; michael@0: uint32_t serial; michael@0: union { michael@0: char *libname; michael@0: char *srcname; michael@0: struct { michael@0: uint32_t library; michael@0: uint32_t filename; michael@0: uint32_t linenumber; michael@0: char *name; michael@0: } method; michael@0: struct { michael@0: uint32_t parent; michael@0: uint32_t method; michael@0: uint32_t offset; michael@0: } site; michael@0: struct { michael@0: uint32_t interval; /* in ticks */ michael@0: uint32_t ptr; michael@0: uint32_t size; michael@0: uint32_t oldserial; michael@0: uint32_t oldptr; michael@0: uint32_t oldsize; michael@0: uint32_t cost; /* in ticks */ michael@0: } alloc; michael@0: struct { michael@0: nsTMStats tmstats; michael@0: uint32_t calltree_maxkids_parent; michael@0: uint32_t calltree_maxstack_top; michael@0: } stats; michael@0: } u; michael@0: }; michael@0: michael@0: struct tmcounts { michael@0: uint32_t direct; /* things allocated by this node's code */ michael@0: uint32_t total; /* direct + things from all descendents */ michael@0: }; michael@0: michael@0: struct tmallcounts { michael@0: tmcounts bytes; michael@0: tmcounts calls; michael@0: }; michael@0: michael@0: struct tmgraphnode { michael@0: PLHashEntry entry; /* key is serial or name, value must be name */ michael@0: tmgraphlink *in; michael@0: tmgraphlink *out; michael@0: tmgraphnode *up; /* parent in supergraph, e.g., JS for JS_*() */ michael@0: tmgraphnode *down; /* subgraph kids, declining bytes.total order */ michael@0: tmgraphnode *next; /* next kid in supergraph node's down list */ michael@0: int low; /* 0 or lowest current tree walk level */ michael@0: tmallcounts allocs; michael@0: tmallcounts frees; michael@0: double sqsum; /* sum of squared bytes.direct */ michael@0: int sort; /* sorted index in node table, -1 if no table */ michael@0: }; michael@0: michael@0: struct tmmethodnode { michael@0: tmgraphnode graphnode; michael@0: char *sourcefile; michael@0: uint32_t linenumber; michael@0: }; michael@0: michael@0: #define tmgraphnode_name(node) ((char*) (node)->entry.value) michael@0: #define tmmethodnode_name(node) ((char*) (node)->graphnode.entry.value) michael@0: michael@0: #define tmlibrary_serial(lib) ((uint32_t) (lib)->entry.key) michael@0: #define tmcomponent_name(comp) ((const char*) (comp)->entry.key) michael@0: #define filename_name(hashentry) ((char*)hashentry->value) michael@0: michael@0: /* Half a graphedge, not including per-edge allocation stats. */ michael@0: struct tmgraphlink { michael@0: tmgraphlink *next; /* next fanning out from or into a node */ michael@0: tmgraphnode *node; /* the other node (to if OUT, from if IN) */ michael@0: }; michael@0: michael@0: /* michael@0: * It's safe to downcast a "from" tmgraphlink (one linked from a node's out michael@0: * pointer) to tmgraphedge. To go from an "out" (linked via tmgraphedge.from) michael@0: * or "in" (linked via tmgraphedge.to) list link to its containing edge, use michael@0: * TM_LINK_TO_EDGE(link, which). michael@0: */ michael@0: struct tmgraphedge { michael@0: tmgraphlink links[2]; michael@0: tmallcounts allocs; michael@0: tmallcounts frees; michael@0: }; michael@0: michael@0: /* Indices into tmgraphedge.links -- out must come first. */ michael@0: #define TM_EDGE_OUT_LINK 0 michael@0: #define TM_EDGE_IN_LINK 1 michael@0: michael@0: #define TM_LINK_TO_EDGE(link,which) ((tmgraphedge*) &(link)[-(which)]) michael@0: michael@0: struct tmcallsite { michael@0: PLHashEntry entry; /* key is site serial number */ michael@0: tmcallsite *parent; /* calling site */ michael@0: tmcallsite *siblings; /* other sites reached from parent */ michael@0: tmcallsite *kids; /* sites reached from here */ michael@0: tmmethodnode *method; /* method node in tmr->methods graph */ michael@0: uint32_t offset; /* pc offset from start of method */ michael@0: tmallcounts allocs; michael@0: tmallcounts frees; michael@0: void *data; /* tmreader clients can stick arbitrary michael@0: * data onto a callsite. michael@0: */ michael@0: }; michael@0: michael@0: struct tmreader { michael@0: const char *program; michael@0: void *data; michael@0: PLHashTable *libraries; michael@0: PLHashTable *filenames; michael@0: PLHashTable *components; michael@0: PLHashTable *methods; michael@0: PLHashTable *callsites; michael@0: PLArenaPool arena; michael@0: tmcallsite calltree_root; michael@0: uint32_t ticksPerSec; michael@0: }; michael@0: michael@0: typedef void (*tmeventhandler)(tmreader *tmr, tmevent *event); michael@0: michael@0: /* The tmreader constructor and destructor. */ michael@0: extern tmreader *tmreader_new(const char *program, void *data); michael@0: extern void tmreader_destroy(tmreader *tmr); michael@0: michael@0: /* michael@0: * Return -1 on permanent fatal error, 0 if filename can't be opened or is not michael@0: * a trace-malloc logfile, and 1 on success. michael@0: */ michael@0: extern int tmreader_eventloop(tmreader *tmr, const char *filename, michael@0: tmeventhandler eventhandler); michael@0: michael@0: /* Map serial number or name to graphnode or callsite. */ michael@0: extern tmgraphnode *tmreader_library(tmreader *tmr, uint32_t serial); michael@0: extern tmgraphnode *tmreader_filename(tmreader *tmr, uint32_t serial); michael@0: extern tmgraphnode *tmreader_component(tmreader *tmr, const char *name); michael@0: extern tmmethodnode *tmreader_method(tmreader *tmr, uint32_t serial); michael@0: extern tmcallsite *tmreader_callsite(tmreader *tmr, uint32_t serial); michael@0: michael@0: /* michael@0: * Connect node 'from' to node 'to' with an edge, if there isn't one already michael@0: * connecting the nodes. Add site's allocation stats to the edge only if we michael@0: * create the edge, or if we find that it exists, but that to->low is zero or michael@0: * less than from->low. michael@0: * michael@0: * If the callsite tree already totals allocation costs (tmcounts.total for michael@0: * each site includes tmcounts.direct for that site, plus tmcounts.total for michael@0: * all kid sites), then the node->low watermarks should be set from the tree michael@0: * level when walking the callsite tree, and should be set to non-zero values michael@0: * only if zero (the root is at level 0). A low watermark should be cleared michael@0: * when the tree walk unwinds past the level at which it was set non-zero. michael@0: * michael@0: * Return 0 on error (malloc failure) and 1 on success. michael@0: */ michael@0: extern int tmgraphnode_connect(tmgraphnode *from, tmgraphnode *to, michael@0: tmcallsite *site); michael@0: michael@0: #ifdef __cplusplus michael@0: } michael@0: #endif michael@0: michael@0: #endif /* tmreader_h___ */