зеркало из https://github.com/mozilla/pjs.git
Revamp REALLOC record for rayw's tool; add flush-log-files function (r=rayw, a=waterson, not part of build).
This commit is contained in:
Родитель
d304d8eb34
Коммит
4c89c9e0d6
|
@ -895,21 +895,23 @@ __ptr_t calloc(size_t count, size_t size)
|
|||
|
||||
__ptr_t realloc(__ptr_t ptr, size_t size)
|
||||
{
|
||||
callsite *oldsite, *site;
|
||||
size_t oldsize;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry *he;
|
||||
allocation *alloc;
|
||||
callsite *site;
|
||||
|
||||
tmstats.realloc_calls++;
|
||||
if (suppress_tracing == 0) {
|
||||
if (tmmon)
|
||||
PR_EnterMonitor(tmmon);
|
||||
oldsite = NULL;
|
||||
oldsize = 0;
|
||||
if (ptr && get_allocations()) {
|
||||
hash = hash_pointer(ptr);
|
||||
he = *PL_HashTableRawLookup(allocations, hash, ptr);
|
||||
if (he) {
|
||||
oldsite = (callsite*) he->value;
|
||||
alloc = (allocation*) he;
|
||||
oldsize = alloc->size;
|
||||
}
|
||||
|
@ -938,8 +940,10 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
|
|||
PR_EnterMonitor(tmmon);
|
||||
#endif
|
||||
site = backtrace(1);
|
||||
if (site)
|
||||
log_event3(logfp, TM_EVENT_REALLOC, site->serial, oldsize, size);
|
||||
if (site) {
|
||||
log_event4(logfp, TM_EVENT_REALLOC, site->serial, size,
|
||||
oldsite ? oldsite->serial : 0, oldsize);
|
||||
}
|
||||
if (ptr && allocations) {
|
||||
suppress_tracing++;
|
||||
he = PL_HashTableAdd(allocations, ptr, site);
|
||||
|
@ -1298,4 +1302,19 @@ NS_TraceMallocDumpAllocations(const char *pathname)
|
|||
return rv;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
NS_TraceMallocFlushLogfiles()
|
||||
{
|
||||
logfile *fp;
|
||||
|
||||
if (tmmon)
|
||||
PR_EnterMonitor(tmmon);
|
||||
|
||||
for (fp = logfile_list; fp; fp = fp->next)
|
||||
flush_logfile(fp);
|
||||
|
||||
if (tmmon)
|
||||
PR_ExitMonitor(tmmon);
|
||||
}
|
||||
|
||||
#endif /* NS_TRACE_MALLOC */
|
||||
|
|
|
@ -45,7 +45,7 @@ PR_BEGIN_EXTERN_C
|
|||
* NS_TraceMallocStartup comment (below) for magic number differences in log
|
||||
* file structure.
|
||||
*/
|
||||
#define NS_TRACE_MALLOC_MAGIC "XPCOM\nTMLog03\r\n\032"
|
||||
#define NS_TRACE_MALLOC_MAGIC "XPCOM\nTMLog04\r\n\032"
|
||||
#define NS_TRACE_MALLOC_MAGIC_SIZE 16
|
||||
|
||||
/**
|
||||
|
@ -105,6 +105,9 @@ typedef struct nsTMStats {
|
|||
* Event Operands (magic TMLog03)
|
||||
* 'T' seconds, microseconds, caption
|
||||
*
|
||||
* Event Operands (magic TMLog04)
|
||||
* 'R' site serial, realloc size, old site serial, realloc oldsize
|
||||
*
|
||||
* See xpcom/base/bloatblame.c for an example log-file reader.
|
||||
*/
|
||||
#define TM_EVENT_LIBRARY 'L'
|
||||
|
@ -168,6 +171,12 @@ PR_EXTERN(void) NS_TraceMallocLogTimestamp(const char *caption);
|
|||
PR_EXTERN(int)
|
||||
NS_TraceMallocDumpAllocations(const char *pathname);
|
||||
|
||||
/**
|
||||
* Flush all logfile buffers.
|
||||
*/
|
||||
PR_EXTERN(void)
|
||||
NS_TraceMallocFlushLogfiles(void);
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
#endif /* nsTraceMalloc_h___ */
|
||||
|
|
|
@ -169,10 +169,12 @@ static int get_tmevent(FILE *fp, tmevent *event)
|
|||
break;
|
||||
|
||||
case TM_EVENT_REALLOC:
|
||||
if (!get_uint32(fp, &event->u.alloc.oldsize))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.size))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.oldserial))
|
||||
return 0;
|
||||
if (!get_uint32(fp, &event->u.alloc.oldsize))
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case TM_EVENT_STATS:
|
||||
|
|
|
@ -65,8 +65,9 @@ struct tmevent {
|
|||
uint32 offset;
|
||||
} site;
|
||||
struct {
|
||||
uint32 oldsize;
|
||||
uint32 size;
|
||||
uint32 oldserial;
|
||||
uint32 oldsize;
|
||||
} alloc;
|
||||
struct {
|
||||
nsTMStats tmstats;
|
||||
|
|
|
@ -895,21 +895,23 @@ __ptr_t calloc(size_t count, size_t size)
|
|||
|
||||
__ptr_t realloc(__ptr_t ptr, size_t size)
|
||||
{
|
||||
callsite *oldsite, *site;
|
||||
size_t oldsize;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry *he;
|
||||
allocation *alloc;
|
||||
callsite *site;
|
||||
|
||||
tmstats.realloc_calls++;
|
||||
if (suppress_tracing == 0) {
|
||||
if (tmmon)
|
||||
PR_EnterMonitor(tmmon);
|
||||
oldsite = NULL;
|
||||
oldsize = 0;
|
||||
if (ptr && get_allocations()) {
|
||||
hash = hash_pointer(ptr);
|
||||
he = *PL_HashTableRawLookup(allocations, hash, ptr);
|
||||
if (he) {
|
||||
oldsite = (callsite*) he->value;
|
||||
alloc = (allocation*) he;
|
||||
oldsize = alloc->size;
|
||||
}
|
||||
|
@ -938,8 +940,10 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
|
|||
PR_EnterMonitor(tmmon);
|
||||
#endif
|
||||
site = backtrace(1);
|
||||
if (site)
|
||||
log_event3(logfp, TM_EVENT_REALLOC, site->serial, oldsize, size);
|
||||
if (site) {
|
||||
log_event4(logfp, TM_EVENT_REALLOC, site->serial, size,
|
||||
oldsite ? oldsite->serial : 0, oldsize);
|
||||
}
|
||||
if (ptr && allocations) {
|
||||
suppress_tracing++;
|
||||
he = PL_HashTableAdd(allocations, ptr, site);
|
||||
|
@ -1298,4 +1302,19 @@ NS_TraceMallocDumpAllocations(const char *pathname)
|
|||
return rv;
|
||||
}
|
||||
|
||||
PR_IMPLEMENT(void)
|
||||
NS_TraceMallocFlushLogfiles()
|
||||
{
|
||||
logfile *fp;
|
||||
|
||||
if (tmmon)
|
||||
PR_EnterMonitor(tmmon);
|
||||
|
||||
for (fp = logfile_list; fp; fp = fp->next)
|
||||
flush_logfile(fp);
|
||||
|
||||
if (tmmon)
|
||||
PR_ExitMonitor(tmmon);
|
||||
}
|
||||
|
||||
#endif /* NS_TRACE_MALLOC */
|
||||
|
|
|
@ -45,7 +45,7 @@ PR_BEGIN_EXTERN_C
|
|||
* NS_TraceMallocStartup comment (below) for magic number differences in log
|
||||
* file structure.
|
||||
*/
|
||||
#define NS_TRACE_MALLOC_MAGIC "XPCOM\nTMLog03\r\n\032"
|
||||
#define NS_TRACE_MALLOC_MAGIC "XPCOM\nTMLog04\r\n\032"
|
||||
#define NS_TRACE_MALLOC_MAGIC_SIZE 16
|
||||
|
||||
/**
|
||||
|
@ -105,6 +105,9 @@ typedef struct nsTMStats {
|
|||
* Event Operands (magic TMLog03)
|
||||
* 'T' seconds, microseconds, caption
|
||||
*
|
||||
* Event Operands (magic TMLog04)
|
||||
* 'R' site serial, realloc size, old site serial, realloc oldsize
|
||||
*
|
||||
* See xpcom/base/bloatblame.c for an example log-file reader.
|
||||
*/
|
||||
#define TM_EVENT_LIBRARY 'L'
|
||||
|
@ -168,6 +171,12 @@ PR_EXTERN(void) NS_TraceMallocLogTimestamp(const char *caption);
|
|||
PR_EXTERN(int)
|
||||
NS_TraceMallocDumpAllocations(const char *pathname);
|
||||
|
||||
/**
|
||||
* Flush all logfile buffers.
|
||||
*/
|
||||
PR_EXTERN(void)
|
||||
NS_TraceMallocFlushLogfiles(void);
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
#endif /* nsTraceMalloc_h___ */
|
||||
|
|
|
@ -1039,7 +1039,7 @@ nsTraceRefcnt::WalkTheStack(FILE* aStream)
|
|||
Dl_info info;
|
||||
int ok = dladdr((void*) pc, &info);
|
||||
if (!ok) {
|
||||
fprintf(aStream, "UNKNOWN 0x%08X\n", (char*)pc);
|
||||
fprintf(aStream, "UNKNOWN 0x%8p\n", (char*)pc);
|
||||
bp = nextbp;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1039,7 +1039,7 @@ nsTraceRefcnt::WalkTheStack(FILE* aStream)
|
|||
Dl_info info;
|
||||
int ok = dladdr((void*) pc, &info);
|
||||
if (!ok) {
|
||||
fprintf(aStream, "UNKNOWN 0x%08X\n", (char*)pc);
|
||||
fprintf(aStream, "UNKNOWN 0x%8p\n", (char*)pc);
|
||||
bp = nextbp;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,700 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is tmreader.h/tmreader.c code, released
|
||||
* July 7, 2000.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2000 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brendan Eich, 7-July-2000
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU Public License (the "GPL"), in which case the
|
||||
* provisions of the GPL are applicable instead of those above.
|
||||
* If you wish to allow use of your version of this file only
|
||||
* under the terms of the GPL and not to allow others to use your
|
||||
* version of this file under the MPL, indicate your decision by
|
||||
* deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h> /* XXX push error reporting out to clients? */
|
||||
#include <unistd.h>
|
||||
#include "prlog.h"
|
||||
#include "plhash.h"
|
||||
#include "nsTraceMalloc.h"
|
||||
#include "tmreader.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int get_tmevent(FILE *fp, tmevent *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 TM_EVENT_LIBRARY:
|
||||
s = get_string(fp);
|
||||
if (!s)
|
||||
return 0;
|
||||
event->u.libname = s;
|
||||
break;
|
||||
|
||||
case TM_EVENT_METHOD:
|
||||
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 TM_EVENT_CALLSITE:
|
||||
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 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 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 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))
|
||||
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 *generic_alloctable(void *pool, PRSize size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void generic_freetable(void *pool, void *item)
|
||||
{
|
||||
free(item);
|
||||
}
|
||||
|
||||
static PLHashEntry *callsite_allocentry(void *pool, const void *key)
|
||||
{
|
||||
return malloc(sizeof(tmcallsite));
|
||||
}
|
||||
|
||||
static PLHashEntry *graphnode_allocentry(void *pool, const void *key)
|
||||
{
|
||||
tmgraphnode *node = (tmgraphnode*) malloc(sizeof(tmgraphnode));
|
||||
if (!node)
|
||||
return NULL;
|
||||
node->in = node->out = NULL;
|
||||
node->up = node->down = node->next = NULL;
|
||||
node->low = 0;
|
||||
node->allocs.bytes.direct = node->allocs.bytes.total = 0;
|
||||
node->allocs.calls.direct = node->allocs.calls.total = 0;
|
||||
node->frees.bytes.direct = node->frees.bytes.total = 0;
|
||||
node->frees.calls.direct = node->frees.calls.total = 0;
|
||||
node->sqsum = 0;
|
||||
node->sort = -1;
|
||||
return &node->entry;
|
||||
}
|
||||
|
||||
static void graphnode_freeentry(void *pool, PLHashEntry *he, PRUintn flag)
|
||||
{
|
||||
/* Always free the value, which points to a strdup'd string. */
|
||||
free(he->value);
|
||||
|
||||
/* Free the whole thing if we're told to. */
|
||||
if (flag == HT_FREE_ENTRY)
|
||||
free((void*) he);
|
||||
}
|
||||
|
||||
static void component_freeentry(void *pool, PLHashEntry *he, PRUintn flag)
|
||||
{
|
||||
if (flag == HT_FREE_ENTRY) {
|
||||
tmgraphnode *comp = (tmgraphnode*) he;
|
||||
|
||||
/* Free the key, which was strdup'd (N.B. value also points to it). */
|
||||
free((void*) tmcomponent_name(comp));
|
||||
free((void*) comp);
|
||||
}
|
||||
}
|
||||
|
||||
static PLHashAllocOps callsite_hashallocops = {
|
||||
generic_alloctable, generic_freetable,
|
||||
callsite_allocentry, graphnode_freeentry
|
||||
};
|
||||
|
||||
static PLHashAllocOps graphnode_hashallocops = {
|
||||
generic_alloctable, generic_freetable,
|
||||
graphnode_allocentry, graphnode_freeentry
|
||||
};
|
||||
|
||||
static PLHashAllocOps component_hashallocops = {
|
||||
generic_alloctable, generic_freetable,
|
||||
graphnode_allocentry, component_freeentry
|
||||
};
|
||||
|
||||
static PLHashNumber hash_serial(const void *key)
|
||||
{
|
||||
return (PLHashNumber) key;
|
||||
}
|
||||
|
||||
tmreader *tmreader_new(const char *program, void *data)
|
||||
{
|
||||
tmreader *tmr;
|
||||
|
||||
tmr = calloc(1, sizeof *tmr);
|
||||
if (!tmr)
|
||||
return NULL;
|
||||
tmr->program = program;
|
||||
tmr->data = data;
|
||||
|
||||
tmr->libraries = PL_NewHashTable(100, hash_serial, PL_CompareValues,
|
||||
PL_CompareStrings, &graphnode_hashallocops,
|
||||
NULL);
|
||||
tmr->components = PL_NewHashTable(10000, PL_HashString, PL_CompareStrings,
|
||||
PL_CompareValues, &component_hashallocops,
|
||||
NULL);
|
||||
tmr->methods = PL_NewHashTable(10000, hash_serial, PL_CompareValues,
|
||||
PL_CompareStrings, &graphnode_hashallocops,
|
||||
NULL);
|
||||
tmr->callsites = PL_NewHashTable(200000, hash_serial, PL_CompareValues,
|
||||
PL_CompareValues, &callsite_hashallocops,
|
||||
NULL);
|
||||
tmr->calltree_root.entry.value = (void*) strdup("root");
|
||||
|
||||
if (!tmr->libraries || !tmr->components || !tmr->methods ||
|
||||
!tmr->callsites || !tmr->calltree_root.entry.value) {
|
||||
tmreader_destroy(tmr);
|
||||
return NULL;
|
||||
}
|
||||
return tmr;
|
||||
}
|
||||
|
||||
void tmreader_destroy(tmreader *tmr)
|
||||
{
|
||||
if (tmr->libraries)
|
||||
PL_HashTableDestroy(tmr->libraries);
|
||||
if (tmr->components)
|
||||
PL_HashTableDestroy(tmr->components);
|
||||
if (tmr->methods)
|
||||
PL_HashTableDestroy(tmr->methods);
|
||||
if (tmr->callsites)
|
||||
PL_HashTableDestroy(tmr->callsites);
|
||||
free(tmr);
|
||||
}
|
||||
|
||||
int tmreader_eventloop(tmreader *tmr, const char *filename,
|
||||
tmeventhandler eventhandler)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[NS_TRACE_MALLOC_MAGIC_SIZE];
|
||||
tmevent event;
|
||||
static const char magic[] = NS_TRACE_MALLOC_MAGIC;
|
||||
|
||||
if (strcmp(filename, "-") == 0) {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "%s: can't open %s: %s.\n",
|
||||
tmr->program, filename, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (read(fileno(fp), buf, sizeof buf) != sizeof buf ||
|
||||
strncmp(buf, magic, sizeof buf) != 0) {
|
||||
fprintf(stderr, "%s: bad magic string %s at start of %s.\n",
|
||||
tmr->program, buf, filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (get_tmevent(fp, &event)) {
|
||||
switch (event.type) {
|
||||
case TM_EVENT_LIBRARY: {
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry **hep, *he;
|
||||
|
||||
key = (const void*) event.serial;
|
||||
hash = hash_serial(key);
|
||||
hep = PL_HashTableRawLookup(tmr->libraries, hash, key);
|
||||
he = *hep;
|
||||
PR_ASSERT(!he);
|
||||
if (he) exit(2);
|
||||
|
||||
he = PL_HashTableRawAdd(tmr->libraries, hep, hash, key,
|
||||
event.u.libname);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TM_EVENT_METHOD: {
|
||||
const void *key;
|
||||
PLHashNumber hash;
|
||||
PLHashEntry **hep, *he;
|
||||
char *name, *head, *mark, save;
|
||||
tmgraphnode *meth, *comp, *lib;
|
||||
|
||||
key = (const void*) event.serial;
|
||||
hash = hash_serial(key);
|
||||
hep = PL_HashTableRawLookup(tmr->methods, hash, key);
|
||||
he = *hep;
|
||||
PR_ASSERT(!he);
|
||||
if (he) exit(2);
|
||||
|
||||
name = event.u.method.name;
|
||||
he = PL_HashTableRawAdd(tmr->methods, hep, hash, key, name);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return -1;
|
||||
}
|
||||
meth = (tmgraphnode*) he;
|
||||
|
||||
head = name;
|
||||
mark = strchr(name, ':');
|
||||
if (!mark) {
|
||||
mark = name;
|
||||
while (*mark != '\0' && *mark == '_')
|
||||
mark++;
|
||||
head = mark;
|
||||
mark = strchr(head, '_');
|
||||
if (!mark) {
|
||||
mark = strchr(head, '+');
|
||||
if (!mark)
|
||||
mark = head + strlen(head);
|
||||
}
|
||||
}
|
||||
|
||||
save = *mark;
|
||||
*mark = '\0';
|
||||
hash = PL_HashString(head);
|
||||
hep = PL_HashTableRawLookup(tmr->components, hash, head);
|
||||
he = *hep;
|
||||
if (he) {
|
||||
comp = (tmgraphnode*) he;
|
||||
} else {
|
||||
head = strdup(head);
|
||||
if (head) {
|
||||
he = PL_HashTableRawAdd(tmr->components, hep, hash, head,
|
||||
head);
|
||||
}
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return -1;
|
||||
}
|
||||
comp = (tmgraphnode*) he;
|
||||
|
||||
key = (const void*) event.u.method.library;
|
||||
hash = hash_serial(key);
|
||||
lib = (tmgraphnode*)
|
||||
*PL_HashTableRawLookup(tmr->libraries, hash, key);
|
||||
if (lib) {
|
||||
comp->up = lib;
|
||||
comp->next = lib->down;
|
||||
lib->down = comp;
|
||||
}
|
||||
}
|
||||
*mark = save;
|
||||
|
||||
meth->up = comp;
|
||||
meth->next = comp->down;
|
||||
comp->down = meth;
|
||||
break;
|
||||
}
|
||||
|
||||
case TM_EVENT_CALLSITE: {
|
||||
const void *key, *mkey;
|
||||
PLHashNumber hash, mhash;
|
||||
PLHashEntry **hep, *he;
|
||||
tmcallsite *site, *parent;
|
||||
tmgraphnode *meth;
|
||||
|
||||
key = (const void*) event.serial;
|
||||
hash = hash_serial(key);
|
||||
hep = PL_HashTableRawLookup(tmr->callsites, hash, key);
|
||||
he = *hep;
|
||||
PR_ASSERT(!he);
|
||||
if (he) exit(2);
|
||||
|
||||
if (event.u.site.parent == 0) {
|
||||
parent = &tmr->calltree_root;
|
||||
} else {
|
||||
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,
|
||||
(unsigned long) event.u.site.parent);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
he = PL_HashTableRawAdd(tmr->callsites, hep, hash, key, NULL);
|
||||
if (!he) {
|
||||
perror(tmr->program);
|
||||
return -1;
|
||||
}
|
||||
|
||||
site = (tmcallsite*) he;
|
||||
site->parent = parent;
|
||||
site->siblings = parent->kids;
|
||||
parent->kids = site;
|
||||
site->kids = NULL;
|
||||
|
||||
mkey = (const void*) event.u.site.method;
|
||||
mhash = hash_serial(mkey);
|
||||
meth = (tmgraphnode*)
|
||||
*PL_HashTableRawLookup(tmr->methods, mhash, mkey);
|
||||
site->method = meth;
|
||||
site->offset = event.u.site.offset;
|
||||
site->allocs.bytes.direct = site->allocs.bytes.total = 0;
|
||||
site->allocs.calls.direct = site->allocs.calls.total = 0;
|
||||
site->frees.bytes.direct = site->frees.bytes.total = 0;
|
||||
site->frees.calls.direct = site->frees.calls.total = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case TM_EVENT_MALLOC:
|
||||
case TM_EVENT_CALLOC:
|
||||
case TM_EVENT_REALLOC: {
|
||||
tmcallsite *site;
|
||||
uint32 size, oldsize;
|
||||
double delta, sqdelta, sqszdelta;
|
||||
tmgraphnode *meth, *comp, *lib;
|
||||
|
||||
site = tmreader_callsite(tmr, event.serial);
|
||||
if (!site) {
|
||||
fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n",
|
||||
tmr->program, event.type, (unsigned long) event.serial);
|
||||
continue;
|
||||
}
|
||||
|
||||
size = event.u.alloc.size;
|
||||
oldsize = event.u.alloc.oldsize;
|
||||
delta = (double)size - (double)oldsize;
|
||||
site->allocs.bytes.direct += delta;
|
||||
if (event.type != TM_EVENT_REALLOC)
|
||||
site->allocs.calls.direct++;
|
||||
meth = site->method;
|
||||
if (meth) {
|
||||
meth->allocs.bytes.direct += delta;
|
||||
sqdelta = delta * delta;
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
sqszdelta = ((double)size * size)
|
||||
- ((double)oldsize * oldsize);
|
||||
meth->sqsum += sqszdelta;
|
||||
} else {
|
||||
meth->sqsum += sqdelta;
|
||||
meth->allocs.calls.direct++;
|
||||
}
|
||||
comp = meth->up;
|
||||
if (comp) {
|
||||
comp->allocs.bytes.direct += delta;
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
comp->sqsum += sqszdelta;
|
||||
} else {
|
||||
comp->sqsum += sqdelta;
|
||||
comp->allocs.calls.direct++;
|
||||
}
|
||||
lib = comp->up;
|
||||
if (lib) {
|
||||
lib->allocs.bytes.direct += delta;
|
||||
if (event.type == TM_EVENT_REALLOC) {
|
||||
lib->sqsum += sqszdelta;
|
||||
} else {
|
||||
lib->sqsum += sqdelta;
|
||||
lib->allocs.calls.direct++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TM_EVENT_FREE: {
|
||||
tmcallsite *site;
|
||||
uint32 size;
|
||||
tmgraphnode *meth, *comp, *lib;
|
||||
|
||||
site = tmreader_callsite(tmr, event.serial);
|
||||
if (!site) {
|
||||
fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n",
|
||||
tmr->program, event.type, (unsigned long) event.serial);
|
||||
continue;
|
||||
}
|
||||
size = event.u.alloc.size;
|
||||
site->frees.bytes.direct += size;
|
||||
site->frees.calls.direct++;
|
||||
meth = site->method;
|
||||
if (meth) {
|
||||
meth->frees.bytes.direct += size;
|
||||
meth->frees.calls.direct++;
|
||||
comp = meth->up;
|
||||
if (comp) {
|
||||
comp->frees.bytes.direct += size;
|
||||
comp->frees.calls.direct++;
|
||||
lib = comp->up;
|
||||
if (lib) {
|
||||
lib->frees.bytes.direct += size;
|
||||
lib->frees.calls.direct++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TM_EVENT_STATS:
|
||||
break;
|
||||
}
|
||||
|
||||
eventhandler(tmr, &event);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
key = (const void*) serial;
|
||||
hash = hash_serial(key);
|
||||
return (tmcallsite*) *PL_HashTableRawLookup(tmr->callsites, hash, key);
|
||||
}
|
||||
|
||||
int tmgraphnode_connect(tmgraphnode *from, tmgraphnode *to, tmcallsite *site)
|
||||
{
|
||||
tmgraphlink *outlink;
|
||||
tmgraphedge *edge;
|
||||
|
||||
for (outlink = from->out; outlink; outlink = outlink->next) {
|
||||
if (outlink->node == to) {
|
||||
/*
|
||||
* Say the stack looks like this: ... => JS => js => JS => js.
|
||||
* We must avoid overcounting JS=>js because the first edge total
|
||||
* includes the second JS=>js edge's total (which is because the
|
||||
* lower site's total includes all its kids' totals).
|
||||
*/
|
||||
edge = TM_LINK_TO_EDGE(outlink, TM_EDGE_OUT_LINK);
|
||||
if (!to->low || to->low < from->low) {
|
||||
/* Add the direct and total counts to edge->allocs. */
|
||||
edge->allocs.bytes.direct += site->allocs.bytes.direct;
|
||||
edge->allocs.bytes.total += site->allocs.bytes.total;
|
||||
edge->allocs.calls.direct += site->allocs.calls.direct;
|
||||
edge->allocs.calls.total += site->allocs.calls.total;
|
||||
|
||||
/* Now update the free counts. */
|
||||
edge->frees.bytes.direct += site->frees.bytes.direct;
|
||||
edge->frees.bytes.total += site->frees.bytes.total;
|
||||
edge->frees.calls.direct += site->frees.calls.direct;
|
||||
edge->frees.calls.total += site->frees.calls.total;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
edge = (tmgraphedge*) malloc(sizeof(tmgraphedge));
|
||||
if (!edge)
|
||||
return 0;
|
||||
edge->links[TM_EDGE_OUT_LINK].node = to;
|
||||
edge->links[TM_EDGE_OUT_LINK].next = from->out;
|
||||
from->out = &edge->links[TM_EDGE_OUT_LINK];
|
||||
edge->links[TM_EDGE_IN_LINK].node = from;
|
||||
edge->links[TM_EDGE_IN_LINK].next = to->in;
|
||||
to->in = &edge->links[TM_EDGE_IN_LINK];
|
||||
edge->allocs = site->allocs;
|
||||
edge->frees = site->frees;
|
||||
return 1;
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is tmreader.h/tmreader.c code, released
|
||||
* July 7, 2000.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2000 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brendan Eich, 7-July-2000
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU Public License (the "GPL"), in which case the
|
||||
* provisions of the GPL are applicable instead of those above.
|
||||
* If you wish to allow use of your version of this file only
|
||||
* under the terms of the GPL and not to allow others to use your
|
||||
* version of this file under the MPL, indicate your decision by
|
||||
* deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*/
|
||||
#ifndef tmreader_h___
|
||||
#define tmreader_h___
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "plhash.h"
|
||||
#include "nsTraceMalloc.h"
|
||||
|
||||
PR_BEGIN_EXTERN_C
|
||||
|
||||
typedef struct tmreader tmreader;
|
||||
typedef struct tmevent tmevent;
|
||||
typedef struct tmcounts tmcounts;
|
||||
typedef struct tmallcounts tmallcounts;
|
||||
typedef struct tmgraphlink tmgraphlink;
|
||||
typedef struct tmgraphedge tmgraphedge;
|
||||
typedef struct tmgraphnode tmgraphnode;
|
||||
typedef struct tmcallsite tmcallsite;
|
||||
|
||||
struct tmevent {
|
||||
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;
|
||||
};
|
||||
|
||||
struct tmcounts {
|
||||
uint32 direct; /* things allocated by this node's code */
|
||||
uint32 total; /* direct + things from all descendents */
|
||||
};
|
||||
|
||||
struct tmallcounts {
|
||||
tmcounts bytes;
|
||||
tmcounts calls;
|
||||
};
|
||||
|
||||
struct tmgraphnode {
|
||||
PLHashEntry entry; /* key is serial or name, value must be name */
|
||||
tmgraphlink *in;
|
||||
tmgraphlink *out;
|
||||
tmgraphnode *up; /* parent in supergraph, e.g., JS for JS_*() */
|
||||
tmgraphnode *down; /* subgraph kids, declining bytes.total order */
|
||||
tmgraphnode *next; /* next kid in supergraph node's down list */
|
||||
int low; /* 0 or lowest current tree walk level */
|
||||
tmallcounts allocs;
|
||||
tmallcounts frees;
|
||||
double sqsum; /* sum of squared bytes.direct */
|
||||
int sort; /* sorted index in node table, -1 if no table */
|
||||
};
|
||||
|
||||
#define tmgraphnode_name(node) ((char*) (node)->entry.value)
|
||||
|
||||
#define tmlibrary_serial(lib) ((uint32) (lib)->entry.key)
|
||||
#define tmcomponent_name(comp) ((const char*) (comp)->entry.key)
|
||||
|
||||
/* Half a graphedge, not including per-edge allocation stats. */
|
||||
struct tmgraphlink {
|
||||
tmgraphlink *next; /* next fanning out from or into a node */
|
||||
tmgraphnode *node; /* the other node (to if OUT, from if IN) */
|
||||
};
|
||||
|
||||
/*
|
||||
* It's safe to downcast a "from" tmgraphlink (one linked from a node's out
|
||||
* pointer) to tmgraphedge. To go from an "out" (linked via tmgraphedge.from)
|
||||
* or "in" (linked via tmgraphedge.to) list link to its containing edge, use
|
||||
* TM_LINK_TO_EDGE(link, which).
|
||||
*/
|
||||
struct tmgraphedge {
|
||||
tmgraphlink links[2];
|
||||
tmallcounts allocs;
|
||||
tmallcounts frees;
|
||||
};
|
||||
|
||||
/* Indices into tmgraphedge.links -- out must come first. */
|
||||
#define TM_EDGE_OUT_LINK 0
|
||||
#define TM_EDGE_IN_LINK 1
|
||||
|
||||
#define TM_LINK_TO_EDGE(link,which) ((tmgraphedge*) &(link)[-(which)])
|
||||
|
||||
struct tmcallsite {
|
||||
PLHashEntry entry; /* key is site serial number */
|
||||
tmcallsite *parent; /* calling site */
|
||||
tmcallsite *siblings; /* other sites reached from parent */
|
||||
tmcallsite *kids; /* sites reached from here */
|
||||
tmgraphnode *method; /* method node in tmr->methods graph */
|
||||
uint32 offset; /* pc offset from start of method */
|
||||
tmallcounts allocs;
|
||||
tmallcounts frees;
|
||||
};
|
||||
|
||||
struct tmreader {
|
||||
const char *program;
|
||||
void *data;
|
||||
PLHashTable *libraries;
|
||||
PLHashTable *components;
|
||||
PLHashTable *methods;
|
||||
PLHashTable *callsites;
|
||||
tmcallsite calltree_root;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* Connect node 'from' to node 'to' with an edge, if there isn't one already
|
||||
* connecting the nodes. Add site's allocation stats to the edge only if we
|
||||
* create the edge, or if we find that it exists, but that to->low is zero or
|
||||
* less than from->low.
|
||||
*
|
||||
* If the callsite tree already totals allocation costs (tmcounts.total for
|
||||
* each site includes tmcounts.direct for that site, plus tmcounts.total for
|
||||
* all kid sites), then the node->low watermarks should be set from the tree
|
||||
* level when walking the callsite tree, and should be set to non-zero values
|
||||
* only if zero (the root is at level 0). A low watermark should be cleared
|
||||
* when the tree walk unwinds past the level at which it was set non-zero.
|
||||
*
|
||||
* Return 0 on error (malloc failure) and 1 on success.
|
||||
*/
|
||||
extern int tmgraphnode_connect(tmgraphnode *from, tmgraphnode *to,
|
||||
tmcallsite *site);
|
||||
|
||||
PR_END_EXTERN_C
|
||||
|
||||
#endif /* tmreader_h___ */
|
Загрузка…
Ссылка в новой задаче