Finish factoring tmreader from bloatblame; misc. clenaups (not part of build).

This commit is contained in:
brendan%mozilla.org 2000-07-21 20:42:15 +00:00
parent f0d4936e2e
commit 02c5512498
10 changed files with 242 additions and 500 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
/**

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
/**

View File

@ -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);

View File

@ -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