mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Finish factoring tmreader from bloatblame; misc. clenaups (not part of build).
This commit is contained in:
parent
f0d4936e2e
commit
02c5512498
@ -63,193 +63,6 @@ static int unified_output = 0;
|
||||
static char *function_dump = NULL;
|
||||
static int32 min_subtotal = 0;
|
||||
|
||||
static int accum_byte(FILE *fp, uint32 *uip)
|
||||
{
|
||||
int c = getc(fp);
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
*uip = (*uip << 8) | c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_uint32(FILE *fp, uint32 *uip)
|
||||
{
|
||||
int c;
|
||||
uint32 ui;
|
||||
|
||||
c = getc(fp);
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
ui = 0;
|
||||
if (c & 0x80) {
|
||||
c &= 0x7f;
|
||||
if (c & 0x40) {
|
||||
c &= 0x3f;
|
||||
if (c & 0x20) {
|
||||
c &= 0x1f;
|
||||
if (c & 0x10) {
|
||||
if (!accum_byte(fp, &ui))
|
||||
return 0;
|
||||
} else {
|
||||
ui = (uint32) c;
|
||||
}
|
||||
if (!accum_byte(fp, &ui))
|
||||
return 0;
|
||||
} else {
|
||||
ui = (uint32) c;
|
||||
}
|
||||
if (!accum_byte(fp, &ui))
|
||||
return 0;
|
||||
} else {
|
||||
ui = (uint32) c;
|
||||
}
|
||||
if (!accum_byte(fp, &ui))
|
||||
return 0;
|
||||
} else {
|
||||
ui = (uint32) c;
|
||||
}
|
||||
*uip = ui;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *get_string(FILE *fp)
|
||||
{
|
||||
char *cp;
|
||||
int c;
|
||||
static char buf[256];
|
||||
static char *bp = buf, *ep = buf + sizeof buf;
|
||||
static size_t bsize = sizeof buf;
|
||||
|
||||
cp = bp;
|
||||
do {
|
||||
c = getc(fp);
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
if (cp == ep) {
|
||||
if (bp == buf) {
|
||||
bp = malloc(2 * bsize);
|
||||
if (bp)
|
||||
memcpy(bp, buf, bsize);
|
||||
} else {
|
||||
bp = realloc(bp, 2 * bsize);
|
||||
}
|
||||
if (!bp)
|
||||
return 0;
|
||||
cp = bp + bsize;
|
||||
bsize *= 2;
|
||||
ep = bp + bsize;
|
||||
}
|
||||
*cp++ = c;
|
||||
} while (c != '\0');
|
||||
return strdup(bp);
|
||||
}
|
||||
|
||||
typedef struct logevent {
|
||||
char type;
|
||||
uint32 serial;
|
||||
union {
|
||||
char *libname;
|
||||
struct {
|
||||
uint32 library;
|
||||
char *name;
|
||||
} method;
|
||||
struct {
|
||||
uint32 parent;
|
||||
uint32 method;
|
||||
uint32 offset;
|
||||
} site;
|
||||
struct {
|
||||
uint32 oldsize;
|
||||
uint32 size;
|
||||
} alloc;
|
||||
struct {
|
||||
nsTMStats tmstats;
|
||||
uint32 calltree_maxkids_parent;
|
||||
uint32 calltree_maxstack_top;
|
||||
} stats;
|
||||
} u;
|
||||
} logevent;
|
||||
|
||||
static int get_logevent(FILE *fp, logevent *event)
|
||||
{
|
||||
int c;
|
||||
char *s;
|
||||
|
||||
c = getc(fp);
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
event->type = (char) c;
|
||||
if (!get_uint32(fp, &event->serial))
|
||||
return 0;
|
||||
switch (c) {
|
||||
case 'L':
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
event->u.libname = s;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
if (!get_uint32(fp, &event->u.method.library))
|
||||
return 0;
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
event->u.method.name = s;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if (!get_uint32(fp, &event->u.site.parent))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.site.method))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.site.offset))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
case 'C':
|
||||
case 'F':
|
||||
event->u.alloc.oldsize = 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if (!get_uint32(fp, &event->u.alloc.oldsize))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxstack)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxdepth)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_parents)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxkids)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidhits)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidmisses)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidsteps)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.callsite_recurrences)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.backtrace_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.backtrace_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.btmalloc_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.dladdr_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.malloc_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.malloc_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calloc_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calloc_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.realloc_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.realloc_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.free_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.null_free_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.calltree_maxkids_parent)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.calltree_maxstack_top)) return 0;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void connect_nodes(tmgraphnode *from, tmgraphnode *to, tmcallsite *site)
|
||||
{
|
||||
tmgraphedge *edge;
|
||||
@ -685,7 +498,7 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname,
|
||||
static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
{
|
||||
switch (event->type) {
|
||||
case 'Z':
|
||||
case TM_EVENT_STATS:
|
||||
if (js_mode)
|
||||
break;
|
||||
fprintf(stdout,
|
||||
@ -735,8 +548,7 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
|
||||
if (event->u.stats.calltree_maxkids_parent) {
|
||||
tmcallsite *site =
|
||||
tmreader_get_callsite(tmr,
|
||||
event->u.stats.calltree_maxkids_parent);
|
||||
tmreader_callsite(tmr, event->u.stats.calltree_maxkids_parent);
|
||||
if (site && site->method) {
|
||||
fprintf(stdout, "<p>callsite with the most kids: %s</p>",
|
||||
tmgraphnode_name(site->method));
|
||||
@ -745,8 +557,7 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
|
||||
if (event->u.stats.calltree_maxstack_top) {
|
||||
tmcallsite *site =
|
||||
tmreader_get_callsite(tmr,
|
||||
event->u.stats.calltree_maxstack_top);
|
||||
tmreader_callsite(tmr, event->u.stats.calltree_maxstack_top);
|
||||
fputs("<p>deepest callsite tree path:\n"
|
||||
"<table border=1>\n"
|
||||
"<tr><th>Method</th><th>Offset</th></tr>\n",
|
||||
@ -766,7 +577,7 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c, i;
|
||||
int c, i, j, rv;
|
||||
tmreader *tmr;
|
||||
FILE *fp;
|
||||
|
||||
@ -823,18 +634,25 @@ int main(int argc, char **argv)
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc == 0) {
|
||||
tmreader_loop(tmr, "-", my_tmevent_handler);
|
||||
if (tmreader_eventloop(tmr, "-", my_tmevent_handler) <= 0)
|
||||
exit(1);
|
||||
} else {
|
||||
for (i = 0; i < argc; i++) {
|
||||
for (i = j = 0; i < argc; i++) {
|
||||
fp = fopen(argv[i], "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "%s: can't open %s: %s\n",
|
||||
program, argv[i], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
tmreader_loop(tmr, argv[i], my_tmevent_handler);
|
||||
rv = tmreader_eventloop(tmr, argv[i], my_tmevent_handler);
|
||||
if (rv < 0)
|
||||
exit(1);
|
||||
if (rv > 0)
|
||||
j++;
|
||||
fclose(fp);
|
||||
}
|
||||
if (j == 0)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
compute_callsite_totals(&tmr->calltree_root);
|
||||
|
@ -36,9 +36,11 @@
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - Fix rusty's SMP realloc oldsize corruption bug
|
||||
* - fix my_dladdr so it builds its own symbol tables from bfd
|
||||
* - extend logfile so 'F' record tells free stack
|
||||
* - diagnose rusty's SMP realloc oldsize corruption bug
|
||||
* - #ifdef __linux__/x86 and port to other platforms
|
||||
* - unify calltree with gc/boehm somehow (common utility libs)
|
||||
* - unify calltree with gc/boehm somehow (common utility lib?)
|
||||
* - provide NS_TraceMallocTimestamp() or do it internally
|
||||
*/
|
||||
#include <errno.h>
|
||||
@ -54,9 +56,12 @@
|
||||
#include "prprf.h"
|
||||
#include "nsTraceMalloc.h"
|
||||
|
||||
/* From libiberty, why isn't this in <libiberty.h> ? */
|
||||
/* From libiberty, why aren't these in <libiberty.h> ? */
|
||||
extern char *cplus_demangle(const char *, int);
|
||||
|
||||
#define DMGL_PARAMS 0x1
|
||||
#define DMGL_ANSI 0x2
|
||||
|
||||
extern __ptr_t __libc_malloc(size_t);
|
||||
extern __ptr_t __libc_calloc(size_t, size_t);
|
||||
extern __ptr_t __libc_realloc(__ptr_t, size_t);
|
||||
@ -380,6 +385,8 @@ static uint32 library_serial_generator = 0;
|
||||
static uint32 method_serial_generator = 0;
|
||||
static uint32 callsite_serial_generator = 0;
|
||||
static uint32 tmstats_serial_generator = 0;
|
||||
|
||||
/* Root of the tree of callsites, the sum of all (cycle-compressed) stacks. */
|
||||
static callsite calltree_root = {0, 0, LFD_SET_STATIC_INITIALIZER, NULL, NULL, NULL, NULL};
|
||||
|
||||
/* Basic instrumentation. */
|
||||
@ -396,7 +403,7 @@ static callsite *last_callsite_recurrence;
|
||||
|
||||
static void log_tmstats(logfile *fp)
|
||||
{
|
||||
log_event1(fp, 'Z', ++tmstats_serial_generator);
|
||||
log_event1(fp, TM_EVENT_STATS, ++tmstats_serial_generator);
|
||||
log_uint32(fp, tmstats.calltree_maxstack);
|
||||
log_uint32(fp, tmstats.calltree_maxdepth);
|
||||
log_uint32(fp, tmstats.calltree_parents);
|
||||
@ -595,7 +602,7 @@ static callsite *calltree(uint32 *bp)
|
||||
slash = strrchr(library, '/');
|
||||
if (slash)
|
||||
library = slash + 1;
|
||||
log_event1(fp, 'L', library_serial);
|
||||
log_event1(fp, TM_EVENT_LIBRARY, library_serial);
|
||||
log_string(fp, library);
|
||||
LFD_SET(fp->lfd, &le->lfdset);
|
||||
}
|
||||
@ -606,11 +613,8 @@ static callsite *calltree(uint32 *bp)
|
||||
offset = (char*)pc - (char*)info.dli_saddr;
|
||||
method = NULL;
|
||||
if (symbol && (len = strlen(symbol)) != 0) {
|
||||
/*
|
||||
* Attempt to demangle symbol in case it's a C++ mangled name.
|
||||
* The magic 3 passed here specifies DMGL_PARAMS | DMGL_ANSI.
|
||||
*/
|
||||
method = cplus_demangle(symbol, 3);
|
||||
/* Attempt to demangle symbol in case it's a C++ mangled name. */
|
||||
method = cplus_demangle(symbol, DMGL_PARAMS | DMGL_ANSI);
|
||||
}
|
||||
if (!method) {
|
||||
method = symbol
|
||||
@ -660,7 +664,7 @@ static callsite *calltree(uint32 *bp)
|
||||
le = (lfdset_entry *) he;
|
||||
}
|
||||
if (le) {
|
||||
log_event2(fp, 'N', method_serial, library_serial);
|
||||
log_event2(fp, TM_EVENT_METHOD, method_serial, library_serial);
|
||||
log_string(fp, method);
|
||||
LFD_SET(fp->lfd, &le->lfdset);
|
||||
}
|
||||
@ -696,8 +700,8 @@ static callsite *calltree(uint32 *bp)
|
||||
}
|
||||
|
||||
/* Log the site with its parent, method, and offset. */
|
||||
log_event4(fp, 'S', site->serial, parent->serial, method_serial,
|
||||
offset);
|
||||
log_event4(fp, TM_EVENT_CALLSITE, site->serial, parent->serial,
|
||||
method_serial, offset);
|
||||
LFD_SET(fp->lfd, &site->lfdset);
|
||||
|
||||
upward:
|
||||
@ -837,7 +841,7 @@ __ptr_t malloc(size_t size)
|
||||
PR_EnterMonitor(tmmon);
|
||||
site = backtrace(1);
|
||||
if (site)
|
||||
log_event2(logfp, 'M', site->serial, size);
|
||||
log_event2(logfp, TM_EVENT_MALLOC, site->serial, size);
|
||||
if (get_allocations()) {
|
||||
suppress_tracing++;
|
||||
he = PL_HashTableAdd(allocations, ptr, site);
|
||||
@ -870,7 +874,7 @@ __ptr_t calloc(size_t count, size_t size)
|
||||
site = backtrace(1);
|
||||
size *= count;
|
||||
if (site)
|
||||
log_event2(logfp, 'C', site->serial, size);
|
||||
log_event2(logfp, TM_EVENT_CALLOC, site->serial, size);
|
||||
if (get_allocations()) {
|
||||
suppress_tracing++;
|
||||
he = PL_HashTableAdd(allocations, ptr, site);
|
||||
@ -932,7 +936,7 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
|
||||
#endif
|
||||
site = backtrace(1);
|
||||
if (site)
|
||||
log_event3(logfp, 'R', site->serial, oldsize, size);
|
||||
log_event3(logfp, TM_EVENT_REALLOC, site->serial, oldsize, size);
|
||||
if (ptr && allocations) {
|
||||
suppress_tracing++;
|
||||
he = PL_HashTableAdd(allocations, ptr, site);
|
||||
@ -967,7 +971,7 @@ void free(__ptr_t ptr)
|
||||
site = (callsite*) he->value;
|
||||
if (site) {
|
||||
alloc = (allocation*) he;
|
||||
log_event2(logfp, 'F', site->serial, alloc->size);
|
||||
log_event2(logfp, TM_EVENT_FREE, site->serial, alloc->size);
|
||||
}
|
||||
PL_HashTableRawRemove(allocations, hep, he);
|
||||
}
|
||||
|
@ -42,7 +42,8 @@ PR_BEGIN_EXTERN_C
|
||||
/**
|
||||
* Magic "number" at start of a trace-malloc log file. Inspired by the PNG
|
||||
* magic string, which inspired XPCOM's typelib (.xpt) file magic. See the
|
||||
* NS_TraceMalloc comment for magic number differences in log file structure.
|
||||
* NS_TraceMallocStartup comment (below) for magic number differences in log
|
||||
* file structure.
|
||||
*/
|
||||
#define NS_TRACE_MALLOC_MAGIC "XPCOM\nTMLog02\r\n\032"
|
||||
#define NS_TRACE_MALLOC_MAGIC_SIZE 16
|
||||
@ -76,7 +77,7 @@ typedef struct nsTMStats {
|
||||
#define NS_TMSTATS_STATIC_INITIALIZER {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
|
||||
|
||||
/**
|
||||
* Call NS_TraceMalloc with a valid log file descriptor to enable logging
|
||||
* Call NS_TraceMallocStartup with a valid file descriptor to enable logging
|
||||
* of compressed malloc traces, including callsite chains. Integers may be
|
||||
* unsigned serial numbers, sizes, or offsets, and require at most 32 bits.
|
||||
* They're encoded as follows:
|
||||
@ -103,6 +104,15 @@ typedef struct nsTMStats {
|
||||
*
|
||||
* See xpcom/base/bloatblame.c for an example log-file reader.
|
||||
*/
|
||||
#define TM_EVENT_LIBRARY 'L'
|
||||
#define TM_EVENT_METHOD 'N'
|
||||
#define TM_EVENT_CALLSITE 'S'
|
||||
#define TM_EVENT_MALLOC 'M'
|
||||
#define TM_EVENT_CALLOC 'C'
|
||||
#define TM_EVENT_REALLOC 'R'
|
||||
#define TM_EVENT_FREE 'F'
|
||||
#define TM_EVENT_STATS 'Z'
|
||||
|
||||
PR_EXTERN(void) NS_TraceMallocStartup(int logfd);
|
||||
|
||||
/**
|
||||
|
@ -135,14 +135,14 @@ static int get_tmevent(FILE *fp, tmevent *event)
|
||||
if (!get_uint32(fp, &event->serial))
|
||||
return 0;
|
||||
switch (c) {
|
||||
case 'L':
|
||||
case TM_EVENT_LIBRARY:
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
event->u.libname = s;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
case TM_EVENT_METHOD:
|
||||
if (!get_uint32(fp, &event->u.method.library))
|
||||
return 0;
|
||||
s = get_string(fp);
|
||||
@ -151,7 +151,7 @@ static int get_tmevent(FILE *fp, tmevent *event)
|
||||
event->u.method.name = s;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
case TM_EVENT_CALLSITE:
|
||||
if (!get_uint32(fp, &event->u.site.parent))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.site.method))
|
||||
@ -160,22 +160,22 @@ static int get_tmevent(FILE *fp, tmevent *event)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
case 'C':
|
||||
case 'F':
|
||||
case TM_EVENT_MALLOC:
|
||||
case TM_EVENT_CALLOC:
|
||||
case TM_EVENT_FREE:
|
||||
event->u.alloc.oldsize = 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
case TM_EVENT_REALLOC:
|
||||
if (!get_uint32(fp, &event->u.alloc.oldsize))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
case TM_EVENT_STATS:
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxstack))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxdepth))
|
||||
@ -341,8 +341,8 @@ void tmreader_destroy(tmreader *tmr)
|
||||
free(tmr);
|
||||
}
|
||||
|
||||
int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler)
|
||||
int tmreader_eventloop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[NS_TRACE_MALLOC_MAGIC_SIZE];
|
||||
@ -369,7 +369,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
|
||||
while (get_tmevent(fp, &event)) {
|
||||
switch (event.type) {
|
||||
case 'L': {
|
||||
case TM_EVENT_LIBRARY: {
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry **hep, *he;
|
||||
@ -385,12 +385,12 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
event.u.libname);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'N': {
|
||||
case TM_EVENT_METHOD: {
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry **hep, *he;
|
||||
@ -408,7 +408,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
he = PL_HashTableRawAdd(tmr->methods, hep, hash, key, name);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
meth = (tmgraphnode*) he;
|
||||
|
||||
@ -442,7 +442,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
}
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
comp = (tmgraphnode*) he;
|
||||
|
||||
@ -464,7 +464,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
break;
|
||||
}
|
||||
|
||||
case 'S': {
|
||||
case TM_EVENT_CALLSITE: {
|
||||
const void *key, *mkey;
|
||||
PLHashNumber hash, mhash;
|
||||
PLHashEntry **hep, *he;
|
||||
@ -481,7 +481,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
if (event.u.site.parent == 0) {
|
||||
parent = &tmr->calltree_root;
|
||||
} else {
|
||||
parent = tmreader_get_callsite(tmr, event.u.site.parent);
|
||||
parent = tmreader_callsite(tmr, event.u.site.parent);
|
||||
if (!parent) {
|
||||
fprintf(stderr, "%s: no parent for %lu (%lu)!\n",
|
||||
tmr->program, (unsigned long) event.serial,
|
||||
@ -493,7 +493,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
he = PL_HashTableRawAdd(tmr->callsites, hep, hash, key, NULL);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
site = (tmcallsite*) he;
|
||||
@ -513,15 +513,15 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
break;
|
||||
}
|
||||
|
||||
case 'M':
|
||||
case 'C':
|
||||
case 'R': {
|
||||
case TM_EVENT_MALLOC:
|
||||
case TM_EVENT_CALLOC:
|
||||
case TM_EVENT_REALLOC: {
|
||||
tmcallsite *site;
|
||||
int32 size, oldsize, delta;
|
||||
tmgraphnode *meth, *comp, *lib;
|
||||
double sqdelta, sqszdelta;
|
||||
|
||||
site = tmreader_get_callsite(tmr, event.serial);
|
||||
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);
|
||||
@ -532,13 +532,13 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
oldsize = (int32)event.u.alloc.oldsize;
|
||||
delta = size - oldsize;
|
||||
site->bytes.direct += delta;
|
||||
if (event.type != 'R')
|
||||
if (event.type != TM_EVENT_REALLOC)
|
||||
site->allocs.direct++;
|
||||
meth = site->method;
|
||||
if (meth) {
|
||||
meth->bytes.direct += delta;
|
||||
sqdelta = delta * delta;
|
||||
if (event.type == 'R') {
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
sqszdelta = ((double)size * size)
|
||||
- ((double)oldsize * oldsize);
|
||||
meth->sqsum += sqszdelta;
|
||||
@ -549,7 +549,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
comp = meth->up;
|
||||
if (comp) {
|
||||
comp->bytes.direct += delta;
|
||||
if (event.type == 'R') {
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
comp->sqsum += sqszdelta;
|
||||
} else {
|
||||
comp->sqsum += sqdelta;
|
||||
@ -558,7 +558,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
lib = comp->up;
|
||||
if (lib) {
|
||||
lib->bytes.direct += delta;
|
||||
if (event.type == 'R') {
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
lib->sqsum += sqszdelta;
|
||||
} else {
|
||||
lib->sqsum += sqdelta;
|
||||
@ -570,10 +570,10 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
break;
|
||||
}
|
||||
|
||||
case 'F':
|
||||
case TM_EVENT_FREE:
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
case TM_EVENT_STATS:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -583,11 +583,38 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmcallsite *tmreader_get_callsite(tmreader *tmr, uint32 serial)
|
||||
tmgraphnode *tmreader_library(tmreader *tmr, uint32 serial)
|
||||
{
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->libraries, hash, key);
|
||||
}
|
||||
|
||||
tmgraphnode *tmreader_component(tmreader *tmr, const char *name)
|
||||
{
|
||||
PLHashNumber hash;
|
||||
|
||||
hash = PL_HashString(name);
|
||||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->components, hash, name);
|
||||
}
|
||||
|
||||
tmgraphnode *tmreader_method(tmreader *tmr, uint32 serial)
|
||||
{
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->methods, hash, key);
|
||||
}
|
||||
|
||||
tmcallsite *tmreader_callsite(tmreader *tmr, uint32 serial)
|
||||
{
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
tmcallsite *site;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
|
@ -43,6 +43,7 @@ PR_BEGIN_EXTERN_C
|
||||
|
||||
typedef struct tmreader tmreader;
|
||||
typedef struct tmevent tmevent;
|
||||
typedef struct tmcounts tmcounts;
|
||||
typedef struct tmgraphedge tmgraphedge;
|
||||
typedef struct tmgraphnode tmgraphnode;
|
||||
typedef struct tmcallsite tmcallsite;
|
||||
@ -73,10 +74,10 @@ struct tmevent {
|
||||
} u;
|
||||
};
|
||||
|
||||
typedef struct tmcounts {
|
||||
struct tmcounts {
|
||||
int32 direct; /* things allocated by this node's code */
|
||||
int32 total; /* direct + things from all descendents */
|
||||
} tmcounts;
|
||||
};
|
||||
|
||||
struct tmgraphnode {
|
||||
PLHashEntry entry; /* key is serial or name, value must be name */
|
||||
@ -126,11 +127,22 @@ struct tmreader {
|
||||
|
||||
typedef void (*tmeventhandler)(tmreader *tmr, tmevent *event);
|
||||
|
||||
/* The tmreader constructor and destructor. */
|
||||
extern tmreader *tmreader_new(const char *program, void *data);
|
||||
extern void tmreader_destroy(tmreader *tmr);
|
||||
extern int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler);
|
||||
extern tmcallsite *tmreader_get_callsite(tmreader *tmr, uint32 serial);
|
||||
|
||||
/*
|
||||
* Return -1 on permanent fatal error, 0 if filename can't be opened or is not
|
||||
* a trace-malloc logfile, and 1 on success.
|
||||
*/
|
||||
extern int tmreader_eventloop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler);
|
||||
|
||||
/* Map serial number or name to graphnode or callsite. */
|
||||
extern tmgraphnode *tmreader_library(tmreader *tmr, uint32 serial);
|
||||
extern tmgraphnode *tmreader_component(tmreader *tmr, const char *name);
|
||||
extern tmgraphnode *tmreader_method(tmreader *tmr, uint32 serial);
|
||||
extern tmcallsite *tmreader_callsite(tmreader *tmr, uint32 serial);
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
|
@ -63,193 +63,6 @@ static int unified_output = 0;
|
||||
static char *function_dump = NULL;
|
||||
static int32 min_subtotal = 0;
|
||||
|
||||
static int accum_byte(FILE *fp, uint32 *uip)
|
||||
{
|
||||
int c = getc(fp);
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
*uip = (*uip << 8) | c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_uint32(FILE *fp, uint32 *uip)
|
||||
{
|
||||
int c;
|
||||
uint32 ui;
|
||||
|
||||
c = getc(fp);
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
ui = 0;
|
||||
if (c & 0x80) {
|
||||
c &= 0x7f;
|
||||
if (c & 0x40) {
|
||||
c &= 0x3f;
|
||||
if (c & 0x20) {
|
||||
c &= 0x1f;
|
||||
if (c & 0x10) {
|
||||
if (!accum_byte(fp, &ui))
|
||||
return 0;
|
||||
} else {
|
||||
ui = (uint32) c;
|
||||
}
|
||||
if (!accum_byte(fp, &ui))
|
||||
return 0;
|
||||
} else {
|
||||
ui = (uint32) c;
|
||||
}
|
||||
if (!accum_byte(fp, &ui))
|
||||
return 0;
|
||||
} else {
|
||||
ui = (uint32) c;
|
||||
}
|
||||
if (!accum_byte(fp, &ui))
|
||||
return 0;
|
||||
} else {
|
||||
ui = (uint32) c;
|
||||
}
|
||||
*uip = ui;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *get_string(FILE *fp)
|
||||
{
|
||||
char *cp;
|
||||
int c;
|
||||
static char buf[256];
|
||||
static char *bp = buf, *ep = buf + sizeof buf;
|
||||
static size_t bsize = sizeof buf;
|
||||
|
||||
cp = bp;
|
||||
do {
|
||||
c = getc(fp);
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
if (cp == ep) {
|
||||
if (bp == buf) {
|
||||
bp = malloc(2 * bsize);
|
||||
if (bp)
|
||||
memcpy(bp, buf, bsize);
|
||||
} else {
|
||||
bp = realloc(bp, 2 * bsize);
|
||||
}
|
||||
if (!bp)
|
||||
return 0;
|
||||
cp = bp + bsize;
|
||||
bsize *= 2;
|
||||
ep = bp + bsize;
|
||||
}
|
||||
*cp++ = c;
|
||||
} while (c != '\0');
|
||||
return strdup(bp);
|
||||
}
|
||||
|
||||
typedef struct logevent {
|
||||
char type;
|
||||
uint32 serial;
|
||||
union {
|
||||
char *libname;
|
||||
struct {
|
||||
uint32 library;
|
||||
char *name;
|
||||
} method;
|
||||
struct {
|
||||
uint32 parent;
|
||||
uint32 method;
|
||||
uint32 offset;
|
||||
} site;
|
||||
struct {
|
||||
uint32 oldsize;
|
||||
uint32 size;
|
||||
} alloc;
|
||||
struct {
|
||||
nsTMStats tmstats;
|
||||
uint32 calltree_maxkids_parent;
|
||||
uint32 calltree_maxstack_top;
|
||||
} stats;
|
||||
} u;
|
||||
} logevent;
|
||||
|
||||
static int get_logevent(FILE *fp, logevent *event)
|
||||
{
|
||||
int c;
|
||||
char *s;
|
||||
|
||||
c = getc(fp);
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
event->type = (char) c;
|
||||
if (!get_uint32(fp, &event->serial))
|
||||
return 0;
|
||||
switch (c) {
|
||||
case 'L':
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
event->u.libname = s;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
if (!get_uint32(fp, &event->u.method.library))
|
||||
return 0;
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
event->u.method.name = s;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if (!get_uint32(fp, &event->u.site.parent))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.site.method))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.site.offset))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
case 'C':
|
||||
case 'F':
|
||||
event->u.alloc.oldsize = 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if (!get_uint32(fp, &event->u.alloc.oldsize))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxstack)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxdepth)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_parents)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxkids)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidhits)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidmisses)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_kidsteps)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.callsite_recurrences)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.backtrace_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.backtrace_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.btmalloc_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.dladdr_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.malloc_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.malloc_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calloc_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calloc_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.realloc_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.realloc_failures)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.free_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.null_free_calls)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.calltree_maxkids_parent)) return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.calltree_maxstack_top)) return 0;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void connect_nodes(tmgraphnode *from, tmgraphnode *to, tmcallsite *site)
|
||||
{
|
||||
tmgraphedge *edge;
|
||||
@ -685,7 +498,7 @@ static void dump_graph(tmreader *tmr, PLHashTable *hashtbl, const char *varname,
|
||||
static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
{
|
||||
switch (event->type) {
|
||||
case 'Z':
|
||||
case TM_EVENT_STATS:
|
||||
if (js_mode)
|
||||
break;
|
||||
fprintf(stdout,
|
||||
@ -735,8 +548,7 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
|
||||
if (event->u.stats.calltree_maxkids_parent) {
|
||||
tmcallsite *site =
|
||||
tmreader_get_callsite(tmr,
|
||||
event->u.stats.calltree_maxkids_parent);
|
||||
tmreader_callsite(tmr, event->u.stats.calltree_maxkids_parent);
|
||||
if (site && site->method) {
|
||||
fprintf(stdout, "<p>callsite with the most kids: %s</p>",
|
||||
tmgraphnode_name(site->method));
|
||||
@ -745,8 +557,7 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
|
||||
if (event->u.stats.calltree_maxstack_top) {
|
||||
tmcallsite *site =
|
||||
tmreader_get_callsite(tmr,
|
||||
event->u.stats.calltree_maxstack_top);
|
||||
tmreader_callsite(tmr, event->u.stats.calltree_maxstack_top);
|
||||
fputs("<p>deepest callsite tree path:\n"
|
||||
"<table border=1>\n"
|
||||
"<tr><th>Method</th><th>Offset</th></tr>\n",
|
||||
@ -766,7 +577,7 @@ static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c, i;
|
||||
int c, i, j, rv;
|
||||
tmreader *tmr;
|
||||
FILE *fp;
|
||||
|
||||
@ -823,18 +634,25 @@ int main(int argc, char **argv)
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc == 0) {
|
||||
tmreader_loop(tmr, "-", my_tmevent_handler);
|
||||
if (tmreader_eventloop(tmr, "-", my_tmevent_handler) <= 0)
|
||||
exit(1);
|
||||
} else {
|
||||
for (i = 0; i < argc; i++) {
|
||||
for (i = j = 0; i < argc; i++) {
|
||||
fp = fopen(argv[i], "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "%s: can't open %s: %s\n",
|
||||
program, argv[i], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
tmreader_loop(tmr, argv[i], my_tmevent_handler);
|
||||
rv = tmreader_eventloop(tmr, argv[i], my_tmevent_handler);
|
||||
if (rv < 0)
|
||||
exit(1);
|
||||
if (rv > 0)
|
||||
j++;
|
||||
fclose(fp);
|
||||
}
|
||||
if (j == 0)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
compute_callsite_totals(&tmr->calltree_root);
|
||||
|
@ -36,9 +36,11 @@
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* - Fix rusty's SMP realloc oldsize corruption bug
|
||||
* - fix my_dladdr so it builds its own symbol tables from bfd
|
||||
* - extend logfile so 'F' record tells free stack
|
||||
* - diagnose rusty's SMP realloc oldsize corruption bug
|
||||
* - #ifdef __linux__/x86 and port to other platforms
|
||||
* - unify calltree with gc/boehm somehow (common utility libs)
|
||||
* - unify calltree with gc/boehm somehow (common utility lib?)
|
||||
* - provide NS_TraceMallocTimestamp() or do it internally
|
||||
*/
|
||||
#include <errno.h>
|
||||
@ -54,9 +56,12 @@
|
||||
#include "prprf.h"
|
||||
#include "nsTraceMalloc.h"
|
||||
|
||||
/* From libiberty, why isn't this in <libiberty.h> ? */
|
||||
/* From libiberty, why aren't these in <libiberty.h> ? */
|
||||
extern char *cplus_demangle(const char *, int);
|
||||
|
||||
#define DMGL_PARAMS 0x1
|
||||
#define DMGL_ANSI 0x2
|
||||
|
||||
extern __ptr_t __libc_malloc(size_t);
|
||||
extern __ptr_t __libc_calloc(size_t, size_t);
|
||||
extern __ptr_t __libc_realloc(__ptr_t, size_t);
|
||||
@ -380,6 +385,8 @@ static uint32 library_serial_generator = 0;
|
||||
static uint32 method_serial_generator = 0;
|
||||
static uint32 callsite_serial_generator = 0;
|
||||
static uint32 tmstats_serial_generator = 0;
|
||||
|
||||
/* Root of the tree of callsites, the sum of all (cycle-compressed) stacks. */
|
||||
static callsite calltree_root = {0, 0, LFD_SET_STATIC_INITIALIZER, NULL, NULL, NULL, NULL};
|
||||
|
||||
/* Basic instrumentation. */
|
||||
@ -396,7 +403,7 @@ static callsite *last_callsite_recurrence;
|
||||
|
||||
static void log_tmstats(logfile *fp)
|
||||
{
|
||||
log_event1(fp, 'Z', ++tmstats_serial_generator);
|
||||
log_event1(fp, TM_EVENT_STATS, ++tmstats_serial_generator);
|
||||
log_uint32(fp, tmstats.calltree_maxstack);
|
||||
log_uint32(fp, tmstats.calltree_maxdepth);
|
||||
log_uint32(fp, tmstats.calltree_parents);
|
||||
@ -595,7 +602,7 @@ static callsite *calltree(uint32 *bp)
|
||||
slash = strrchr(library, '/');
|
||||
if (slash)
|
||||
library = slash + 1;
|
||||
log_event1(fp, 'L', library_serial);
|
||||
log_event1(fp, TM_EVENT_LIBRARY, library_serial);
|
||||
log_string(fp, library);
|
||||
LFD_SET(fp->lfd, &le->lfdset);
|
||||
}
|
||||
@ -606,11 +613,8 @@ static callsite *calltree(uint32 *bp)
|
||||
offset = (char*)pc - (char*)info.dli_saddr;
|
||||
method = NULL;
|
||||
if (symbol && (len = strlen(symbol)) != 0) {
|
||||
/*
|
||||
* Attempt to demangle symbol in case it's a C++ mangled name.
|
||||
* The magic 3 passed here specifies DMGL_PARAMS | DMGL_ANSI.
|
||||
*/
|
||||
method = cplus_demangle(symbol, 3);
|
||||
/* Attempt to demangle symbol in case it's a C++ mangled name. */
|
||||
method = cplus_demangle(symbol, DMGL_PARAMS | DMGL_ANSI);
|
||||
}
|
||||
if (!method) {
|
||||
method = symbol
|
||||
@ -660,7 +664,7 @@ static callsite *calltree(uint32 *bp)
|
||||
le = (lfdset_entry *) he;
|
||||
}
|
||||
if (le) {
|
||||
log_event2(fp, 'N', method_serial, library_serial);
|
||||
log_event2(fp, TM_EVENT_METHOD, method_serial, library_serial);
|
||||
log_string(fp, method);
|
||||
LFD_SET(fp->lfd, &le->lfdset);
|
||||
}
|
||||
@ -696,8 +700,8 @@ static callsite *calltree(uint32 *bp)
|
||||
}
|
||||
|
||||
/* Log the site with its parent, method, and offset. */
|
||||
log_event4(fp, 'S', site->serial, parent->serial, method_serial,
|
||||
offset);
|
||||
log_event4(fp, TM_EVENT_CALLSITE, site->serial, parent->serial,
|
||||
method_serial, offset);
|
||||
LFD_SET(fp->lfd, &site->lfdset);
|
||||
|
||||
upward:
|
||||
@ -837,7 +841,7 @@ __ptr_t malloc(size_t size)
|
||||
PR_EnterMonitor(tmmon);
|
||||
site = backtrace(1);
|
||||
if (site)
|
||||
log_event2(logfp, 'M', site->serial, size);
|
||||
log_event2(logfp, TM_EVENT_MALLOC, site->serial, size);
|
||||
if (get_allocations()) {
|
||||
suppress_tracing++;
|
||||
he = PL_HashTableAdd(allocations, ptr, site);
|
||||
@ -870,7 +874,7 @@ __ptr_t calloc(size_t count, size_t size)
|
||||
site = backtrace(1);
|
||||
size *= count;
|
||||
if (site)
|
||||
log_event2(logfp, 'C', site->serial, size);
|
||||
log_event2(logfp, TM_EVENT_CALLOC, site->serial, size);
|
||||
if (get_allocations()) {
|
||||
suppress_tracing++;
|
||||
he = PL_HashTableAdd(allocations, ptr, site);
|
||||
@ -932,7 +936,7 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
|
||||
#endif
|
||||
site = backtrace(1);
|
||||
if (site)
|
||||
log_event3(logfp, 'R', site->serial, oldsize, size);
|
||||
log_event3(logfp, TM_EVENT_REALLOC, site->serial, oldsize, size);
|
||||
if (ptr && allocations) {
|
||||
suppress_tracing++;
|
||||
he = PL_HashTableAdd(allocations, ptr, site);
|
||||
@ -967,7 +971,7 @@ void free(__ptr_t ptr)
|
||||
site = (callsite*) he->value;
|
||||
if (site) {
|
||||
alloc = (allocation*) he;
|
||||
log_event2(logfp, 'F', site->serial, alloc->size);
|
||||
log_event2(logfp, TM_EVENT_FREE, site->serial, alloc->size);
|
||||
}
|
||||
PL_HashTableRawRemove(allocations, hep, he);
|
||||
}
|
||||
|
@ -42,7 +42,8 @@ PR_BEGIN_EXTERN_C
|
||||
/**
|
||||
* Magic "number" at start of a trace-malloc log file. Inspired by the PNG
|
||||
* magic string, which inspired XPCOM's typelib (.xpt) file magic. See the
|
||||
* NS_TraceMalloc comment for magic number differences in log file structure.
|
||||
* NS_TraceMallocStartup comment (below) for magic number differences in log
|
||||
* file structure.
|
||||
*/
|
||||
#define NS_TRACE_MALLOC_MAGIC "XPCOM\nTMLog02\r\n\032"
|
||||
#define NS_TRACE_MALLOC_MAGIC_SIZE 16
|
||||
@ -76,7 +77,7 @@ typedef struct nsTMStats {
|
||||
#define NS_TMSTATS_STATIC_INITIALIZER {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
|
||||
|
||||
/**
|
||||
* Call NS_TraceMalloc with a valid log file descriptor to enable logging
|
||||
* Call NS_TraceMallocStartup with a valid file descriptor to enable logging
|
||||
* of compressed malloc traces, including callsite chains. Integers may be
|
||||
* unsigned serial numbers, sizes, or offsets, and require at most 32 bits.
|
||||
* They're encoded as follows:
|
||||
@ -103,6 +104,15 @@ typedef struct nsTMStats {
|
||||
*
|
||||
* See xpcom/base/bloatblame.c for an example log-file reader.
|
||||
*/
|
||||
#define TM_EVENT_LIBRARY 'L'
|
||||
#define TM_EVENT_METHOD 'N'
|
||||
#define TM_EVENT_CALLSITE 'S'
|
||||
#define TM_EVENT_MALLOC 'M'
|
||||
#define TM_EVENT_CALLOC 'C'
|
||||
#define TM_EVENT_REALLOC 'R'
|
||||
#define TM_EVENT_FREE 'F'
|
||||
#define TM_EVENT_STATS 'Z'
|
||||
|
||||
PR_EXTERN(void) NS_TraceMallocStartup(int logfd);
|
||||
|
||||
/**
|
||||
|
@ -135,14 +135,14 @@ static int get_tmevent(FILE *fp, tmevent *event)
|
||||
if (!get_uint32(fp, &event->serial))
|
||||
return 0;
|
||||
switch (c) {
|
||||
case 'L':
|
||||
case TM_EVENT_LIBRARY:
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
event->u.libname = s;
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
case TM_EVENT_METHOD:
|
||||
if (!get_uint32(fp, &event->u.method.library))
|
||||
return 0;
|
||||
s = get_string(fp);
|
||||
@ -151,7 +151,7 @@ static int get_tmevent(FILE *fp, tmevent *event)
|
||||
event->u.method.name = s;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
case TM_EVENT_CALLSITE:
|
||||
if (!get_uint32(fp, &event->u.site.parent))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.site.method))
|
||||
@ -160,22 +160,22 @@ static int get_tmevent(FILE *fp, tmevent *event)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
case 'C':
|
||||
case 'F':
|
||||
case TM_EVENT_MALLOC:
|
||||
case TM_EVENT_CALLOC:
|
||||
case TM_EVENT_FREE:
|
||||
event->u.alloc.oldsize = 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
case TM_EVENT_REALLOC:
|
||||
if (!get_uint32(fp, &event->u.alloc.oldsize))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
case TM_EVENT_STATS:
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxstack))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.stats.tmstats.calltree_maxdepth))
|
||||
@ -341,8 +341,8 @@ void tmreader_destroy(tmreader *tmr)
|
||||
free(tmr);
|
||||
}
|
||||
|
||||
int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler)
|
||||
int tmreader_eventloop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[NS_TRACE_MALLOC_MAGIC_SIZE];
|
||||
@ -369,7 +369,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
|
||||
while (get_tmevent(fp, &event)) {
|
||||
switch (event.type) {
|
||||
case 'L': {
|
||||
case TM_EVENT_LIBRARY: {
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry **hep, *he;
|
||||
@ -385,12 +385,12 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
event.u.libname);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'N': {
|
||||
case TM_EVENT_METHOD: {
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry **hep, *he;
|
||||
@ -408,7 +408,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
he = PL_HashTableRawAdd(tmr->methods, hep, hash, key, name);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
meth = (tmgraphnode*) he;
|
||||
|
||||
@ -442,7 +442,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
}
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
comp = (tmgraphnode*) he;
|
||||
|
||||
@ -464,7 +464,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
break;
|
||||
}
|
||||
|
||||
case 'S': {
|
||||
case TM_EVENT_CALLSITE: {
|
||||
const void *key, *mkey;
|
||||
PLHashNumber hash, mhash;
|
||||
PLHashEntry **hep, *he;
|
||||
@ -481,7 +481,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
if (event.u.site.parent == 0) {
|
||||
parent = &tmr->calltree_root;
|
||||
} else {
|
||||
parent = tmreader_get_callsite(tmr, event.u.site.parent);
|
||||
parent = tmreader_callsite(tmr, event.u.site.parent);
|
||||
if (!parent) {
|
||||
fprintf(stderr, "%s: no parent for %lu (%lu)!\n",
|
||||
tmr->program, (unsigned long) event.serial,
|
||||
@ -493,7 +493,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
he = PL_HashTableRawAdd(tmr->callsites, hep, hash, key, NULL);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
site = (tmcallsite*) he;
|
||||
@ -513,15 +513,15 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
break;
|
||||
}
|
||||
|
||||
case 'M':
|
||||
case 'C':
|
||||
case 'R': {
|
||||
case TM_EVENT_MALLOC:
|
||||
case TM_EVENT_CALLOC:
|
||||
case TM_EVENT_REALLOC: {
|
||||
tmcallsite *site;
|
||||
int32 size, oldsize, delta;
|
||||
tmgraphnode *meth, *comp, *lib;
|
||||
double sqdelta, sqszdelta;
|
||||
|
||||
site = tmreader_get_callsite(tmr, event.serial);
|
||||
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);
|
||||
@ -532,13 +532,13 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
oldsize = (int32)event.u.alloc.oldsize;
|
||||
delta = size - oldsize;
|
||||
site->bytes.direct += delta;
|
||||
if (event.type != 'R')
|
||||
if (event.type != TM_EVENT_REALLOC)
|
||||
site->allocs.direct++;
|
||||
meth = site->method;
|
||||
if (meth) {
|
||||
meth->bytes.direct += delta;
|
||||
sqdelta = delta * delta;
|
||||
if (event.type == 'R') {
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
sqszdelta = ((double)size * size)
|
||||
- ((double)oldsize * oldsize);
|
||||
meth->sqsum += sqszdelta;
|
||||
@ -549,7 +549,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
comp = meth->up;
|
||||
if (comp) {
|
||||
comp->bytes.direct += delta;
|
||||
if (event.type == 'R') {
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
comp->sqsum += sqszdelta;
|
||||
} else {
|
||||
comp->sqsum += sqdelta;
|
||||
@ -558,7 +558,7 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
lib = comp->up;
|
||||
if (lib) {
|
||||
lib->bytes.direct += delta;
|
||||
if (event.type == 'R') {
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
lib->sqsum += sqszdelta;
|
||||
} else {
|
||||
lib->sqsum += sqdelta;
|
||||
@ -570,10 +570,10 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
break;
|
||||
}
|
||||
|
||||
case 'F':
|
||||
case TM_EVENT_FREE:
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
case TM_EVENT_STATS:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -583,11 +583,38 @@ int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
return 1;
|
||||
}
|
||||
|
||||
tmcallsite *tmreader_get_callsite(tmreader *tmr, uint32 serial)
|
||||
tmgraphnode *tmreader_library(tmreader *tmr, uint32 serial)
|
||||
{
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->libraries, hash, key);
|
||||
}
|
||||
|
||||
tmgraphnode *tmreader_component(tmreader *tmr, const char *name)
|
||||
{
|
||||
PLHashNumber hash;
|
||||
|
||||
hash = PL_HashString(name);
|
||||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->components, hash, name);
|
||||
}
|
||||
|
||||
tmgraphnode *tmreader_method(tmreader *tmr, uint32 serial)
|
||||
{
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
return (tmgraphnode*) *PL_HashTableRawLookup(tmr->methods, hash, key);
|
||||
}
|
||||
|
||||
tmcallsite *tmreader_callsite(tmreader *tmr, uint32 serial)
|
||||
{
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
tmcallsite *site;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
|
@ -43,6 +43,7 @@ PR_BEGIN_EXTERN_C
|
||||
|
||||
typedef struct tmreader tmreader;
|
||||
typedef struct tmevent tmevent;
|
||||
typedef struct tmcounts tmcounts;
|
||||
typedef struct tmgraphedge tmgraphedge;
|
||||
typedef struct tmgraphnode tmgraphnode;
|
||||
typedef struct tmcallsite tmcallsite;
|
||||
@ -73,10 +74,10 @@ struct tmevent {
|
||||
} u;
|
||||
};
|
||||
|
||||
typedef struct tmcounts {
|
||||
struct tmcounts {
|
||||
int32 direct; /* things allocated by this node's code */
|
||||
int32 total; /* direct + things from all descendents */
|
||||
} tmcounts;
|
||||
};
|
||||
|
||||
struct tmgraphnode {
|
||||
PLHashEntry entry; /* key is serial or name, value must be name */
|
||||
@ -126,11 +127,22 @@ struct tmreader {
|
||||
|
||||
typedef void (*tmeventhandler)(tmreader *tmr, tmevent *event);
|
||||
|
||||
/* The tmreader constructor and destructor. */
|
||||
extern tmreader *tmreader_new(const char *program, void *data);
|
||||
extern void tmreader_destroy(tmreader *tmr);
|
||||
extern int tmreader_loop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler);
|
||||
extern tmcallsite *tmreader_get_callsite(tmreader *tmr, uint32 serial);
|
||||
|
||||
/*
|
||||
* Return -1 on permanent fatal error, 0 if filename can't be opened or is not
|
||||
* a trace-malloc logfile, and 1 on success.
|
||||
*/
|
||||
extern int tmreader_eventloop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler);
|
||||
|
||||
/* Map serial number or name to graphnode or callsite. */
|
||||
extern tmgraphnode *tmreader_library(tmreader *tmr, uint32 serial);
|
||||
extern tmgraphnode *tmreader_component(tmreader *tmr, const char *name);
|
||||
extern tmgraphnode *tmreader_method(tmreader *tmr, uint32 serial);
|
||||
extern tmcallsite *tmreader_callsite(tmreader *tmr, uint32 serial);
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user