1. Fix jsdhash/pldhash.[ch] so that add/remove cycles for a given k do not

create a long chain of removed sentinels.  Also, when adding k to a table
    where k is not mapped, but where k hashes to a chain that includes removed
    sentinels, recycle the first removed sentinel in the chain for k's entry.
2.  Cache cx->resolving till js_DestroyContext, to avoid high JSDHashTable
    new/destroy overhead in js_LookupProperty.
3.  Add NS_TraceStack to nsTraceMalloc.[ch] and clean the .c file up a bit.


git-svn-id: svn://10.0.0.236/trunk@102796 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
brendan%mozilla.org
2001-09-12 06:39:31 +00:00
parent 6d47ac1557
commit 9457b2de04
11 changed files with 575 additions and 410 deletions

View File

@@ -53,6 +53,7 @@
#include <sys/time.h>
#endif
#include "plhash.h"
#include "pratom.h"
#include "prlog.h"
#include "prmon.h"
#include "prprf.h"
@@ -232,15 +233,15 @@ typedef void (__stdcall *FREEDEBUGPROC) ( void *, int);
struct AllocationFuncs
{
MALLOCPROC malloc_proc;
CALLOCPROC calloc_proc;
REALLOCPROC realloc_proc;
MALLOCPROC malloc_proc;
CALLOCPROC calloc_proc;
REALLOCPROC realloc_proc;
FREEPROC free_proc;
#ifdef _DEBUG
MALLOCDEBUGPROC malloc_debug_proc;
CALLOCDEBUGPROC calloc_debug_proc;
REALLOCDEBUGPROC realloc_debug_proc;
FREEDEBUGPROC free_debug_proc;
MALLOCDEBUGPROC malloc_debug_proc;
CALLOCDEBUGPROC calloc_debug_proc;
REALLOCDEBUGPROC realloc_debug_proc;
FREEDEBUGPROC free_debug_proc;
#endif
int prevent_reentry;
}gAllocFuncs;
@@ -271,6 +272,24 @@ static logfile *logfp = &default_logfile;
static PRMonitor *tmmon = NULL;
static char *sdlogname = NULL; /* filename for shutdown leak log */
/*
* This counter suppresses tracing, in case any tracing code needs to malloc,
* and it must be tested and manipulated only within tmmon.
*/
static uint32 suppress_tracing = 0;
#define TM_ENTER_MONITOR() \
PR_BEGIN_MACRO \
if (tmmon) \
PR_EnterMonitor(tmmon); \
PR_END_MACRO
#define TM_EXIT_MONITOR() \
PR_BEGIN_MACRO \
if (tmmon) \
PR_ExitMonitor(tmmon); \
PR_END_MACRO
/* We don't want more than 32 logfiles open at once, ok? */
typedef uint32 lfd_set;
@@ -319,8 +338,7 @@ retry:
static void flush_logfile(logfile *fp)
{
int len, cnt;
int fd;
int len, cnt, fd;
char *bp;
len = fp->pos;
@@ -442,11 +460,10 @@ struct callsite {
};
/* NB: these counters are incremented and decremented only within tmmon. */
static uint32 suppress_tracing = 0;
static uint32 library_serial_generator = 0;
static uint32 method_serial_generator = 0;
static uint32 callsite_serial_generator = 0;
static uint32 tmstats_serial_generator = 0;
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, 0, NULL, NULL, NULL};
@@ -534,12 +551,11 @@ static PLHashTable *libraries = NULL;
/* Table mapping method names to logged 'N' record serial numbers. */
static PLHashTable *methods = NULL;
#if XP_WIN32
#ifdef XP_WIN32
#define MAX_STACKFRAMES 256
#define MAX_UNMANGLED_NAME_LEN 256
static callsite *calltree(int skip)
{
logfile *fp = logfp;
@@ -570,19 +586,19 @@ static callsite *calltree(int skip)
ok = EnsureSymInitialized();
if (! ok)
return 0;
return 0;
/*
* Get the context information for this thread. That way we will
* know where our sp, fp, pc, etc. are and can fill in the
* STACKFRAME with the initial values.
* Get the context information for this thread. That way we will know
* where our sp, fp, pc, etc. are, and we can fill in the STACKFRAME with
* the initial values.
*/
context.ContextFlags = CONTEXT_FULL;
ok = GetThreadContext(myThread, &context);
if (! ok)
return 0;
return 0;
/* Setup initial stack frame to walk from */
/* Setup initial stack frame from which to walk. */
memset(&(frame[0]), 0, sizeof(frame[0]));
frame[0].AddrPC.Offset = context.Eip;
frame[0].AddrPC.Mode = AddrModeFlat;
@@ -590,40 +606,38 @@ static callsite *calltree(int skip)
frame[0].AddrStack.Mode = AddrModeFlat;
frame[0].AddrFrame.Offset = context.Ebp;
frame[0].AddrFrame.Mode = AddrModeFlat;
while (1) {
PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL) buf;
if (framenum)
{
memcpy(&(frame[framenum]),&(frame[framenum-1]),sizeof(STACKFRAME));
}
for (;;) {
PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL) buf;
if (framenum)
memcpy(&(frame[framenum]),&(frame[framenum-1]),sizeof(STACKFRAME));
ok = _StackWalk(IMAGE_FILE_MACHINE_I386,
myProcess,
myThread,
&(frame[framenum]),
&context,
0, /* read process memory routine */
_SymFunctionTableAccess, /* function table access
routine */
_SymGetModuleBase, /* module base routine */
0); /* translate address routine */
ok = _StackWalk(IMAGE_FILE_MACHINE_I386,
myProcess,
myThread,
&(frame[framenum]),
&context,
0, /* read process memory hook */
_SymFunctionTableAccess, /* function table access hook */
_SymGetModuleBase, /* module base hook */
0); /* translate address hook */
if (!ok) {
break;
}
if (skip)
{
skip--;
continue;/*skip tells us to skip the first skip amount of stackframes*/
}
if (frame[framenum].AddrPC.Offset == 0)
break;
framenum++;
if (!ok)
break;
if (skip) {
/* skip tells us to skip the first skip amount of stackframes */
skip--;
continue;
}
if (frame[framenum].AddrPC.Offset == 0)
break;
framenum++;
}
depth = framenum;
maxstack = (depth > tmstats.calltree_maxstack);
if (maxstack)
tmstats.calltree_maxstack = depth;
/* Reverse the stack again, finding and building a path in the tree. */
parent = &calltree_root;
do {
@@ -675,17 +689,16 @@ static callsite *calltree(int skip)
/*
* Not in tree at all, or not logged to fp: let's find our symbolic
* callsite info. XXX static syms are masked by nearest lower global
* Load up the info for the dll.
*/
if (!_SymGetModuleInfo(myProcess,frame[framenum].AddrPC.Offset,&imagehelp))/*load up the info for the dll*/
{
if (!_SymGetModuleInfo(myProcess,
frame[framenum].AddrPC.Offset,
&imagehelp)) {
DWORD error = GetLastError();
PR_ASSERT(error);
library = "unknown";/* ew */
}
else
{
library = imagehelp.ModuleName;
library = "unknown"; /* XXX mjudge sez "ew!" */
} else {
library = imagehelp.ModuleName;
}
symbol = (PIMAGEHLP_SYMBOL) buf;
@@ -842,9 +855,8 @@ static callsite *calltree(int skip)
return site;
}
#else /*XP_UNIX*/
#else
/*XP_UNIX*/
static callsite *calltree(uint32 *bp)
{
logfile *fp = logfp;
@@ -1116,8 +1128,7 @@ backtrace(int skip)
return site;
}
#else
/*XP_UNIX*/
#else /*XP_UNIX*/
callsite *
backtrace(int skip)
@@ -1153,7 +1164,7 @@ backtrace(int skip)
}
#endif /*XP_WIN32*/
#endif /* XP_UNIX */
typedef struct allocation {
@@ -1228,7 +1239,7 @@ static PLHashTable *new_allocations(void)
#define get_allocations() (allocations ? allocations : new_allocations())
#if XP_UNIX
#ifdef XP_UNIX
__ptr_t malloc(size_t size)
{
@@ -1238,8 +1249,7 @@ __ptr_t malloc(size_t size)
allocation *alloc;
ptr = __libc_malloc(size);
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
tmstats.malloc_calls++;
if (!ptr) {
tmstats.malloc_failures++;
@@ -1257,8 +1267,7 @@ __ptr_t malloc(size_t size)
}
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
return ptr;
}
@@ -1270,8 +1279,7 @@ __ptr_t calloc(size_t count, size_t size)
allocation *alloc;
ptr = __libc_calloc(count, size);
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
tmstats.calloc_calls++;
if (!ptr) {
tmstats.calloc_failures++;
@@ -1290,8 +1298,7 @@ __ptr_t calloc(size_t count, size_t size)
}
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
return ptr;
}
@@ -1304,8 +1311,7 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
PLHashEntry **hep, *he;
allocation *alloc;
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
tmstats.realloc_calls++;
if (suppress_tracing == 0) {
oldptr = ptr;
@@ -1323,13 +1329,11 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
}
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
ptr = __libc_realloc(ptr, size);
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
if (!ptr && size) {
/*
* When realloc() fails, the original block is not freed or moved, so
@@ -1346,8 +1350,9 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
suppress_tracing++;
if (ptr != oldptr) {
/*
* If we're reallocating (not allocating new space by passing
* null to realloc) and realloc moved the block, free oldptr.
* If we're reallocating (not merely allocating new space by
* passing null to realloc) and realloc has moved the block,
* free oldptr.
*/
if (he)
PL_HashTableRemove(allocations, oldptr);
@@ -1356,8 +1361,8 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
he = PL_HashTableAdd(allocations, ptr, site);
} else {
/*
* If we haven't yet recorded an allocation (possibly due to a
* temporary memory shortage), do it now.
* If we haven't yet recorded an allocation (possibly due to
* a temporary memory shortage), do it now.
*/
if (!he)
he = PL_HashTableAdd(allocations, ptr, site);
@@ -1369,8 +1374,7 @@ __ptr_t realloc(__ptr_t ptr, size_t size)
}
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
return ptr;
}
@@ -1380,8 +1384,7 @@ void free(__ptr_t ptr)
callsite *site;
allocation *alloc;
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
tmstats.free_calls++;
if (!ptr) {
tmstats.null_free_calls++;
@@ -1393,17 +1396,18 @@ void free(__ptr_t ptr)
site = (callsite*) he->value;
if (site) {
alloc = (allocation*) he;
log_event2(logfp, TM_EVENT_FREE, site->serial, alloc->size);
log_event2(logfp, TM_EVENT_FREE, site->serial,
alloc->size);
}
PL_HashTableRawRemove(allocations, hep, he);
}
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
__libc_free(ptr);
}
#endif
#endif /* XP_UNIX */
static const char magic[] = NS_TRACE_MALLOC_MAGIC;
@@ -1426,10 +1430,11 @@ PR_IMPLEMENT(void) NS_TraceMallocStartup(int logfd)
atexit(NS_TraceMallocShutdown);
tmmon = PR_NewMonitor();
/*register listeners for win32*/
#ifdef XP_WIN32
/* Register listeners for win32. */
{
StartupHooker();
StartupHooker();
}
#endif
}
@@ -1494,8 +1499,8 @@ PR_IMPLEMENT(int) NS_TraceMallocStartupArgs(int argc, char* argv[])
int pipefds[2];
switch (*tmlogname) {
#if XP_UNIX
case '|':
#ifdef XP_UNIX
case '|':
if (pipe(pipefds) == 0) {
pid_t pid = fork();
if (pid == 0) {
@@ -1544,7 +1549,7 @@ PR_IMPLEMENT(int) NS_TraceMallocStartupArgs(int argc, char* argv[])
exit(1);
}
break;
#endif /*XP_UNIX*/
#endif /*XP_UNIX*/
case '-':
/* Don't log from startup, but do prepare to log later. */
/* XXX traditional meaning of '-' as option argument is "stdin" or "stdout" */
@@ -1610,32 +1615,26 @@ PR_IMPLEMENT(void) NS_TraceMallocDisable()
{
logfile *fp;
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
for (fp = logfile_list; fp; fp = fp->next)
flush_logfile(fp);
suppress_tracing++;
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
}
PR_IMPLEMENT(void) NS_TraceMallocEnable()
{
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
suppress_tracing--;
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
}
PR_IMPLEMENT(int) NS_TraceMallocChangeLogFD(int fd)
{
logfile *oldfp, *fp;
struct stat sb;
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
oldfp = logfp;
if (oldfp->fd != fd) {
flush_logfile(oldfp);
@@ -1646,13 +1645,10 @@ PR_IMPLEMENT(int) NS_TraceMallocChangeLogFD(int fd)
(void) write(fd, magic, NS_TRACE_MALLOC_MAGIC_SIZE);
logfp = fp;
}
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
return oldfp->fd;
}
static PRIntn
lfd_clr_enumerator(PLHashEntry *he, PRIntn i, void *arg)
{
@@ -1667,7 +1663,7 @@ static void
lfd_clr_walk(callsite *site, logfile *fp)
{
callsite *kid;
LFD_CLR(fp->lfd, &site->lfdset);
for (kid = site->kids; kid; kid = kid->siblings)
lfd_clr_walk(kid, fp);
@@ -1678,8 +1674,7 @@ NS_TraceMallocCloseLogFD(int fd)
{
logfile *fp;
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
fp = get_logfile(fd);
if (fp) {
@@ -1710,44 +1705,39 @@ NS_TraceMallocCloseLogFD(int fd)
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
close(fd);
}
PR_IMPLEMENT(void)
NS_TraceMallocLogTimestamp(const char *caption)
{
logfile *fp;
#if defined(XP_UNIX)
#ifdef XP_UNIX
struct timeval tv;
#endif
#if defined(XP_WIN32)
#ifdef XP_WIN32
struct _timeb tb;
#endif
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
fp = logfp;
log_byte(fp, TM_EVENT_TIMESTAMP);
#if defined(XP_UNIX)
#ifdef XP_UNIX
gettimeofday(&tv, NULL);
log_uint32(fp, (uint32) tv.tv_sec);
log_uint32(fp, (uint32) tv.tv_usec);
#endif
#if defined(XP_WIN32)
#ifdef XP_WIN32
_ftime(&tb);
log_uint32(fp, (uint32) tb.time);
log_uint32(fp, (uint32) tb.millitm);
#endif
log_string(fp, caption);
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
}
static PRIntn
@@ -1775,6 +1765,18 @@ allocation_enumerator(PLHashEntry *he, PRIntn i, void *arg)
return HT_ENUMERATE_NEXT;
}
PR_IMPLEMENT(void)
NS_TraceStack(int skip, FILE *ofp)
{
callsite *site;
site = backtrace(skip + 1);
while (site) {
if (site->name || site->parent)
fprintf(ofp, "%s[%s +0x%X]\n", site->name, site->library, site->offset);
site = site->parent;
}
}
PR_IMPLEMENT(int)
NS_TraceMallocDumpAllocations(const char *pathname)
@@ -1796,32 +1798,28 @@ NS_TraceMallocFlushLogfiles()
{
logfile *fp;
if (tmmon)
PR_EnterMonitor(tmmon);
TM_ENTER_MONITOR();
for (fp = logfile_list; fp; fp = fp->next)
flush_logfile(fp);
if (tmmon)
PR_ExitMonitor(tmmon);
TM_EXIT_MONITOR();
}
#ifdef XP_WIN32
PR_IMPLEMENT(void)
PR_IMPLEMENT(void)
MallocCallback(void *aPtr, size_t size)
{
callsite *site;
PLHashEntry *he;
allocation *alloc;
TM_ENTER_MONITOR();
tmstats.malloc_calls++;
if (!aPtr) {
tmstats.malloc_failures++;
} else if (suppress_tracing == 0) {
if (tmmon)
PR_EnterMonitor(tmmon);
site = backtrace(4);
if (site)
log_event2(logfp, TM_EVENT_MALLOC, site->serial, size);
@@ -1834,13 +1832,10 @@ MallocCallback(void *aPtr, size_t size)
alloc->size = size;
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
}
TM_EXIT_MONITOR();
}
PR_IMPLEMENT(void)
CallocCallback(void *ptr, size_t count, size_t size)
{
@@ -1848,12 +1843,11 @@ CallocCallback(void *ptr, size_t count, size_t size)
PLHashEntry *he;
allocation *alloc;
TM_ENTER_MONITOR();
tmstats.calloc_calls++;
if (!ptr) {
tmstats.calloc_failures++;
} else if (suppress_tracing == 0) {
if (tmmon)
PR_EnterMonitor(tmmon);
site = backtrace(1);
size *= count;
if (site)
@@ -1867,9 +1861,8 @@ CallocCallback(void *ptr, size_t count, size_t size)
alloc->size = size;
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
}
TM_EXIT_MONITOR();
}
PR_IMPLEMENT(void)
@@ -1881,13 +1874,12 @@ ReallocCallback(void * oldptr, void *ptr, size_t size)
PLHashEntry **hep, *he;
allocation *alloc;
TM_ENTER_MONITOR();
tmstats.realloc_calls++;
if (suppress_tracing == 0) {
oldsite = NULL;
oldsize = 0;
he = NULL;
if (tmmon)
PR_EnterMonitor(tmmon);
if (oldptr && get_allocations()) {
hash = hash_pointer(oldptr);
hep = PL_HashTableRawLookup(allocations, hash, oldptr);
@@ -1898,31 +1890,15 @@ ReallocCallback(void * oldptr, void *ptr, size_t size)
oldsize = alloc->size;
}
}
#ifdef EXIT_TMMON_AROUND_REALLOC
/* XXX rusty.lynch@intel.com found that oldsize gets corrupted on
his SMP Linux box occasionally, unless tmmon is held across
the call to __libc_realloc. Figure out why that stack var
is being trashed, and until then use his workaround. */
if (tmmon)
PR_ExitMonitor(tmmon);
#endif
}
if (!ptr && size) {
tmstats.realloc_failures++;
#ifndef EXIT_TMMON_AROUND_REALLOC
if (tmmon && suppress_tracing == 0)
PR_ExitMonitor(tmmon);
#endif
}
if (!ptr && size) {
tmstats.realloc_failures++;
/*
* When realloc() fails, the original block is not freed or moved, so
* we'll leave the allocation entry untouched.
*/
} else if (suppress_tracing == 0) {
#ifdef EXIT_TMMON_AROUND_REALLOC
if (tmmon)
PR_EnterMonitor(tmmon);
#endif
/*
* When realloc() fails, the original block is not freed or moved, so
* we'll leave the allocation entry untouched.
*/
} else if (suppress_tracing == 0) {
site = backtrace(1);
if (site) {
log_event4(logfp, TM_EVENT_REALLOC, site->serial, size,
@@ -1954,9 +1930,8 @@ ReallocCallback(void * oldptr, void *ptr, size_t size)
alloc->size = size;
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
}
TM_EXIT_MONITOR();
}
PR_IMPLEMENT(void)
@@ -1966,12 +1941,11 @@ FreeCallback(void * ptr)
callsite *site;
allocation *alloc;
TM_ENTER_MONITOR();
tmstats.free_calls++;
if (!ptr) {
tmstats.null_free_calls++;
} else if (suppress_tracing == 0) {
if (tmmon)
PR_EnterMonitor(tmmon);
if (get_allocations()) {
hep = PL_HashTableRawLookup(allocations, hash_pointer(ptr), ptr);
he = *hep;
@@ -1984,11 +1958,10 @@ FreeCallback(void * ptr)
PL_HashTableRawRemove(allocations, hep, he);
}
}
if (tmmon)
PR_ExitMonitor(tmmon);
}
TM_EXIT_MONITOR();
}
#endif /*XP_WIN32*/
#endif /* NS_TRACE_MALLOC */

View File

@@ -161,6 +161,15 @@ PR_EXTERN(void) NS_TraceMallocCloseLogFD(int fd);
*/
PR_EXTERN(void) NS_TraceMallocLogTimestamp(const char *caption);
/**
* Walk the stack, dumping frames in standard form to ofp. If skip is 0,
* exclude the frames for NS_TraceStack and anything it calls to do the walk.
* If skip is less than 0, include -skip such frames. If skip is positive,
* exclude that many frames leading to the call to NS_TraceStack.
*/
PR_EXTERN(void)
NS_TraceStack(int skip, FILE *ofp);
/**
* Dump a human-readable listing of current allocations and their compressed
* stack backtraces to the file named by pathname. Beware this file may have