From efa68f7304a386b2439111b1b59bfcf89d974f7d Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Sun, 13 Jan 2008 00:31:32 +0000 Subject: [PATCH] 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 --- mozilla/js/src/Makefile.ref | 1 + mozilla/js/src/js.c | 2 +- mozilla/js/src/jsapi.c | 13 +++- mozilla/js/src/jsarena.c | 23 ++---- mozilla/js/src/jscntxt.h | 21 +++++- mozilla/js/src/jsemit.c | 13 +++- mozilla/js/src/jsemit.h | 3 + mozilla/js/src/jsgc.c | 26 ++++++- mozilla/js/src/jshash.c | 31 +++----- mozilla/js/src/jshash.h | 2 +- mozilla/js/src/jsinterp.c | 8 +- mozilla/js/src/jsobj.c | 106 +++++++++++--------------- mozilla/js/src/jsobj.h | 20 +++-- mozilla/js/src/jsparse.c | 13 ++++ mozilla/js/src/jsparse.h | 5 +- mozilla/js/src/jsscope.c | 131 +++++++++++++------------------- mozilla/js/src/jsscript.c | 7 ++ mozilla/js/src/jsstr.c | 40 +++------- mozilla/js/src/jsutil.c | 147 ++++++++++++++++++++++++++++++++++++ mozilla/js/src/jsutil.h | 75 +++++++++++++++--- mozilla/js/src/jsxml.c | 6 +- 21 files changed, 448 insertions(+), 245 deletions(-) diff --git a/mozilla/js/src/Makefile.ref b/mozilla/js/src/Makefile.ref index 91af44d7fdc..e15d13f86f1 100644 --- a/mozilla/js/src/Makefile.ref +++ b/mozilla/js/src/Makefile.ref @@ -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) diff --git a/mozilla/js/src/js.c b/mozilla/js/src/js.c index a5a5f452523..baf1656e701 100644 --- a/mozilla/js/src/js.c +++ b/mozilla/js/src/js.c @@ -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); diff --git a/mozilla/js/src/jsapi.c b/mozilla/js/src/jsapi.c index 74242ccd27b..e7e2f105e7c 100644 --- a/mozilla/js/src/jsapi.c +++ b/mozilla/js/src/jsapi.c @@ -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; diff --git a/mozilla/js/src/jsarena.c b/mozilla/js/src/jsarena.c index fea08011c06..3a15fb03226 100644 --- a/mozilla/js/src/jsarena.c +++ b/mozilla/js/src/jsarena.c @@ -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 #include 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); diff --git a/mozilla/js/src/jscntxt.h b/mozilla/js/src/jscntxt.h index b9031de2135..6f794cfb6ef 100644 --- a/mozilla/js/src/jscntxt.h +++ b/mozilla/js/src/jscntxt.h @@ -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 }; diff --git a/mozilla/js/src/jsemit.c b/mozilla/js/src/jsemit.c index 5795a9c24f3..2a95e04d981 100644 --- a/mozilla/js/src/jsemit.c +++ b/mozilla/js/src/jsemit.c @@ -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) || diff --git a/mozilla/js/src/jsemit.h b/mozilla/js/src/jsemit.h index 71776d8cb76..0ff9f3cd45a 100644 --- a/mozilla/js/src/jsemit.h +++ b/mozilla/js/src/jsemit.h @@ -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), \ diff --git a/mozilla/js/src/jsgc.c b/mozilla/js/src/jsgc.c index 80f644db758..ed7c9d6f18a 100644 --- a/mozilla/js/src/jsgc.c +++ b/mozilla/js/src/jsgc.c @@ -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); /* diff --git a/mozilla/js/src/jshash.c b/mozilla/js/src/jshash.c index 8e25517d5c0..7b9adaedb6c 100644 --- a/mozilla/js/src/jshash.c +++ b/mozilla/js/src/jshash.c @@ -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 +#ifdef JS_HASHMETER #include 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; diff --git a/mozilla/js/src/jshash.h b/mozilla/js/src/jshash.h index 2a125e1907d..0b586f75da0 100644 --- a/mozilla/js/src/jshash.h +++ b/mozilla/js/src/jshash.h @@ -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 */ diff --git a/mozilla/js/src/jsinterp.c b/mozilla/js/src/jsinterp.c index 8d341b4cb08..1dabda97270 100644 --- a/mozilla/js/src/jsinterp.c +++ b/mozilla/js/src/jsinterp.c @@ -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) { diff --git a/mozilla/js/src/jsobj.c b/mozilla/js/src/jsobj.c index 30949c0d180..09962c5bad9 100644 --- a/mozilla/js/src/jsobj.c +++ b/mozilla/js/src/jsobj.c @@ -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 -#include -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 diff --git a/mozilla/js/src/jsobj.h b/mozilla/js/src/jsobj.h index 90832419b56..7445254e64a 100644 --- a/mozilla/js/src/jsobj.h +++ b/mozilla/js/src/jsobj.h @@ -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); diff --git a/mozilla/js/src/jsparse.c b/mozilla/js/src/jsparse.c index 28f7aa474c0..d8611ff6b0a 100644 --- a/mozilla/js/src/jsparse.c +++ b/mozilla/js/src/jsparse.c @@ -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); diff --git a/mozilla/js/src/jsparse.h b/mozilla/js/src/jsparse.h index 4aea3b6ea6e..3fefa9b3e8c 100644 --- a/mozilla/js/src/jsparse.h +++ b/mozilla/js/src/jsparse.h @@ -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 * * * 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 diff --git a/mozilla/js/src/jsscope.c b/mozilla/js/src/jsscope.c index ea62fdad0ca..151c6554735 100644 --- a/mozilla/js/src/jsscope.c +++ b/mozilla/js/src/jsscope.c @@ -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 -#include - -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) diff --git a/mozilla/js/src/jsscript.c b/mozilla/js/src/jsscript.c index acf0af14db2..88bad3ab62c 100644 --- a/mozilla/js/src/jsscript.c +++ b/mozilla/js/src/jsscript.c @@ -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; diff --git a/mozilla/js/src/jsstr.c b/mozilla/js/src/jsstr.c index c570d24cad4..e45ea98467d 100644 --- a/mozilla/js/src/jsstr.c +++ b/mozilla/js/src/jsstr.c @@ -2545,39 +2545,21 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start, #ifdef DEBUG #include -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, diff --git a/mozilla/js/src/jsutil.c b/mozilla/js/src/jsutil.c index 1bb9f939910..eaac15e9c7f 100644 --- a/mozilla/js/src/jsutil.c +++ b/mozilla/js/src/jsutil.c @@ -63,6 +63,153 @@ JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) abort(); } +#ifdef JS_BASIC_STATS + +#include +#include +#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 diff --git a/mozilla/js/src/jsutil.h b/mozilla/js/src/jsutil.h index a779343ee1f..36c48c195f4 100644 --- a/mozilla/js/src/jsutil.h +++ b/mozilla/js/src/jsutil.h @@ -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 + +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; diff --git a/mozilla/js/src/jsxml.c b/mozilla/js/src/jsxml.c index 4ba94740a0e..e7bc328ce21 100644 --- a/mozilla/js/src/jsxml.c +++ b/mozilla/js/src/jsxml.c @@ -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;