Fixes and improvements for property instrumentation (411630, r=igor).
* Menu of -D flags for enabling instrumentation, as a commented-out CFLAGS += setting for convenient testing. * js_FindProperty and js_LookupPropertyWithFlags return indexes into the scope and prototype chains, respectively, to support internal instrumentation, and to pave the way for the return of the property cache (bug 365851).. * jsutil.[ch] JSBasicStats struct and functions for computing mean/sigma/max and auto-scaling histogram. * JS_SCOPE_DEPTH_METER instrumentation for compile- and run-time scope chain length instrumentation: + At compile time, rt->hostenvScopeDepthStats and rt->lexicalScopeDepthStats meter scope chains passed into the compile and evaluate APIs. + At runtime, rt->protoLookupDepthStats and rt->scopeSearchDepthStats track steps along the prototype and scope chains until the sought-after property is found. * JS_ARENAMETER uses JSBasicStats now. * Added rt->liveScopePropsPreSweep to fix the property tree stats code that rotted when property tree sweeping moved to after the finalization phase. * Un-bitrotted some DEBUG_brendan code, turned some off for myself via XXX. * Mac OS X toolchain requires initialized data shared across dynamic library member files, outlaws common data, so initialize extern metering vars. * Old HASHMETER code in jshash.[ch] is now JS_HASHMETER-controlled and based on JSBasicStats. * DEBUG_scopemeters macro renamed JS_DUMP_SCOPE_METERS; uses JSBasicStats now. * Disentangle DEBUG and DUMP_SCOPE_STATS (now JS_DUMP_PROPTREE_STATS) and fix inconsistent thread safety for liveScopeProps (sometimes atomic-incremented, sometimes runtime-locked). * Compiler-modeled maxScopeDepth will propagate via JSScript to runtime for capability-based, interpreter-inlined cache hit qualifier bits, to bypass scope and prototype chain lookup by optimizing for common monomorphic get, set, and call site referencing a prototype property in a well-named object (no shadowing or mutation in 99.9% of the cases). git-svn-id: svn://10.0.0.236/trunk@242973 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
71ef3532f5
commit
efa68f7304
@ -82,6 +82,7 @@ endif
|
||||
#
|
||||
# XCFLAGS may be set in the environment or on the gmake command line
|
||||
#
|
||||
#CFLAGS += -DDEBUG -DDEBUG_brendan -DJS_ARENAMETER -DJS_HASHMETER -DJS_DUMP_PROPTREE_STATS -DJS_DUMP_SCOPE_METERS -DJS_SCOPE_DEPTH_METER -DJS_BASIC_STATS
|
||||
CFLAGS += $(OPTIMIZER) $(OS_CFLAGS) $(DEFINES) $(INCLUDES) $(XCFLAGS)
|
||||
|
||||
LDFLAGS = $(XLDFLAGS)
|
||||
|
||||
@ -1567,7 +1567,7 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0);
|
||||
if (!atom)
|
||||
return JS_FALSE;
|
||||
if (!js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop))
|
||||
if (js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &obj2, &prop) < 0)
|
||||
return JS_FALSE;
|
||||
if (prop) {
|
||||
OBJ_DROP_PROPERTY(cx, obj2, prop);
|
||||
|
||||
@ -3388,7 +3388,7 @@ JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name,
|
||||
return JS_FALSE;
|
||||
ok = OBJ_IS_NATIVE(obj)
|
||||
? js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), flags,
|
||||
&obj2, &prop)
|
||||
&obj2, &prop) >= 0
|
||||
: OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop);
|
||||
if (ok)
|
||||
*vp = LookupResult(cx, obj, obj2, prop);
|
||||
@ -4693,6 +4693,17 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
|
||||
fun = NULL;
|
||||
}
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
if (fun && obj) {
|
||||
JSObject *pobj = obj;
|
||||
uintN depth = 1;
|
||||
|
||||
while ((pobj = OBJ_GET_PARENT(cx, pobj)) != NULL)
|
||||
++depth;
|
||||
JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
LAST_FRAME_CHECKS(cx, fun);
|
||||
return fun;
|
||||
|
||||
@ -419,8 +419,10 @@ JS_FinishArenaPool(JSArenaPool *pool)
|
||||
{
|
||||
JSArenaStats *stats, **statsp;
|
||||
|
||||
if (pool->stats.name)
|
||||
if (pool->stats.name) {
|
||||
free(pool->stats.name);
|
||||
pool->stats.name = NULL;
|
||||
}
|
||||
for (statsp = &arena_stats_list; (stats = *statsp) != 0;
|
||||
statsp = &stats->next) {
|
||||
if (stats == &pool->stats) {
|
||||
@ -483,30 +485,17 @@ JS_ArenaCountRetract(JSArenaPool *pool, char *mark)
|
||||
pool->stats.nfastrels++;
|
||||
}
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_DumpArenaStats(FILE *fp)
|
||||
{
|
||||
JSArenaStats *stats;
|
||||
uint32 nallocs, nbytes;
|
||||
double mean, variance, sigma;
|
||||
double mean, sigma;
|
||||
|
||||
for (stats = arena_stats_list; stats; stats = stats->next) {
|
||||
nallocs = stats->nallocs;
|
||||
if (nallocs != 0) {
|
||||
nbytes = stats->nbytes;
|
||||
mean = (double)nbytes / nallocs;
|
||||
variance = stats->variance * nallocs - nbytes * nbytes;
|
||||
if (variance < 0 || nallocs == 1)
|
||||
variance = 0;
|
||||
else
|
||||
variance /= nallocs * (nallocs - 1);
|
||||
sigma = sqrt(variance);
|
||||
} else {
|
||||
mean = variance = sigma = 0;
|
||||
}
|
||||
mean = JS_MeanAndStdDev(stats->nallocs, stats->nbytes, stats->variance,
|
||||
&sigma);
|
||||
|
||||
fprintf(fp, "\n%s allocation statistics:\n", stats->name);
|
||||
fprintf(fp, " number of arenas: %u\n", stats->narenas);
|
||||
|
||||
@ -383,7 +383,7 @@ struct JSRuntime {
|
||||
/* Literal table maintained by jsatom.c functions. */
|
||||
JSAtomState atomState;
|
||||
|
||||
#ifdef DEBUG
|
||||
#if defined DEBUG || defined JS_DUMP_PROPTREE_STATS
|
||||
/* Function invocation metering. */
|
||||
jsrefcount inlineCalls;
|
||||
jsrefcount nativeCalls;
|
||||
@ -398,8 +398,8 @@ struct JSRuntime {
|
||||
jsrefcount liveScopes;
|
||||
jsrefcount sharedScopes;
|
||||
jsrefcount totalScopes;
|
||||
jsrefcount badUndependStrings;
|
||||
jsrefcount liveScopeProps;
|
||||
jsrefcount liveScopePropsPreSweep;
|
||||
jsrefcount totalScopeProps;
|
||||
jsrefcount livePropTreeNodes;
|
||||
jsrefcount duplicatePropTreeNodes;
|
||||
@ -412,10 +412,27 @@ struct JSRuntime {
|
||||
jsrefcount totalStrings;
|
||||
jsrefcount liveDependentStrings;
|
||||
jsrefcount totalDependentStrings;
|
||||
jsrefcount badUndependStrings;
|
||||
double lengthSum;
|
||||
double lengthSquaredSum;
|
||||
double strdepLengthSum;
|
||||
double strdepLengthSquaredSum;
|
||||
#endif /* DEBUG || JS_DUMP_PROPTREE_STATS */
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
/*
|
||||
* Stats on runtime prototype chain lookups and scope chain depths, i.e.,
|
||||
* counts of objects traversed on a chain until the wanted id is found.
|
||||
*/
|
||||
JSBasicStats protoLookupDepthStats;
|
||||
JSBasicStats scopeSearchDepthStats;
|
||||
|
||||
/*
|
||||
* Stats on compile-time host environment and lexical scope chain lengths
|
||||
* (maximum depths).
|
||||
*/
|
||||
JSBasicStats hostenvScopeDepthStats;
|
||||
JSBasicStats lexicalScopeDepthStats;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@ -871,8 +871,10 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
|
||||
|
||||
if (growth) {
|
||||
#ifdef DEBUG_brendan
|
||||
JSTokenStream *ts = &cg->treeContext.parseContext->tokenStream;
|
||||
|
||||
printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n",
|
||||
cg->filename ? cg->filename : "stdin", cg->firstLine,
|
||||
ts->filename ? ts->filename : "stdin", cg->firstLine,
|
||||
growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps,
|
||||
passes, offset + growth, offset, growth);
|
||||
#endif
|
||||
@ -1499,8 +1501,10 @@ js_PopStatement(JSTreeContext *tc)
|
||||
tc->topStmt = stmt->down;
|
||||
if (STMT_LINKS_SCOPE(stmt)) {
|
||||
tc->topScopeStmt = stmt->downScope;
|
||||
if (stmt->flags & SIF_SCOPE)
|
||||
if (stmt->flags & SIF_SCOPE) {
|
||||
tc->blockChain = STOBJ_GET_PARENT(stmt->u.blockObj);
|
||||
--tc->scopeDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3905,7 +3909,7 @@ EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
|
||||
return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0;
|
||||
}
|
||||
|
||||
#if defined DEBUG_brendan || defined DEBUG_mrbkap
|
||||
#if defined DEBUG_brendanXXX || defined DEBUG_mrbkap
|
||||
static JSBool
|
||||
GettableNoteForNextOp(JSCodeGenerator *cg)
|
||||
{
|
||||
@ -3982,6 +3986,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
cg->codePool, cg->notePool,
|
||||
pn->pn_pos.begin.lineno);
|
||||
cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION);
|
||||
cg2->treeContext.maxScopeDepth = pn->pn_sclen;
|
||||
fun = GET_FUNCTION_PRIVATE(cx, pn->pn_funpob->object);
|
||||
cg2->treeContext.fun = fun;
|
||||
cg2->parent = cg;
|
||||
@ -5952,7 +5957,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
(!(stmt = stmtInfo.down)
|
||||
? !(cg->treeContext.flags & TCF_IN_FUNCTION)
|
||||
: stmt->type == STMT_BLOCK)) {
|
||||
#if defined DEBUG_brendan || defined DEBUG_mrbkap
|
||||
#if defined DEBUG_brendanXXX || defined DEBUG_mrbkap
|
||||
/* There must be no source note already output for the next op. */
|
||||
JS_ASSERT(CG_NOTE_COUNT(cg) == 0 ||
|
||||
CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) ||
|
||||
|
||||
@ -163,6 +163,8 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
uint16 ngvars; /* max. no. of global variables/regexps */
|
||||
uint32 globalUses; /* optimizable global var uses in total */
|
||||
uint32 loopyGlobalUses;/* optimizable global var uses in loops */
|
||||
uint16 scopeDepth; /* current lexical scope chain depth */
|
||||
uint16 maxScopeDepth; /* maximum lexical scope chain depth */
|
||||
JSStmtInfo *topStmt; /* top of statement info stack */
|
||||
JSStmtInfo *topScopeStmt; /* top lexical scope statement */
|
||||
JSObject *blockChain; /* compile time block scope chain (NB: one
|
||||
@ -207,6 +209,7 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
#define TREE_CONTEXT_INIT(tc, pc) \
|
||||
((tc)->flags = (tc)->ngvars = 0, \
|
||||
(tc)->globalUses = (tc)->loopyGlobalUses = 0, \
|
||||
(tc)->scopeDepth = (tc)->maxScopeDepth = 0, \
|
||||
(tc)->topStmt = (tc)->topScopeStmt = NULL, \
|
||||
(tc)->blockChain = NULL, \
|
||||
ATOM_LIST_INIT(&(tc)->decls), \
|
||||
|
||||
@ -1277,7 +1277,7 @@ static struct GCHist {
|
||||
JSGCThing *freeList;
|
||||
} gchist[NGCHIST];
|
||||
|
||||
unsigned gchpos;
|
||||
unsigned gchpos = 0;
|
||||
#endif
|
||||
|
||||
void *
|
||||
@ -2469,7 +2469,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind)
|
||||
/* Reset malloc counter. */
|
||||
rt->gcMallocBytes = 0;
|
||||
|
||||
#ifdef DEBUG_scopemeters
|
||||
#ifdef JS_DUMP_SCOPE_METERS
|
||||
{ extern void js_DumpScopeMeters(JSRuntime *rt);
|
||||
js_DumpScopeMeters(rt);
|
||||
}
|
||||
@ -2553,6 +2553,11 @@ restart:
|
||||
/* Finalize watch points associated with unreachable objects. */
|
||||
js_SweepWatchPoints(cx);
|
||||
|
||||
#ifdef DEBUG
|
||||
/* Save the pre-sweep count of scope-mapped properties. */
|
||||
rt->liveScopePropsPreSweep = rt->liveScopeProps;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Here we need to ensure that JSObject instances are finalized before GC-
|
||||
* allocated JSString and jsdouble instances so object's finalizer can
|
||||
@ -2693,6 +2698,23 @@ restart:
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
{ static FILE *fp;
|
||||
if (!fp)
|
||||
fp = fopen("/tmp/scopedepth.stats", "w");
|
||||
|
||||
if (fp) {
|
||||
JS_DumpBasicStats(&rt->protoLookupDepthStats, "proto-lookup depth", fp);
|
||||
JS_DumpBasicStats(&rt->scopeSearchDepthStats, "scope-search depth", fp);
|
||||
JS_DumpBasicStats(&rt->hostenvScopeDepthStats, "hostenv scope depth", fp);
|
||||
JS_DumpBasicStats(&rt->lexicalScopeDepthStats, "lexical scope depth", fp);
|
||||
|
||||
putc('\n', fp);
|
||||
fflush(fp);
|
||||
}
|
||||
}
|
||||
#endif /* JS_SCOPE_DEPTH_METER */
|
||||
|
||||
JS_LOCK_GC(rt);
|
||||
|
||||
/*
|
||||
|
||||
@ -171,7 +171,7 @@ JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)
|
||||
{
|
||||
JSHashEntry *he, **hep, **hep0;
|
||||
|
||||
#ifdef HASHMETER
|
||||
#ifdef JS_HASHMETER
|
||||
ht->nlookups++;
|
||||
#endif
|
||||
hep = hep0 = BUCKET_HEAD(ht, keyHash);
|
||||
@ -186,7 +186,7 @@ JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)
|
||||
return hep0;
|
||||
}
|
||||
hep = &he->next;
|
||||
#ifdef HASHMETER
|
||||
#ifdef JS_HASHMETER
|
||||
ht->nsteps++;
|
||||
#endif
|
||||
}
|
||||
@ -256,7 +256,7 @@ JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep,
|
||||
if (ht->nentries >= OVERLOADED(n)) {
|
||||
if (!Resize(ht, ht->shift - 1))
|
||||
return NULL;
|
||||
#ifdef HASHMETER
|
||||
#ifdef JS_HASHMETER
|
||||
ht->ngrows++;
|
||||
#endif
|
||||
hep = JS_HashTableRawLookup(ht, keyHash, key);
|
||||
@ -309,7 +309,7 @@ JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)
|
||||
n = NBUCKETS(ht);
|
||||
if (--ht->nentries < UNDERLOADED(n)) {
|
||||
Resize(ht, ht->shift + 1);
|
||||
#ifdef HASHMETER
|
||||
#ifdef JS_HASHMETER
|
||||
ht->nshrinks++;
|
||||
#endif
|
||||
}
|
||||
@ -396,21 +396,20 @@ out:
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
#ifdef HASHMETER
|
||||
#include <math.h>
|
||||
#ifdef JS_HASHMETER
|
||||
#include <stdio.h>
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
|
||||
{
|
||||
double sqsum, mean, variance, sigma;
|
||||
uint32 nchains, nbuckets, nentries;
|
||||
double sqsum, mean, sigma;
|
||||
uint32 nchains, nbuckets;
|
||||
uint32 i, n, maxChain, maxChainLen;
|
||||
JSHashEntry *he;
|
||||
|
||||
sqsum = 0;
|
||||
nchains = 0;
|
||||
maxChainLen = 0;
|
||||
maxChain = maxChainLen = 0;
|
||||
nbuckets = NBUCKETS(ht);
|
||||
for (i = 0; i < nbuckets; i++) {
|
||||
he = ht->buckets[i];
|
||||
@ -425,14 +424,8 @@ JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
|
||||
maxChain = i;
|
||||
}
|
||||
}
|
||||
nentries = ht->nentries;
|
||||
mean = (double)nentries / nchains;
|
||||
variance = nchains * sqsum - nentries * nentries;
|
||||
if (variance < 0 || nchains == 1)
|
||||
variance = 0;
|
||||
else
|
||||
variance /= nchains * (nchains - 1);
|
||||
sigma = sqrt(variance);
|
||||
|
||||
mean = JS_MeanAndStdDev(nchains, ht->nentries, sqsum, &sigma);
|
||||
|
||||
fprintf(fp, "\nHash table statistics:\n");
|
||||
fprintf(fp, " number of lookups: %u\n", ht->nlookups);
|
||||
@ -450,7 +443,7 @@ JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
|
||||
if (dump(he, i, fp) != HT_ENUMERATE_NEXT)
|
||||
break;
|
||||
}
|
||||
#endif /* HASHMETER */
|
||||
#endif /* JS_HASHMETER */
|
||||
|
||||
JS_PUBLIC_API(int)
|
||||
JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
|
||||
@ -458,7 +451,7 @@ JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)
|
||||
int count;
|
||||
|
||||
count = JS_HashTableEnumerateEntries(ht, dump, fp);
|
||||
#ifdef HASHMETER
|
||||
#ifdef JS_HASHMETER
|
||||
JS_HashTableDumpMeter(ht, dump, fp);
|
||||
#endif
|
||||
return count;
|
||||
|
||||
@ -91,7 +91,7 @@ struct JSHashTable {
|
||||
JSHashComparator valueCompare; /* value comparison function */
|
||||
JSHashAllocOps *allocOps; /* allocation operations */
|
||||
void *allocPriv; /* allocation private data */
|
||||
#ifdef HASHMETER
|
||||
#ifdef JS_HASHMETER
|
||||
uint32 nlookups; /* total number of lookups */
|
||||
uint32 nsteps; /* number of hash chains traversed */
|
||||
uint32 ngrows; /* number of table expansions */
|
||||
|
||||
@ -2593,7 +2593,7 @@ interrupt:
|
||||
* call. See bug 372331.
|
||||
*/
|
||||
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop) >= 0;
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (prop)
|
||||
@ -3184,7 +3184,7 @@ interrupt:
|
||||
id = ATOM_TO_JSID(atom);
|
||||
|
||||
SAVE_SP_AND_PC(fp);
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop) >= 0;
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
@ -3262,7 +3262,7 @@ interrupt:
|
||||
id = ATOM_TO_JSID(atom);
|
||||
|
||||
SAVE_SP_AND_PC(fp);
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop) >= 0;
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (!prop)
|
||||
@ -3927,7 +3927,7 @@ interrupt:
|
||||
id = ATOM_TO_JSID(atom);
|
||||
|
||||
SAVE_SP_AND_PC(fp);
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
|
||||
ok = js_FindProperty(cx, id, &obj, &obj2, &prop) >= 0;
|
||||
if (!ok)
|
||||
goto out;
|
||||
if (!prop) {
|
||||
|
||||
@ -2752,8 +2752,8 @@ js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
|
||||
}
|
||||
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
|
||||
&pobj, &prop)) {
|
||||
if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
|
||||
&pobj, &prop) < 0) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
v = JSVAL_VOID;
|
||||
@ -3225,14 +3225,21 @@ JS_FRIEND_API(JSBool)
|
||||
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
|
||||
JSProperty **propp)
|
||||
{
|
||||
return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
|
||||
return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp) >= 0;
|
||||
}
|
||||
|
||||
JSBool
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
# define SCOPE_DEPTH_ACCUM(bs,val) JS_BASIC_STATS_ACCUM(bs,val)
|
||||
#else
|
||||
# define SCOPE_DEPTH_ACCUM(bs,val) /* nothing */
|
||||
#endif
|
||||
|
||||
int
|
||||
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
JSObject **objp, JSProperty **propp)
|
||||
{
|
||||
JSObject *start, *obj2, *proto;
|
||||
int protoIndex;
|
||||
JSScope *scope;
|
||||
JSScopeProperty *sprop;
|
||||
JSClass *clasp;
|
||||
@ -3255,7 +3262,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
JS_COUNT_OPERATION(cx, JSOW_LOOKUP_PROPERTY);
|
||||
/* Search scopes starting with obj and following the prototype link. */
|
||||
start = obj;
|
||||
for (;;) {
|
||||
for (protoIndex = 0; ; protoIndex++) {
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
if (scope->object == obj) {
|
||||
@ -3282,7 +3289,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
*/
|
||||
if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
|
||||
JS_UNLOCK_OBJ(cx, obj);
|
||||
return JS_FALSE;
|
||||
return -1;
|
||||
}
|
||||
if (!entry) {
|
||||
/* Already resolving id in obj -- suppress recursion. */
|
||||
@ -3380,60 +3387,70 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
|
||||
cleanup:
|
||||
js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
|
||||
if (!ok || *propp)
|
||||
return ok;
|
||||
if (!ok)
|
||||
return -1;
|
||||
if (*propp)
|
||||
return protoIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (sprop) {
|
||||
SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
|
||||
JS_ASSERT(OBJ_SCOPE(obj) == scope);
|
||||
*objp = scope->object; /* XXXbe hide in jsscope.[ch] */
|
||||
|
||||
*propp = (JSProperty *) sprop;
|
||||
return JS_TRUE;
|
||||
return protoIndex;
|
||||
}
|
||||
|
||||
proto = LOCKED_OBJ_GET_PROTO(obj);
|
||||
JS_UNLOCK_OBJ(cx, obj);
|
||||
if (!proto)
|
||||
break;
|
||||
if (!OBJ_IS_NATIVE(proto))
|
||||
return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
|
||||
if (!OBJ_IS_NATIVE(proto)) {
|
||||
if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp))
|
||||
return -1;
|
||||
return protoIndex;
|
||||
}
|
||||
obj = proto;
|
||||
}
|
||||
|
||||
out:
|
||||
*objp = NULL;
|
||||
*propp = NULL;
|
||||
return JS_TRUE;
|
||||
return protoIndex;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
JS_FRIEND_API(int)
|
||||
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
|
||||
JSProperty **propp)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSObject *obj, *pobj, *lastobj;
|
||||
int scopeIndex;
|
||||
JSProperty *prop;
|
||||
|
||||
rt = cx->runtime;
|
||||
obj = cx->fp->scopeChain;
|
||||
scopeIndex = 0;
|
||||
do {
|
||||
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
|
||||
return JS_FALSE;
|
||||
return -1;
|
||||
if (prop) {
|
||||
SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex);
|
||||
*objp = obj;
|
||||
*pobjp = pobj;
|
||||
*propp = prop;
|
||||
return JS_TRUE;
|
||||
return scopeIndex;
|
||||
}
|
||||
lastobj = obj;
|
||||
scopeIndex++;
|
||||
} while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
|
||||
|
||||
*objp = lastobj;
|
||||
*pobjp = NULL;
|
||||
*propp = NULL;
|
||||
return JS_TRUE;
|
||||
return scopeIndex;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
@ -3446,7 +3463,7 @@ js_FindIdentifierBase(JSContext *cx, jsid id)
|
||||
* Look for id's property along the "with" statement chain and the
|
||||
* statically-linked scope chain.
|
||||
*/
|
||||
if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
|
||||
if (js_FindProperty(cx, id, &obj, &pobj, &prop) < 0)
|
||||
return NULL;
|
||||
if (prop) {
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
@ -4747,32 +4764,18 @@ js_XDRObject(JSXDRState *xdr, JSObject **objp)
|
||||
|
||||
#endif /* JS_HAS_XDR */
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef JS_DUMP_SCOPE_METERS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
uint32 js_entry_count_max;
|
||||
uint32 js_entry_count_sum;
|
||||
double js_entry_count_sqsum;
|
||||
uint32 js_entry_count_hist[11];
|
||||
JSBasicStats js_entry_count_bs = JS_INIT_STATIC_BASIC_STATS;
|
||||
|
||||
static void
|
||||
MeterEntryCount(uintN count)
|
||||
{
|
||||
if (count) {
|
||||
js_entry_count_sum += count;
|
||||
js_entry_count_sqsum += (double)count * count;
|
||||
if (count > js_entry_count_max)
|
||||
js_entry_count_max = count;
|
||||
}
|
||||
js_entry_count_hist[JS_MIN(count, 10)]++;
|
||||
JS_BASIC_STATS_ACCUM(&js_entry_count_bs, count);
|
||||
}
|
||||
|
||||
#define DEBUG_scopemeters
|
||||
#endif /* DEBUG_brendan */
|
||||
|
||||
#ifdef DEBUG_scopemeters
|
||||
void
|
||||
js_DumpScopeMeters(JSRuntime *rt)
|
||||
{
|
||||
@ -4781,36 +4784,17 @@ js_DumpScopeMeters(JSRuntime *rt)
|
||||
logfp = fopen("/tmp/scope.stats", "a");
|
||||
|
||||
{
|
||||
double mean = 0., var = 0., sigma = 0.;
|
||||
double nscopes = rt->liveScopes;
|
||||
double nentrys = js_entry_count_sum;
|
||||
if (nscopes > 0 && nentrys >= 0) {
|
||||
mean = nentrys / nscopes;
|
||||
var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
|
||||
if (var < 0.0 || nscopes <= 1)
|
||||
var = 0.0;
|
||||
else
|
||||
var /= nscopes * (nscopes - 1);
|
||||
double mean, sigma;
|
||||
|
||||
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
|
||||
sigma = (var != 0.) ? sqrt(var) : 0.;
|
||||
}
|
||||
mean = JS_MeanAndStdDevBS(&js_entry_count_bs, &sigma);
|
||||
|
||||
fprintf(logfp,
|
||||
"scopes %g entries %g mean %g sigma %g max %u",
|
||||
nscopes, nentrys, mean, sigma, js_entry_count_max);
|
||||
fprintf(logfp, "scopes %u entries %g mean %g sigma %g max %u",
|
||||
js_entry_count_bs.num, js_entry_count_bs.sum, mean, sigma,
|
||||
js_entry_count_bs.max);
|
||||
}
|
||||
|
||||
fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
|
||||
js_entry_count_hist[0], js_entry_count_hist[1],
|
||||
js_entry_count_hist[2], js_entry_count_hist[3],
|
||||
js_entry_count_hist[4], js_entry_count_hist[5],
|
||||
js_entry_count_hist[6], js_entry_count_hist[7],
|
||||
js_entry_count_hist[8], js_entry_count_hist[9],
|
||||
js_entry_count_hist[10]);
|
||||
js_entry_count_sum = js_entry_count_max = 0;
|
||||
js_entry_count_sqsum = 0;
|
||||
memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
|
||||
JS_DumpHistogram(&js_entry_count_bs, logfp);
|
||||
JS_BASIC_STATS_INIT(&js_entry_count_bs);
|
||||
fflush(logfp);
|
||||
}
|
||||
#endif
|
||||
@ -4887,7 +4871,7 @@ js_TraceObject(JSTracer *trc, JSObject *obj)
|
||||
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
scope = OBJ_SCOPE(obj);
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef JS_DUMP_SCOPE_METERS
|
||||
if (scope->object == obj)
|
||||
MeterEntryCount(scope->entryCount);
|
||||
#endif
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=80:
|
||||
* vim: set ts=8 sw=4 et tw=78:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
@ -480,10 +480,10 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
uintN flags, intN shortid, JSProperty **propp);
|
||||
|
||||
/*
|
||||
* Unlike js_DefineProperty, propp must be non-null. On success, and if id was
|
||||
* Unlike js_DefineProperty, propp must be non-null. On success, and if id was
|
||||
* found, return true with *objp non-null and locked, and with a held property
|
||||
* stored in *propp. If successful but id was not found, return true with both
|
||||
* *objp and *propp null. Therefore all callers who receive a non-null *propp
|
||||
* stored in *propp. If successful but id was not found, return true with both
|
||||
* *objp and *propp null. Therefore all callers who receive a non-null *propp
|
||||
* must later call OBJ_DROP_PROPERTY(cx, *objp, *propp).
|
||||
*/
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
@ -491,13 +491,19 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
|
||||
JSProperty **propp);
|
||||
|
||||
/*
|
||||
* Specialized subroutine that allows caller to preset JSRESOLVE_* flags.
|
||||
* Specialized subroutine that allows caller to preset JSRESOLVE_* flags and
|
||||
* returns the index along the prototype chain in which *propp was found, or
|
||||
* the last index if not found, or -1 on error.
|
||||
*/
|
||||
extern JSBool
|
||||
extern int
|
||||
js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
JSObject **objp, JSProperty **propp);
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
/*
|
||||
* Return the index along the scope chain in which id was found, or the last
|
||||
* index if not found, or -1 on error.
|
||||
*/
|
||||
extern JS_FRIEND_API(int)
|
||||
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
|
||||
JSProperty **propp);
|
||||
|
||||
|
||||
@ -662,6 +662,16 @@ js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals,
|
||||
#endif
|
||||
script = js_NewScriptFromCG(cx, &cg);
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
if (script) {
|
||||
JSObject *pobj = obj;
|
||||
uintN depth = 1;
|
||||
while ((pobj = OBJ_GET_PARENT(cx, pobj)) != NULL)
|
||||
++depth;
|
||||
JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
js_FinishCodeGenerator(cx, &cg);
|
||||
JS_FinishArenaPool(&codePool);
|
||||
@ -1402,6 +1412,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
pn->pn_op = op;
|
||||
pn->pn_body = body;
|
||||
pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS);
|
||||
pn->pn_sclen = funtc.maxScopeDepth;
|
||||
TREE_CONTEXT_FINISH(&funtc);
|
||||
return result;
|
||||
}
|
||||
@ -3258,6 +3269,8 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
stmt->type == STMT_FINALLY);
|
||||
stmt->downScope = tc->topScopeStmt;
|
||||
tc->topScopeStmt = stmt;
|
||||
if (++tc->scopeDepth > tc->maxScopeDepth)
|
||||
tc->maxScopeDepth = tc->scopeDepth;
|
||||
} else {
|
||||
JS_ASSERT(stmt->type == STMT_CATCH);
|
||||
JS_ASSERT(stmt->downScope);
|
||||
|
||||
@ -68,6 +68,7 @@ JS_BEGIN_EXTERN_C
|
||||
* pn_body: TOK_LC node for function body statements
|
||||
* pn_flags: TCF_FUN_* flags (see jsemit.h) collected
|
||||
* while parsing the function's body
|
||||
* pn_sclen: maximum lexical scope chain length
|
||||
*
|
||||
* <Statements>
|
||||
* TOK_LC list pn_head: list of pn_count statements
|
||||
@ -279,7 +280,8 @@ struct JSParseNode {
|
||||
struct { /* TOK_FUNCTION node */
|
||||
JSParsedObjectBox *funpob; /* function object */
|
||||
JSParseNode *body; /* TOK_LC list of statements */
|
||||
uint32 flags; /* accumulated tree context flags */
|
||||
uint16 flags; /* accumulated tree context flags */
|
||||
uint16 sclen; /* maximum scope chain length */
|
||||
} func;
|
||||
struct { /* list of next-linked nodes */
|
||||
JSParseNode *head; /* first node in list */
|
||||
@ -328,6 +330,7 @@ struct JSParseNode {
|
||||
#define pn_funpob pn_u.func.funpob
|
||||
#define pn_body pn_u.func.body
|
||||
#define pn_flags pn_u.func.flags
|
||||
#define pn_sclen pn_u.func.sclen
|
||||
#define pn_head pn_u.list.head
|
||||
#define pn_tail pn_u.list.tail
|
||||
#define pn_count pn_u.list.count
|
||||
|
||||
@ -177,6 +177,13 @@ extern void
|
||||
js_unlog_scope(JSScope *scope);
|
||||
#endif
|
||||
|
||||
#if defined DEBUG || defined JS_DUMP_PROPTREE_STATS
|
||||
# include "jsprf.h"
|
||||
# define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr)
|
||||
#else
|
||||
# define LIVE_SCOPE_METER(cx,expr) /* nothing */
|
||||
#endif
|
||||
|
||||
void
|
||||
js_DestroyScope(JSContext *cx, JSScope *scope)
|
||||
{
|
||||
@ -193,15 +200,12 @@ js_DestroyScope(JSContext *cx, JSScope *scope)
|
||||
if (scope->table)
|
||||
JS_free(cx, scope->table);
|
||||
|
||||
#ifdef DEBUG
|
||||
JS_LOCK_RUNTIME_VOID(cx->runtime,
|
||||
cx->runtime->liveScopeProps -= scope->entryCount);
|
||||
#endif
|
||||
LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= scope->entryCount);
|
||||
JS_RUNTIME_UNMETER(cx->runtime, liveScopes);
|
||||
JS_free(cx, scope);
|
||||
}
|
||||
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
#ifdef JS_DUMP_PROPTREE_STATS
|
||||
typedef struct JSScopeStats {
|
||||
jsrefcount searches;
|
||||
jsrefcount hits;
|
||||
@ -222,7 +226,7 @@ typedef struct JSScopeStats {
|
||||
jsrefcount shrinks;
|
||||
} JSScopeStats;
|
||||
|
||||
JS_FRIEND_DATA(JSScopeStats) js_scope_stats;
|
||||
JS_FRIEND_DATA(JSScopeStats) js_scope_stats = {0};
|
||||
|
||||
# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x)
|
||||
#else
|
||||
@ -1271,10 +1275,12 @@ js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,
|
||||
scope->entryCount++;
|
||||
scope->lastProp = sprop;
|
||||
CHECK_ANCESTOR_LINE(scope, JS_FALSE);
|
||||
#ifdef DEBUG
|
||||
if (!overwriting) {
|
||||
JS_RUNTIME_METER(cx->runtime, liveScopeProps);
|
||||
LIVE_SCOPE_METER(cx, ++cx->runtime->liveScopeProps);
|
||||
JS_RUNTIME_METER(cx->runtime, totalScopeProps);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If we reach the hashing threshold, try to allocate scope->table.
|
||||
@ -1396,7 +1402,7 @@ js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,
|
||||
child.attrs, child.flags, child.shortid);
|
||||
}
|
||||
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
#ifdef JS_DUMP_PROPTREE_STATS
|
||||
if (!newsprop)
|
||||
METER(changeFailures);
|
||||
#endif
|
||||
@ -1451,7 +1457,7 @@ js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id)
|
||||
*spp = NULL;
|
||||
}
|
||||
scope->entryCount--;
|
||||
JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps);
|
||||
LIVE_SCOPE_METER(cx, --cx->runtime->liveScopeProps);
|
||||
|
||||
/* Update scope->lastProp directly, or set its deferred update flag. */
|
||||
if (sprop == SCOPE_LAST_PROP(scope)) {
|
||||
@ -1480,10 +1486,7 @@ void
|
||||
js_ClearScope(JSContext *cx, JSScope *scope)
|
||||
{
|
||||
CHECK_ANCESTOR_LINE(scope, JS_TRUE);
|
||||
#ifdef DEBUG
|
||||
JS_LOCK_RUNTIME_VOID(cx->runtime,
|
||||
cx->runtime->liveScopeProps -= scope->entryCount);
|
||||
#endif
|
||||
LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= scope->entryCount);
|
||||
|
||||
if (scope->table)
|
||||
free(scope->table);
|
||||
@ -1501,10 +1504,6 @@ js_TraceId(JSTracer *trc, jsid id)
|
||||
JS_CALL_VALUE_TRACER(trc, v, "id");
|
||||
}
|
||||
|
||||
#if defined DEBUG || defined DUMP_SCOPE_STATS
|
||||
# include "jsprf.h"
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
PrintPropertyGetterOrSetter(JSTracer *trc, char *buf, size_t bufsize)
|
||||
@ -1558,30 +1557,19 @@ js_TraceScopeProperty(JSTracer *trc, JSScopeProperty *sprop)
|
||||
#endif /* JS_HAS_GETTER_SETTER */
|
||||
}
|
||||
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
#ifdef JS_DUMP_PROPTREE_STATS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
uint32 js_nkids_max;
|
||||
uint32 js_nkids_sum;
|
||||
double js_nkids_sqsum;
|
||||
uint32 js_nkids_hist[11];
|
||||
|
||||
static void
|
||||
MeterKidCount(uintN nkids)
|
||||
MeterKidCount(JSBasicStats *bs, uintN nkids)
|
||||
{
|
||||
if (nkids) {
|
||||
js_nkids_sum += nkids;
|
||||
js_nkids_sqsum += (double)nkids * nkids;
|
||||
if (nkids > js_nkids_max)
|
||||
js_nkids_max = nkids;
|
||||
}
|
||||
js_nkids_hist[JS_MIN(nkids, 10)]++;
|
||||
JS_BASIC_STATS_ACCUM(bs, nkids);
|
||||
bs->hist[JS_MIN(nkids, 10)]++;
|
||||
}
|
||||
|
||||
static void
|
||||
MeterPropertyTree(JSScopeProperty *node)
|
||||
MeterPropertyTree(JSBasicStats *bs, JSScopeProperty *node)
|
||||
{
|
||||
uintN i, nkids;
|
||||
JSScopeProperty *kids, *kid;
|
||||
@ -1596,17 +1584,17 @@ MeterPropertyTree(JSScopeProperty *node)
|
||||
kid = chunk->kids[i];
|
||||
if (!kid)
|
||||
break;
|
||||
MeterPropertyTree(kid);
|
||||
MeterPropertyTree(bs, kid);
|
||||
nkids++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MeterPropertyTree(kids);
|
||||
MeterPropertyTree(bs, kids);
|
||||
nkids = 1;
|
||||
}
|
||||
}
|
||||
|
||||
MeterKidCount(nkids);
|
||||
MeterKidCount(bs, nkids);
|
||||
}
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSDHashOperator)
|
||||
@ -1614,8 +1602,9 @@ js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number,
|
||||
void *arg)
|
||||
{
|
||||
JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr;
|
||||
JSBasicStats *bs = (JSBasicStats *)arg;
|
||||
|
||||
MeterPropertyTree(entry->child);
|
||||
MeterPropertyTree(bs, entry->child);
|
||||
return JS_DHASH_NEXT;
|
||||
}
|
||||
|
||||
@ -1636,7 +1625,7 @@ DumpSubtree(JSContext *cx, JSScopeProperty *sprop, int level, FILE *fp)
|
||||
if (JSID_IS_ATOM(sprop->id)) {
|
||||
str = JSVAL_TO_STRING(v);
|
||||
} else {
|
||||
JSASSERT(JSID_IS_OBJECT(sprop->id));
|
||||
JS_ASSERT(JSID_IS_OBJECT(sprop->id));
|
||||
str = js_ValueToString(cx, v);
|
||||
fputs("object ", fp);
|
||||
}
|
||||
@ -1646,10 +1635,9 @@ DumpSubtree(JSContext *cx, JSScopeProperty *sprop, int level, FILE *fp)
|
||||
js_FileEscapedString(fp, str, '"');
|
||||
}
|
||||
|
||||
fprintf(fp, " g/s %p/%p slot %lu attrs %x flags %x shortid %d\n",
|
||||
(void *) sprop->getter, (void *) sprop->setter,
|
||||
(unsigned long) sprop->slot, sprop->attrs, sprop->flags,
|
||||
sprop->shortid);
|
||||
fprintf(fp, " g/s %p/%p slot %u attrs %x flags %x shortid %d\n",
|
||||
(void *) sprop->getter, (void *) sprop->setter, sprop->slot,
|
||||
sprop->attrs, sprop->flags, sprop->shortid);
|
||||
kids = sprop->kids;
|
||||
if (kids) {
|
||||
++level;
|
||||
@ -1671,60 +1659,43 @@ DumpSubtree(JSContext *cx, JSScopeProperty *sprop, int level, FILE *fp)
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DUMP_SCOPE_STATS */
|
||||
#endif /* JS_DUMP_PROPTREE_STATS */
|
||||
|
||||
void
|
||||
js_SweepScopeProperties(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSRuntime *rt = cx->runtime;
|
||||
JSArena **ap, *a;
|
||||
JSScopeProperty *limit, *sprop, *parent, *kids, *kid;
|
||||
uintN liveCount;
|
||||
PropTreeKidsChunk *chunk, *nextChunk, *freeChunk;
|
||||
uintN i;
|
||||
|
||||
rt = cx->runtime;
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
#ifdef JS_DUMP_PROPTREE_STATS
|
||||
JSBasicStats bs;
|
||||
uint32 livePropCapacity = 0, totalLiveCount = 0;
|
||||
static FILE *logfp;
|
||||
if (!logfp)
|
||||
logfp = fopen("/tmp/proptree.stats", "a");
|
||||
logfp = fopen("/tmp/proptree.stats", "w");
|
||||
|
||||
MeterKidCount(rt->propertyTreeHash.entryCount);
|
||||
JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL);
|
||||
JS_BASIC_STATS_INIT(&bs);
|
||||
MeterKidCount(&bs, rt->propertyTreeHash.entryCount);
|
||||
JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, &bs);
|
||||
|
||||
{
|
||||
double mean = 0.0, var = 0.0, sigma = 0.0;
|
||||
double nodesum = rt->livePropTreeNodes;
|
||||
double kidsum = js_nkids_sum;
|
||||
if (nodesum > 0 && kidsum >= 0) {
|
||||
mean = kidsum / nodesum;
|
||||
var = nodesum * js_nkids_sqsum - kidsum * kidsum;
|
||||
if (var < 0.0 || nodesum <= 1)
|
||||
var = 0.0;
|
||||
else
|
||||
var /= nodesum * (nodesum - 1);
|
||||
double props, nodes, mean, sigma;
|
||||
|
||||
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
|
||||
sigma = (var != 0.0) ? sqrt(var) : 0.0;
|
||||
}
|
||||
props = rt->liveScopePropsPreSweep;
|
||||
nodes = rt->livePropTreeNodes;
|
||||
JS_ASSERT(nodes == bs.sum);
|
||||
mean = JS_MeanAndStdDevBS(&bs, &sigma);
|
||||
|
||||
fprintf(logfp,
|
||||
"props %u nodes %g beta %g meankids %g sigma %g max %u",
|
||||
rt->liveScopeProps, nodesum, nodesum / rt->liveScopeProps,
|
||||
mean, sigma, js_nkids_max);
|
||||
"props %g nodes %g beta %g meankids %g sigma %g max %u\n",
|
||||
props, nodes, nodes / props, mean, sigma, bs.max);
|
||||
}
|
||||
|
||||
fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u",
|
||||
js_nkids_hist[0], js_nkids_hist[1],
|
||||
js_nkids_hist[2], js_nkids_hist[3],
|
||||
js_nkids_hist[4], js_nkids_hist[5],
|
||||
js_nkids_hist[6], js_nkids_hist[7],
|
||||
js_nkids_hist[8], js_nkids_hist[9],
|
||||
js_nkids_hist[10]);
|
||||
js_nkids_sum = js_nkids_max = 0;
|
||||
js_nkids_sqsum = 0;
|
||||
memset(js_nkids_hist, 0, sizeof js_nkids_hist);
|
||||
JS_DumpHistogram(&bs, logfp);
|
||||
#endif
|
||||
|
||||
ap = &rt->propertyArenaPool.first.next;
|
||||
@ -1842,7 +1813,7 @@ js_SweepScopeProperties(JSContext *cx)
|
||||
FREENODE_REMOVE(sprop);
|
||||
JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap);
|
||||
} else {
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
#ifdef JS_DUMP_PROPTREE_STATS
|
||||
livePropCapacity += limit - (JSScopeProperty *) a->base;
|
||||
totalLiveCount += liveCount;
|
||||
#endif
|
||||
@ -1850,9 +1821,11 @@ js_SweepScopeProperties(JSContext *cx)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DUMP_SCOPE_STATS
|
||||
fprintf(logfp, " arenautil %g%%\n",
|
||||
(totalLiveCount * 100.0) / livePropCapacity);
|
||||
#ifdef JS_DUMP_PROPTREE_STATS
|
||||
fprintf(logfp, "arenautil %g%%\n",
|
||||
(totalLiveCount && livePropCapacity)
|
||||
? (totalLiveCount * 100.0) / livePropCapacity
|
||||
: 0.0);
|
||||
|
||||
#define RATE(f1, f2) (((double)js_scope_stats.f1 / js_scope_stats.f2) * 100.0)
|
||||
|
||||
|
||||
@ -1451,8 +1451,15 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
fun->u.i.script = script;
|
||||
if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
if (fun->flags & JSFUN_HEAVYWEIGHT)
|
||||
++cg->treeContext.maxScopeDepth;
|
||||
}
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
JS_BASIC_STATS_ACCUM(&cx->runtime->lexicalScopeDepthStats,
|
||||
cg->treeContext.maxScopeDepth);
|
||||
#endif
|
||||
|
||||
/* Tell the debugger about this compiled script. */
|
||||
js_CallNewScriptHook(cx, script, fun);
|
||||
return script;
|
||||
|
||||
@ -2545,39 +2545,21 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start,
|
||||
#ifdef DEBUG
|
||||
#include <math.h>
|
||||
|
||||
void printJSStringStats(JSRuntime *rt) {
|
||||
double mean = 0., var = 0., sigma = 0.;
|
||||
jsrefcount count = rt->totalStrings;
|
||||
if (count > 0 && rt->lengthSum >= 0) {
|
||||
mean = rt->lengthSum / count;
|
||||
var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum;
|
||||
if (var < 0.0 || count <= 1)
|
||||
var = 0.0;
|
||||
else
|
||||
var /= count * (count - 1);
|
||||
void printJSStringStats(JSRuntime *rt)
|
||||
{
|
||||
double mean, sigma;
|
||||
|
||||
mean = JS_MeanAndStdDev(rt->totalStrings, rt->lengthSum,
|
||||
rt->lengthSquaredSum, &sigma);
|
||||
|
||||
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
|
||||
sigma = (var != 0.) ? sqrt(var) : 0.;
|
||||
}
|
||||
fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n",
|
||||
(unsigned long)count, mean, sigma);
|
||||
(unsigned long)rt->totalStrings, mean, sigma);
|
||||
|
||||
mean = var = sigma = 0.;
|
||||
count = rt->totalDependentStrings;
|
||||
if (count > 0 && rt->strdepLengthSum >= 0) {
|
||||
mean = rt->strdepLengthSum / count;
|
||||
var = count * rt->strdepLengthSquaredSum
|
||||
- rt->strdepLengthSum * rt->strdepLengthSum;
|
||||
if (var < 0.0 || count <= 1)
|
||||
var = 0.0;
|
||||
else
|
||||
var /= count * (count - 1);
|
||||
mean = JS_MeanAndStdDev(rt->totalDependentStrings, rt->strdepLengthSum,
|
||||
rt->strdepLengthSquaredSum, &sigma);
|
||||
|
||||
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
|
||||
sigma = (var != 0.) ? sqrt(var) : 0.;
|
||||
}
|
||||
fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n",
|
||||
(unsigned long)count, mean, sigma);
|
||||
(unsigned long)rt->totalDependentStrings, mean, sigma);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -4924,7 +4906,7 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)
|
||||
return ucs4Char;
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(DUMP_SCOPE_STATS)
|
||||
#if defined DEBUG || defined JS_DUMP_PROPTREE_STATS
|
||||
|
||||
JS_FRIEND_API(size_t)
|
||||
js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp,
|
||||
|
||||
@ -63,6 +63,153 @@ JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln)
|
||||
abort();
|
||||
}
|
||||
|
||||
#ifdef JS_BASIC_STATS
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "jscompat.h"
|
||||
#include "jsbit.h"
|
||||
|
||||
/*
|
||||
* Histogram bins count occurrences of values <= the bin label, as follows:
|
||||
*
|
||||
* linear: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or more
|
||||
* 2**x: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 or more
|
||||
* 10**x: 0, 1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9 or more
|
||||
*
|
||||
* We wish to count occurrences of 0 and 1 values separately, always.
|
||||
*/
|
||||
static uint32
|
||||
BinToVal(uintN logscale, uintN bin)
|
||||
{
|
||||
JS_ASSERT(bin <= 10);
|
||||
if (bin <= 1 || logscale == 0)
|
||||
return bin;
|
||||
--bin;
|
||||
if (logscale == 2)
|
||||
return JS_BIT(bin);
|
||||
JS_ASSERT(logscale == 10);
|
||||
return (uint32) pow(10, bin);
|
||||
}
|
||||
|
||||
static uintN
|
||||
ValToBin(uintN logscale, uint32 val)
|
||||
{
|
||||
uintN bin;
|
||||
|
||||
if (val <= 1)
|
||||
return val;
|
||||
bin = (logscale == 10)
|
||||
? (uintN) ceil(log10(val))
|
||||
: (logscale == 2)
|
||||
? (uintN) JS_CeilingLog2(val)
|
||||
: val;
|
||||
return JS_MIN(bin, 10);
|
||||
}
|
||||
|
||||
void
|
||||
JS_BasicStatsAccum(JSBasicStats *bs, uint32 val)
|
||||
{
|
||||
uintN oldscale, newscale, bin;
|
||||
double mean;
|
||||
|
||||
++bs->num;
|
||||
if (bs->max < val)
|
||||
bs->max = val;
|
||||
bs->sum += val;
|
||||
bs->sqsum += (double)val * val;
|
||||
|
||||
oldscale = bs->logscale;
|
||||
if (oldscale != 10) {
|
||||
mean = bs->sum / bs->num;
|
||||
if (bs->max > 16 && mean > 8) {
|
||||
newscale = (bs->max > 1e6 && mean > 1000) ? 10 : 2;
|
||||
if (newscale != oldscale) {
|
||||
uint32 newhist[11], newbin;
|
||||
|
||||
memset(newhist, 0, sizeof newhist);
|
||||
for (bin = 0; bin <= 10; bin++) {
|
||||
newbin = ValToBin(newscale, BinToVal(oldscale, bin));
|
||||
newhist[newbin] += bs->hist[bin];
|
||||
}
|
||||
memcpy(bs->hist, newhist, sizeof bs->hist);
|
||||
bs->logscale = newscale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bin = ValToBin(bs->logscale, val);
|
||||
++bs->hist[bin];
|
||||
}
|
||||
|
||||
double
|
||||
JS_MeanAndStdDev(uint32 num, double sum, double sqsum, double *sigma)
|
||||
{
|
||||
double var;
|
||||
|
||||
if (num == 0 || sum == 0) {
|
||||
*sigma = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
var = num * sqsum - sum * sum;
|
||||
if (var < 0 || num == 1)
|
||||
var = 0;
|
||||
else
|
||||
var /= (double)num * (num - 1);
|
||||
|
||||
/* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
|
||||
*sigma = (var != 0) ? sqrt(var) : 0;
|
||||
return sum / num;
|
||||
}
|
||||
|
||||
void
|
||||
JS_DumpBasicStats(JSBasicStats *bs, const char *title, FILE *fp)
|
||||
{
|
||||
double mean, sigma;
|
||||
|
||||
mean = JS_MeanAndStdDevBS(bs, &sigma);
|
||||
fprintf(fp, "\nmean %s %g, std. deviation %g, max %lu\n",
|
||||
title, mean, sigma, (unsigned long) bs->max);
|
||||
JS_DumpHistogram(bs, fp);
|
||||
}
|
||||
|
||||
void
|
||||
JS_DumpHistogram(JSBasicStats *bs, FILE *fp)
|
||||
{
|
||||
uintN bin;
|
||||
uint32 cnt, max, prev, val, i;
|
||||
double sum, mean;
|
||||
|
||||
for (bin = 0, max = 0, sum = 0; bin <= 10; bin++) {
|
||||
cnt = bs->hist[bin];
|
||||
if (max < cnt)
|
||||
max = cnt;
|
||||
sum += cnt;
|
||||
}
|
||||
mean = sum / cnt;
|
||||
for (bin = 0, prev = 0; bin <= 10; bin++, prev = val) {
|
||||
val = BinToVal(bs->logscale, bin);
|
||||
cnt = bs->hist[bin];
|
||||
if (prev + 1 >= val)
|
||||
fprintf(fp, " [%6u]", val);
|
||||
else
|
||||
fprintf(fp, "[%6u, %6u]", prev + 1, val);
|
||||
fprintf(fp, "%s %8u ", (bin == 10) ? "+" : ":", cnt);
|
||||
if (cnt != 0) {
|
||||
if (max > 1e6 && mean > 1e3)
|
||||
cnt = ceil(log10(cnt));
|
||||
else if (max > 16 && mean > 8)
|
||||
cnt = JS_CeilingLog2(cnt);
|
||||
for (i = 0; i < cnt; i++)
|
||||
putc('*', fp);
|
||||
}
|
||||
putc('\n', fp);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* JS_BASIC_STATS */
|
||||
|
||||
#if defined DEBUG_notme && defined XP_UNIX
|
||||
|
||||
#define __USE_GNU 1
|
||||
|
||||
@ -50,20 +50,21 @@ JS_BEGIN_EXTERN_C
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_Assert(const char *s, const char *file, JSIntn ln);
|
||||
#define JS_ASSERT(_expr) \
|
||||
((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__))
|
||||
|
||||
#define JS_ASSERT_IF(_cond, _expr) \
|
||||
(!(_cond) || (_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__))
|
||||
#define JS_ASSERT(expr) \
|
||||
((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
|
||||
|
||||
#define JS_NOT_REACHED(_reasonStr) \
|
||||
JS_Assert(_reasonStr,__FILE__,__LINE__)
|
||||
#define JS_ASSERT_IF(cond, expr) \
|
||||
((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
|
||||
|
||||
#define JS_NOT_REACHED(reason) \
|
||||
JS_Assert(reason, __FILE__, __LINE__)
|
||||
|
||||
#else
|
||||
|
||||
#define JS_ASSERT(expr) ((void) 0)
|
||||
#define JS_ASSERT(expr) ((void) 0)
|
||||
#define JS_ASSERT_IF(cond,expr) ((void) 0)
|
||||
#define JS_NOT_REACHED(reasonStr)
|
||||
#define JS_NOT_REACHED(reason)
|
||||
|
||||
#endif /* defined(DEBUG) */
|
||||
|
||||
@ -76,12 +77,62 @@ JS_Assert(const char *s, const char *file, JSIntn ln);
|
||||
extern void js_static_assert(int arg[(condition) ? 1 : -1])
|
||||
|
||||
/*
|
||||
** Abort the process in a non-graceful manner. This will cause a core file,
|
||||
** call to the debugger or other moral equivalent as well as causing the
|
||||
** entire process to stop.
|
||||
*/
|
||||
* Abort the process in a non-graceful manner. This will cause a core file,
|
||||
* call to the debugger or other moral equivalent as well as causing the
|
||||
* entire process to stop.
|
||||
*/
|
||||
extern JS_PUBLIC_API(void) JS_Abort(void);
|
||||
|
||||
#if 0
|
||||
# define JS_BASIC_STATS 1
|
||||
# define JS_SCOPE_DEPTH_METER 1
|
||||
#endif
|
||||
|
||||
#if defined DEBUG && !defined JS_BASIC_STATS
|
||||
# define JS_BASIC_STATS 1
|
||||
#endif
|
||||
|
||||
#ifdef JS_BASIC_STATS
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct JSBasicStats {
|
||||
uint32 num;
|
||||
uint32 max;
|
||||
double sum;
|
||||
double sqsum;
|
||||
uint32 logscale; /* logarithmic scale: 0 (linear), 2, 10 */
|
||||
uint32 hist[11];
|
||||
} JSBasicStats;
|
||||
|
||||
#define JS_INIT_STATIC_BASIC_STATS {0,0,0,0,0,{0,0,0,0,0,0,0,0,0,0,0}}
|
||||
#define JS_BASIC_STATS_INIT(bs) memset((bs), 0, sizeof(JSBasicStats))
|
||||
|
||||
#define JS_BASIC_STATS_ACCUM(bs,val) \
|
||||
JS_BasicStatsAccum(bs, val)
|
||||
|
||||
#define JS_MeanAndStdDevBS(bs,sigma) \
|
||||
JS_MeanAndStdDev((bs)->num, (bs)->sum, (bs)->sqsum, sigma)
|
||||
|
||||
extern void
|
||||
JS_BasicStatsAccum(JSBasicStats *bs, uint32 val);
|
||||
|
||||
extern double
|
||||
JS_MeanAndStdDev(uint32 num, double sum, double sqsum, double *sigma);
|
||||
|
||||
extern void
|
||||
JS_DumpBasicStats(JSBasicStats *bs, const char *title, FILE *fp);
|
||||
|
||||
extern void
|
||||
JS_DumpHistogram(JSBasicStats *bs, FILE *fp);
|
||||
|
||||
#else
|
||||
|
||||
#define JS_BASIC_STATS_ACCUM(bs,val) /* nothing */
|
||||
|
||||
#endif /* JS_BASIC_STATS */
|
||||
|
||||
|
||||
#ifdef XP_UNIX
|
||||
|
||||
typedef struct JSCallsite JSCallsite;
|
||||
|
||||
@ -86,11 +86,7 @@
|
||||
* - JS_TypeOfValue sure could use a cleaner interface to "types"
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#define METERING 1
|
||||
#endif
|
||||
|
||||
#ifdef METERING
|
||||
#ifdef XML_METERING
|
||||
static struct {
|
||||
jsrefcount qname;
|
||||
jsrefcount qnameobj;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user