author | Igor Bukanov <igor@mir2.org> |
Fri, 28 May 2010 14:19:20 +0200 | |
changeset 43205 | 8609e2cdd335eaba901a4009f629d56f51dc24db |
parent 43204 | 1070cd7a9da034a481e85998a3a40a004d618afc |
child 43206 | 36d81cc1a7de6770632b9cbc24ae65ad820c47e9 |
push id | 13641 |
push user | rsayre@mozilla.com |
push date | Sun, 06 Jun 2010 19:08:23 +0000 |
treeherder | mozilla-central@5b3604a3cfbe [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 1.9.3a5pre |
backs out | 1070cd7a9da034a481e85998a3a40a004d618afc |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
|
js/src/jsapi-tests/Makefile.in | file | annotate | diff | comparison | revisions | |
js/src/jsapi-tests/testConservativeGC.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsapi-tests/testIsAboutToBeFinalized.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsapi.cpp | file | annotate | diff | comparison | revisions | |
js/src/jscntxt.cpp | file | annotate | diff | comparison | revisions | |
js/src/jscntxt.h | file | annotate | diff | comparison | revisions | |
js/src/jsgc.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsgc.h | file | annotate | diff | comparison | revisions | |
js/src/jsstr.cpp | file | annotate | diff | comparison | revisions | |
js/src/jstypes.h | file | annotate | diff | comparison | revisions | |
js/src/tests/jstests.py | file | annotate | diff | comparison | revisions |
--- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -43,17 +43,16 @@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk PROGRAM = jsapi-tests$(BIN_SUFFIX) CPPSRCS = \ tests.cpp \ selfTest.cpp \ - testConservativeGC.cpp \ testContexts.cpp \ testDebugger.cpp \ testDefineGetterSetterNonEnumerable.cpp \ testDefineProperty.cpp \ testExtendedEq.cpp \ testIntString.cpp \ testIsAboutToBeFinalized.cpp \ testLookup.cpp \
deleted file mode 100644 --- a/js/src/jsapi-tests/testConservativeGC.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "tests.h" -#include "jsobj.h" -#include "jsstr.h" - -BEGIN_TEST(testConservativeGC) -{ - jsval v1; - EVAL("Math.sqrt(42);", &v1); - CHECK(JSVAL_IS_DOUBLE(v1)); - double numCopy = *JSVAL_TO_DOUBLE(v1); - - jsval v2; - EVAL("({foo: 'bar'});", &v2); - CHECK(JSVAL_IS_OBJECT(v2)); - JSObject objCopy = *JSVAL_TO_OBJECT(v2); - - jsval v3; - EVAL("String(Math.PI);", &v3); - CHECK(JSVAL_IS_STRING(v3)); - JSString strCopy = *JSVAL_TO_STRING(v3); - - jsval tmp; - EVAL("Math.sqrt(41);", &tmp); - CHECK(JSVAL_IS_DOUBLE(tmp)); - jsdouble *num2 = JSVAL_TO_DOUBLE(tmp); - jsdouble num2Copy = *num2; - - EVAL("({foo2: 'bar2'});", &tmp); - CHECK(JSVAL_IS_OBJECT(tmp)); - JSObject *obj2 = JSVAL_TO_OBJECT(tmp); - JSObject obj2Copy = *obj2; - - EVAL("String(Math.sqrt(3));", &tmp); - CHECK(JSVAL_IS_STRING(tmp)); - JSString *str2 = JSVAL_TO_STRING(tmp); - JSString str2Copy = *str2; - - tmp = JSVAL_NULL; - - JS_GC(cx); - - EVAL("var a = [];\n" - "for (var i = 0; i != 10000; ++i) {\n" - "a.push(i + 0.1, [1, 2], String(Math.sqrt(i)));\n" - "}", &tmp); - - JS_GC(cx); - - CHECK(numCopy == *JSVAL_TO_DOUBLE(v1)); - CHECK(!memcmp(&objCopy, JSVAL_TO_OBJECT(v2), sizeof(objCopy))); - CHECK(!memcmp(&strCopy, JSVAL_TO_STRING(v3), sizeof(strCopy))); - - CHECK(num2Copy == *num2); - CHECK(!memcmp(&obj2Copy, obj2, sizeof(obj2Copy))); - CHECK(!memcmp(&str2Copy, str2, sizeof(str2Copy))); - - return true; -} -END_TEST(testConservativeGC)
--- a/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp +++ b/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp @@ -1,81 +1,33 @@ #include "tests.h" #include "jsstr.h" static JSGCCallback oldGCCallback; static void **checkPointers; static jsuint checkPointersLength; -static size_t checkPointersStaticStrings; static JSBool TestAboutToBeFinalizedCallback(JSContext *cx, JSGCStatus status) { if (status == JSGC_MARK_END && checkPointers) { for (jsuint i = 0; i != checkPointersLength; ++i) { void *p = checkPointers[i]; JS_ASSERT(p); if (JS_IsAboutToBeFinalized(cx, p)) checkPointers[i] = NULL; } } return !oldGCCallback || oldGCCallback(cx, status); } -static JS_NEVER_INLINE size_t -NativeFrameCleaner() -{ - char buffer[1 << 16]; - memset(buffer, 0, sizeof buffer); - size_t count = 0; - for (size_t i = 0; i != sizeof buffer; ++i) { - if (buffer[i]) - count++; - } - return count; -} - BEGIN_TEST(testIsAboutToBeFinalized_bug528645) { - /* - * Due to the conservative GC we use separated never-inline function to - * test rooted elements. - */ - createAndTestRooted(); - NativeFrameCleaner(); - - JS_GC(cx); - - /* Everything is unrooted except unit strings. */ - for (jsuint i = 0; i != checkPointersLength; ++i) { - void *p = checkPointers[i]; - if (p) { - CHECK(JSString::isStatic(p)); - CHECK(checkPointersStaticStrings != 0); - --checkPointersStaticStrings; - } - } - CHECK(checkPointersStaticStrings == 0); - - free(checkPointers); - checkPointers = NULL; - JS_SetGCCallback(cx, oldGCCallback); - - return true; -} - -JS_NEVER_INLINE bool createAndTestRooted(); - -END_TEST(testIsAboutToBeFinalized_bug528645) - -JS_NEVER_INLINE bool -cls_testIsAboutToBeFinalized_bug528645::createAndTestRooted() -{ jsvalRoot root(cx); /* * Check various types of GC things against JS_IsAboutToBeFinalized. * Make sure to include unit and numeric strings to the set. */ EVAL("var x = 1.1; " "[x + 0.1, ''+x, 'a', '42', 'something'.substring(1), " @@ -83,34 +35,55 @@ cls_testIsAboutToBeFinalized_bug528645:: root.addr()); JSObject *array = JSVAL_TO_OBJECT(root.value()); JS_ASSERT(JS_IsArrayObject(cx, array)); JSBool ok = JS_GetArrayLength(cx, array, &checkPointersLength); CHECK(ok); - checkPointers = (void **) malloc(sizeof(void *) * checkPointersLength); - CHECK(checkPointers); + void **elems = (void **) malloc(sizeof(void *) * checkPointersLength); + CHECK(elems); - checkPointersStaticStrings = 0; + size_t staticStrings = 0; for (jsuint i = 0; i != checkPointersLength; ++i) { jsval v; ok = JS_GetElement(cx, array, i, &v); CHECK(ok); JS_ASSERT(JSVAL_IS_GCTHING(v)); JS_ASSERT(!JSVAL_IS_NULL(v)); - checkPointers[i] = JSVAL_TO_GCTHING(v); - if (JSString::isStatic(checkPointers[i])) - ++checkPointersStaticStrings; + elems[i] = JSVAL_TO_GCTHING(v); + if (JSString::isStatic(elems[i])) + ++staticStrings; } oldGCCallback = JS_SetGCCallback(cx, TestAboutToBeFinalizedCallback); + checkPointers = elems; JS_GC(cx); /* * All GC things are rooted via the root holding the array containing them * and TestAboutToBeFinalizedCallback must keep them as is. */ for (jsuint i = 0; i != checkPointersLength; ++i) CHECK(checkPointers[i]); + + root = JSVAL_NULL; + JS_GC(cx); + + /* Everything is unrooted except unit strings. */ + for (jsuint i = 0; i != checkPointersLength; ++i) { + void *p = checkPointers[i]; + if (p) { + CHECK(JSString::isStatic(p)); + CHECK(staticStrings != 0); + --staticStrings; + } + } + CHECK(staticStrings == 0); + + checkPointers = NULL; + JS_SetGCCallback(cx, oldGCCallback); + free(elems); + + return true; } - +END_TEST(testIsAboutToBeFinalized_bug528645)
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -837,46 +837,36 @@ JS_YieldRequest(JSContext *cx) #endif } JS_PUBLIC_API(jsrefcount) JS_SuspendRequest(JSContext *cx) { #ifdef JS_THREADSAFE jsrefcount saveDepth = cx->requestDepth; - if (saveDepth == 0) - return 0; - - do { + + while (cx->requestDepth) { cx->outstandingRequests++; /* compensate for JS_EndRequest */ JS_EndRequest(cx); - } while (cx->requestDepth); - - JS_THREAD_DATA(cx)->conservativeGC.enable(); - + } return saveDepth; #else return 0; #endif } JS_PUBLIC_API(void) JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) { #ifdef JS_THREADSAFE - if (saveDepth == 0) - return; - - JS_THREAD_DATA(cx)->conservativeGC.disable(); - - JS_ASSERT(cx->outstandingRequests != 0); - do { + JS_ASSERT(!cx->requestDepth); + while (--saveDepth >= 0) { JS_BeginRequest(cx); cx->outstandingRequests--; /* compensate for JS_BeginRequest */ - } while (--saveDepth != 0); + } #endif } JS_PUBLIC_API(void) JS_TransferRequest(JSContext *cx, JSContext *another) { JS_ASSERT(cx != another); JS_ASSERT(cx->runtime == another->runtime);
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -56,27 +56,27 @@ #include "jsversion.h" #include "jsdbgapi.h" #include "jsexn.h" #include "jsfun.h" #include "jsgc.h" #include "jsiter.h" #include "jslock.h" #include "jsmath.h" -#include "jsnativestack.h" #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jspubtd.h" #include "jsscan.h" #include "jsscope.h" #include "jsscript.h" #include "jsstaticcheck.h" #include "jsstr.h" #include "jstracer.h" +#include "jsnativestack.h" #include "jscntxtinlines.h" #ifdef XP_WIN # include <windows.h> #else # include <unistd.h> # include <sys/mman.h> @@ -519,17 +519,16 @@ void JSThreadData::finish() { #ifdef DEBUG /* All GC-related things must be already removed at this point. */ JS_ASSERT(gcFreeLists.isEmpty()); for (size_t i = 0; i != JS_ARRAY_LENGTH(scriptsToGC); ++i) JS_ASSERT(!scriptsToGC[i]); JS_ASSERT(!localRootStack); - JS_ASSERT(conservativeGC.enableCount == 0); #endif if (dtoaState) js_DestroyDtoaState(dtoaState); js_FinishGSNCache(&gsnCache); propertyCache.~PropertyCache(); #if defined JS_TRACER
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1028,18 +1028,16 @@ struct JSThreadData { } dtoaCache; /* Cached native iterators. */ JSObject *cachedNativeIterators[NATIVE_ITER_CACHE_SIZE]; /* Base address of the native stack for the current thread. */ jsuword *nativeStackBase; - js::ConservativeGCThreadData conservativeGC; - bool init(); void finish(); void mark(JSTracer *trc); void purge(JSContext *cx); void purgeGCFreeLists(); }; #ifdef JS_THREADSAFE
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -805,24 +805,16 @@ GetFinalizableTraceKind(size_t thingKind static inline size_t GetFinalizableArenaTraceKind(JSGCArenaInfo *ainfo) { JS_ASSERT(ainfo->list); return GetFinalizableTraceKind(ainfo->list->thingKind); } static inline size_t -GetArenaTraceKind(JSGCArenaInfo *ainfo) -{ - if (!ainfo->list) - return JSTRACE_DOUBLE; - return GetFinalizableArenaTraceKind(ainfo); -} - -static inline size_t GetFinalizableThingTraceKind(void *thing) { JSGCArenaInfo *ainfo = JSGCArenaInfo::fromGCThing(thing); return GetFinalizableArenaTraceKind(ainfo); } static void InitGCArenaLists(JSRuntime *rt) @@ -869,17 +861,19 @@ js_GetExternalStringGCType(JSString *str JS_FRIEND_API(uint32) js_GetGCThingTraceKind(void *thing) { if (JSString::isStatic(thing)) return JSTRACE_STRING; JSGCArenaInfo *ainfo = JSGCArenaInfo::fromGCThing(thing); - return GetArenaTraceKind(ainfo); + if (!ainfo->list) + return JSTRACE_DOUBLE; + return GetFinalizableArenaTraceKind(ainfo); } JSRuntime * js_GetGCThingRuntime(void *thing) { jsuword chunk = JSGCArena::fromGCThing(thing)->getChunk(); return JSGCChunkInfo::fromChunk(chunk)->runtime; } @@ -929,415 +923,16 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes * (during JS engine start). */ rt->setGCLastBytes(8192); METER(PodZero(&rt->gcStats)); return true; } -namespace js { - -struct GCChunkHasher -{ - typedef jsuword Lookup; - static HashNumber hash(jsuword chunk) { - /* - * Strip zeros for better distribution after multiplying by the golden - * ratio. - */ - JS_ASSERT(!(chunk & GC_CHUNK_MASK)); - return HashNumber(chunk >> GC_CHUNK_SHIFT); - } - static bool match(jsuword k, jsuword l) { - JS_ASSERT(!(k & GC_CHUNK_MASK)); - JS_ASSERT(!(l & GC_CHUNK_MASK)); - return k == l; - } -}; - -class ConservativeGCStackMarker { - public: - ConservativeGCStackMarker(JSTracer *trc); - - ~ConservativeGCStackMarker() { -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - dumpConservativeRoots(); -#endif -#ifdef JS_GCMETER - JSConservativeGCStats *total = &trc->context->runtime->gcStats.conservative; - total->words += stats.words; - total->unique += stats.unique; - total->oddaddress += stats.oddaddress; - total->outside += stats.outside; - total->notchunk += stats.notchunk; - total->notarena += stats.notarena; - total->wrongtag += stats.wrongtag; - total->notlive += stats.notlive; - total->gcthings += stats.gcthings; - total->raw += stats.raw; - total->unmarked += stats.unmarked; -#endif - } - - void markRoots(); - - private: - void markRange(jsuword *begin, jsuword *end); - void markWord(jsuword w); - - JSTracer *trc; - jsuword gcthingAddressStart; - jsuword gcthingAddressSpan; - HashSet<jsuword, GCChunkHasher, SystemAllocPolicy> chunkSet; - HashSet<jsuword, DefaultHasher<jsuword>, SystemAllocPolicy> history; - -#if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) || defined(JS_GCMETER) - JSConservativeGCStats stats; - - public: - static void dumpStats(FILE *fp, JSConservativeGCStats *stats); - -# define CONSERVATIVE_METER(x) ((void) (x)) -# define CONSERVATIVE_METER_IF(condition, x) ((void) ((condition) && (x))) - -#else - -# define CONSERVATIVE_METER(x) ((void) 0) -# define CONSERVATIVE_METER_IF(condition, x) ((void) 0) - -#endif - -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - private: - struct ConservativeRoot { void *thing; uint32 traceKind; }; - Vector<ConservativeRoot, 0, SystemAllocPolicy> conservativeRoots; - const char *dumpFileName; - - void dumpConservativeRoots(); -#endif -}; - -ConservativeGCStackMarker::ConservativeGCStackMarker(JSTracer *trc) - : trc(trc) -{ - /* - * If initializing fails because we are out of memory, stack scanning - * slows down but is otherwise unaffected. - */ - history.init(); - - JSRuntime *rt = trc->context->runtime; - jsuword minchunk = 0, maxchunk = 0; - bool chunkSetOk = chunkSet.init(rt->gcChunks.length()); - for (JSGCChunkInfo **i = rt->gcChunks.begin(); i != rt->gcChunks.end(); ++i) { - jsuword chunk =(*i)->getChunk(); - if (chunkSetOk) { - JS_ASSERT(!chunkSet.has(chunk)); - JS_ALWAYS_TRUE(chunkSet.put(chunk)); - } - - if (minchunk == 0) - minchunk = maxchunk = chunk; - else if (chunk < minchunk) - minchunk = chunk; - else if (chunk > maxchunk) - maxchunk = chunk; - } - - gcthingAddressStart = minchunk; - gcthingAddressSpan = (minchunk != 0) - ? maxchunk + GC_MARK_BITMAP_ARRAY_OFFSET - : 0; - -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - dumpFileName = getenv("JS_DUMP_CONSERVATIVE_GC_ROOTS"); - memset(&stats, 0, sizeof(stats)); -#endif -} - -#if defined(JS_DUMP_CONSERVATIVE_GC_ROOTS) || defined(JS_GCMETER) -/* static */ -void -ConservativeGCStackMarker::dumpStats(FILE *fp, JSConservativeGCStats *stats) -{ -#define ULSTAT(x) ((unsigned long)(stats->x)) - fprintf(fp, "CONSERVATIVE STACK SCANNING:\n"); - fprintf(fp, " number of stack words: %lu\n", ULSTAT(words)); - fprintf(fp, " number of unique words: %lu\n", ULSTAT(unique)); - fprintf(fp, " excluded, low bit set: %lu\n", ULSTAT(oddaddress)); - fprintf(fp, " not withing chunk range: %lu\n", ULSTAT(outside)); - fprintf(fp, " not withing a chunk: %lu\n", ULSTAT(notchunk)); - fprintf(fp, " not withing an arena: %lu\n", ULSTAT(notarena)); - fprintf(fp, " excluded, wrong tag: %lu\n", ULSTAT(wrongtag)); - fprintf(fp, " excluded, not live: %lu\n", ULSTAT(notlive)); - fprintf(fp, " things marked: %lu\n", ULSTAT(gcthings)); - fprintf(fp, " raw pointers marked: %lu\n", ULSTAT(raw)); - fprintf(fp, " conservative roots: %lu\n", ULSTAT(unmarked)); -#undef ULSTAT -} -#endif - -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS -void -ConservativeGCStackMarker::dumpConservativeRoots() -{ - if (!dumpFileName) - return; - - JS_ASSERT(stats.unmarked == conservativeRoots.length()); - - FILE *fp = !strcmp(dumpFileName, "stdout") ? stdout - : !strcmp(dumpFileName, "stderr") ? stderr - : fopen(dumpFileName, "aw"); - if (!fp) { - fprintf(stderr, - "Warning: cannot open %s to dump the conservative roots\n", - dumpFileName); - return; - } - - dumpStats(fp, &stats); - for (ConservativeRoot *i = conservativeRoots.begin(); - i != conservativeRoots.end(); - ++i) { - fprintf(fp, " %p: ", i->thing); - switch (i->traceKind) { - default: - JS_NOT_REACHED("Unknown trace kind"); - - case JSTRACE_OBJECT: { - JSObject *obj = (JSObject *) i->thing; - fprintf(fp, "object %s", obj->getClass()->name); - break; - } - case JSTRACE_STRING: { - JSString *str = (JSString *) i->thing; - char buf[50]; - js_PutEscapedString(buf, sizeof buf, str, '"'); - fprintf(fp, "string %s", buf); - break; - } - case JSTRACE_DOUBLE: { - jsdouble *dp = (jsdouble *) i->thing; - fprintf(fp, "double %e", *dp); - break; - } -# if JS_HAS_XML_SUPPORT - case JSTRACE_XML: { - JSXML *xml = (JSXML *) i->thing; - fprintf(fp, "xml %u", xml->xml_class); - break; - } -# endif - } - fputc('\n', fp); - } - fputc('\n', fp); - - if (fp != stdout && fp != stderr) - fclose(fp); -} -#endif /* JS_DUMP_CONSERVATIVE_GC_ROOTS */ - -void -ConservativeGCStackMarker::markWord(jsuword w) -{ -#define RETURN(x) do { CONSERVATIVE_METER(stats.x++); return; } while (0) - - JSRuntime *rt = trc->context->runtime; - - CONSERVATIVE_METER(stats.unique++); - - /* If this is an odd addresses or a tagged integer, skip it. */ - if (w & 1) - RETURN(oddaddress); - - /* Strip off the tag bits. */ - jsuword tag = w & JSVAL_TAGMASK; - jsuword p = w & ~(JSVAL_TAGMASK); - - /* Also ignore tagged special values (they never contain pointers). */ - if (tag == JSVAL_SPECIAL) - RETURN(wrongtag); - - /* The remaining pointer must be within the heap boundaries. */ - if ((p - gcthingAddressStart) >= gcthingAddressSpan) - RETURN(outside); - - if ((p & GC_CHUNK_MASK) >= GC_MARK_BITMAP_ARRAY_OFFSET) - RETURN(outside); - - jsuword chunk = p & ~GC_CHUNK_MASK; - JSGCChunkInfo *ci; - if (JS_LIKELY(chunkSet.initialized())) { - if (!chunkSet.has(chunk)) - RETURN(notchunk); - ci = JSGCChunkInfo::fromChunk(chunk); - } else { - ci = JSGCChunkInfo::fromChunk(chunk); - for (JSGCChunkInfo **i = rt->gcChunks.begin(); ; ++i) { - if (i == rt->gcChunks.end()) - RETURN(notchunk); - if (*i == ci) - break; - } - } - - size_t arenaIndex = (p & GC_CHUNK_MASK) >> GC_ARENA_SHIFT; - if (JS_TEST_BIT(ci->getFreeArenaBitmap(), arenaIndex)) - RETURN(notarena); - - JSGCArena *a = JSGCArena::fromChunkAndIndex(chunk, arenaIndex); - JSGCArenaInfo *ainfo = a->getInfo(); - - JSGCThing *thing; - if (!ainfo->list) { /* doubles */ - if (tag && tag != JSVAL_DOUBLE) - RETURN(wrongtag); - JS_STATIC_ASSERT(JSVAL_TAGMASK == 7 && (sizeof(double) - 1) == 7); - thing = (JSGCThing *) p; - } else { - if (tag == JSVAL_DOUBLE) - RETURN(wrongtag); - jsuword start = a->toPageStart(); - jsuword offset = p - start; - size_t thingSize = ainfo->list->thingSize; - p = (start + offset - (offset % thingSize)); - thing = (JSGCThing *) p; - - /* Make sure the thing is not on the freelist of the arena. */ - JSGCThing *cursor = ainfo->freeList; - while (cursor) { - /* If the cursor moves past the thing, its not in the freelist. */ - if (thing < cursor) - break; - /* If we find it on the freelist, its dead. */ - if (thing == cursor) - RETURN(notlive); - cursor = cursor->link; - } - } - - CONSERVATIVE_METER(stats.gcthings++); - CONSERVATIVE_METER_IF(!tag, stats.raw++); - - /* - * We have now a valid pointer, that is either raw or tagged properly. - * Since we do not rely on the conservative scanning yet and assume that - * all the roots are precisely reported, any unmarked GC things here mean - * a leak. - */ - if (IS_GC_MARKING_TRACER(trc)) { - if (!js_IsAboutToBeFinalized(thing)) - return; - CONSERVATIVE_METER(stats.unmarked++); - } - - uint32 traceKind = GetArenaTraceKind(ainfo); -#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS - if (IS_GC_MARKING_TRACER(trc) && dumpFileName) { - ConservativeRoot root = {thing, traceKind}; - conservativeRoots.append(root); - } -#endif - JS_SET_TRACING_NAME(trc, "machine stack"); - js_CallGCMarker(trc, thing, traceKind); - -#undef RETURN -} - -void -ConservativeGCStackMarker::markRange(jsuword *begin, jsuword *end) -{ - JS_ASSERT(begin <= end); - if (history.initialized()) { - for (jsuword *i = begin; i != end; ++i) { - CONSERVATIVE_METER(stats.words++); - jsuword p = *i; - if (history.has(p)) - continue; - markWord(p); - - /* - * If adding the address to the hash table fails because we are - * out of memory, stack scanning slows down but is otherwise - * unaffected. - */ - history.put(p); - } - } else { - for (jsuword *i = begin; i != end; ++i) - markWord(*i); - } -} - -void -ConservativeGCStackMarker::markRoots() -{ - /* Do conservative scanning of the stack. */ - for (ThreadDataIter i(trc->context->runtime); !i.empty(); i.popFront()) { - JSThreadData *td = i.threadData(); - ConservativeGCThreadData *ctd = &td->conservativeGC; - if (ctd->enableCount) { -#if JS_STACK_GROWTH_DIRECTION > 0 - JS_ASSERT(td->nativeStackBase <= ctd->nativeStackTop); - markRange(td->nativeStackBase, ctd->nativeStackTop); -#else - JS_ASSERT(td->nativeStackBase >= ctd->nativeStackTop + 1); - markRange(ctd->nativeStackTop + 1, td->nativeStackBase); -#endif - markRange(ctd->registerSnapshot.words, - JS_ARRAY_END(ctd->registerSnapshot.words)); - } - } -} - -/* static */ -JS_NEVER_INLINE void -ConservativeGCThreadData::enable(bool knownStackBoundary) -{ - /* Update the native stack pointer if it points to a bigger stack. */ -#if JS_STACK_GROWTH_DIRECTION > 0 -# define CMP > -#else -# define CMP < -#endif - jsuword dummy; - if (knownStackBoundary || enableCount == 0 || &dummy CMP nativeStackTop) - nativeStackTop = &dummy; -#undef CMP - - /* Update the register snapshot with the latest values. */ -#if defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable: 4611) -#endif - setjmp(registerSnapshot.jmpbuf); -#if defined(_MSC_VER) -# pragma warning(pop) -#endif - - ++enableCount; -} - -JS_NEVER_INLINE void -ConservativeGCThreadData::disable() -{ - JS_ASSERT(enableCount != 0); - --enableCount; -#ifdef DEBUG - if (enableCount == 0) - nativeStackTop = NULL; -#endif -} - -} /* namespace js */ - - #ifdef JS_GCMETER static void UpdateArenaStats(JSGCArenaStats *st, uint32 nlivearenas, uint32 nkilledArenas, uint32 nthings) { size_t narenas; @@ -1493,18 +1088,16 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp) fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree)); fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg)); fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots)); fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose)); fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose)); fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater)); fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater)); - ConservativeGCStackMarker::dumpStats(fp, &rt->gcStats.conservative); - #undef UL #undef ULSTAT #undef PERCENT } #endif #ifdef DEBUG static void @@ -2614,44 +2207,37 @@ js_TraceContext(JSTracer *trc, JSContext } #endif } JS_REQUIRES_STACK void js_TraceRuntime(JSTracer *trc) { JSRuntime *rt = trc->context->runtime; + JSContext *iter, *acx; for (GCRoots::Range r = rt->gcRootsHash.all(); !r.empty(); r.popFront()) gc_root_traversal(r.front(), trc); for (GCLocks::Range r = rt->gcLocksHash.all(); !r.empty(); r.popFront()) gc_lock_traversal(r.front(), trc); js_TraceAtomState(trc); js_TraceRuntimeNumberState(trc); js_MarkTraps(trc); - JSContext *iter = NULL; - while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter)) + iter = NULL; + while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) js_TraceContext(trc, acx); for (ThreadDataIter i(rt); !i.empty(); i.popFront()) i.threadData()->mark(trc); if (rt->gcExtraRootsTraceOp) rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData); - - /* - * For now we use the conservative stack scanner only in the check mode - * and mark conservatively after marking all other roots to detect - * conservative leaks. - */ - if (rt->state != JSRTS_LANDING) - ConservativeGCStackMarker(trc).markRoots(); } void js_TriggerGC(JSContext *cx, JSBool gcLocked) { JSRuntime *rt = cx->runtime; #ifdef JS_THREADSAFE @@ -3413,29 +2999,27 @@ LetOtherGCFinish(JSContext *cx) cx->thread->gcWaiting = true; js_ShareWaitingTitles(cx); /* * Check that we did not release the GC lock above and let the GC to * finish before we wait. */ JS_ASSERT(rt->gcThread); - JS_THREAD_DATA(cx)->conservativeGC.enable(true); /* * Wait for GC to finish on the other thread, even if requestDebit is 0 * and even if GC has not started yet because the gcThread is waiting in * BeginGCSession. This ensures that js_GC never returns without a full GC * cycle happening. */ do { JS_AWAIT_GC_DONE(rt); } while (rt->gcThread); - JS_THREAD_DATA(cx)->conservativeGC.disable(); cx->thread->gcWaiting = false; rt->requestCount += requestDebit; } #endif /* * Start a new GC session assuming no GC is running on this or other threads. @@ -3552,27 +3136,16 @@ GCUntilDone(JSContext *cx, JSGCInvocatio return; } #endif /* JS_THREADSAFE */ BeginGCSession(cx); METER(rt->gcStats.poke++); - /* - * Do not scan the current thread when on the shutdown or when the - * GC is called outside a request. - */ - bool scanGCThreadStack = (rt->state != JSRTS_LANDING) -#ifndef JS_THREADSAFE - && (rt->gcThread->contextsInRequests != 0) -#endif - ; - if (scanGCThreadStack) - JS_THREAD_DATA(cx)->conservativeGC.enable(true); bool firstRun = true; do { rt->gcPoke = false; AutoUnlockGC unlock(rt); if (firstRun) { PreGCCleanup(cx, gckind); TIMESTAMP(startMark); @@ -3581,19 +3154,16 @@ GCUntilDone(JSContext *cx, JSGCInvocatio GC(cx GCTIMER_ARG); // GC again if: // - another thread, not in a request, called js_GC // - js_GC was called recursively // - a finalizer called js_RemoveRoot or js_UnlockGCThingRT. } while (rt->gcPoke); - if (rt->state != JSRTS_LANDING) - JS_THREAD_DATA(cx)->conservativeGC.disable(); - rt->gcRegenShapes = false; rt->setGCLastBytes(rt->gcBytes); EndGCSession(cx); } /* * The gckind flag bit GC_LOCK_HELD indicates a call from js_NewGCThing with
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -37,19 +37,16 @@ * * ***** END LICENSE BLOCK ***** */ #ifndef jsgc_h___ #define jsgc_h___ /* * JS Garbage Collector. */ -#include <setjmp.h> - -#include "jstypes.h" #include "jsprvtd.h" #include "jspubtd.h" #include "jsdhash.h" #include "jsbit.h" #include "jsutil.h" #include "jstask.h" #include "jsvector.h" #include "jsversion.h" @@ -372,19 +369,19 @@ struct JSWeakRoots { /* Root for the result of the most recent js_InternalInvoke call. */ jsval lastInternalResult; void mark(JSTracer *trc); }; #define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots))) -namespace js { +#ifdef JS_THREADSAFE -#ifdef JS_THREADSAFE +namespace js { /* * During the finalization we do not free immediately. Rather we add the * corresponding pointers to a buffer which we later release on the * background thread. * * The buffer is implemented as a vector of 64K arrays of pointers, not as a * simple vector, to avoid realloc calls during the vector growth and to not @@ -418,70 +415,29 @@ class BackgroundSweepTask : public JSBac *freeCursor++ = ptr; else replenishAndFreeLater(ptr); } virtual void run(); }; -#endif /* JS_THREADSAFE */ - -struct ConservativeGCThreadData { - - /* - * The GC scans conservatively between JSThreadData::nativeStackBase and - * nativeStackTop unless the latter is NULL. - */ - jsuword *nativeStackTop; - - union { - jmp_buf jmpbuf; - jsuword words[JS_HOWMANY(sizeof(jmp_buf), sizeof(jsuword))]; - } registerSnapshot; - - size_t enableCount; - - JS_NEVER_INLINE void enable(bool knownStackBoundary = false); - void disable(); -}; - -} /* namespace js */ - -#define JS_DUMP_CONSERVATIVE_GC_ROOTS 1 +} +#endif extern void js_FinalizeStringRT(JSRuntime *rt, JSString *str); #if defined JS_GCMETER const bool JS_WANT_GC_METER_PRINT = true; #elif defined DEBUG # define JS_GCMETER 1 const bool JS_WANT_GC_METER_PRINT = false; #endif -#if defined JS_GCMETER || defined JS_DUMP_CONSERVATIVE_GC_ROOTS - -struct JSConservativeGCStats { - uint32 words; /* number of words on native stacks */ - uint32 unique; /* number of unique words */ - uint32 oddaddress; /* excluded because low bit was set */ - uint32 outside; /* not within chunk min/max address range */ - uint32 notchunk; /* not within a valid chunk */ - uint32 notarena; /* not within non-free arena */ - uint32 wrongtag; /* tagged pointer but wrong type */ - uint32 notlive; /* gcthing is not allocated */ - uint32 gcthings; /* number of live gcthings */ - uint32 raw; /* number of raw pointers marked */ - uint32 unmarked; /* number of unmarked gc things discovered on the - stack */ -}; - -#endif - #ifdef JS_GCMETER struct JSGCArenaStats { uint32 alloc; /* allocation attempts */ uint32 localalloc; /* allocations from local lists */ uint32 retry; /* allocation retries after running the GC */ uint32 fail; /* allocation failures */ uint32 nthings; /* live GC things */ @@ -520,18 +476,16 @@ struct JSGCStats { uint32 maxcloselater; /* max number of close hooks scheduled to run */ uint32 nallarenas; /* number of all allocated arenas */ uint32 maxnallarenas; /* maximum number of all allocated arenas */ uint32 nchunks; /* number of allocated chunks */ uint32 maxnchunks; /* maximum number of allocated chunks */ JSGCArenaStats arenaStats[FINALIZE_LIMIT]; JSGCArenaStats doubleArenaStats; - - JSConservativeGCStats conservative; }; extern JS_FRIEND_API(void) js_DumpGCStats(JSRuntime *rt, FILE *fp); #endif /* JS_GCMETER */ /*
--- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -5526,17 +5526,17 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffe ucs4Char = OVERLONG_UTF8; } else if (ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { ucs4Char = 0xFFFD; } } return ucs4Char; } -#if defined DEBUG || defined JS_DUMP_CONSERVATIVE_GC_ROOTS +#ifdef DEBUG JS_FRIEND_API(size_t) js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp, JSString *str, uint32 quote) { const jschar *chars, *charsEnd; size_t n; const char *escape;
--- a/js/src/jstypes.h +++ b/js/src/jstypes.h @@ -199,26 +199,16 @@ # define JS_ALWAYS_INLINE __forceinline # elif defined __GNUC__ # define JS_ALWAYS_INLINE __attribute__((always_inline)) JS_INLINE # else # define JS_ALWAYS_INLINE JS_INLINE # endif #endif -#ifndef JS_NEVER_INLINE -# if defined _MSC_VER -# define JS_NEVER_INLINE __declspec(noinline) -# elif defined __GNUC__ -# define JS_NEVER_INLINE __attribute__((noinline)) -# else -# define JS_NEVER_INLINE -# endif -#endif - #ifdef NS_STATIC_CHECKING /* * Attributes for static analysis. Functions declared with JS_REQUIRES_STACK * always have a valid cx->fp and can access it freely. Other functions can * access cx->fp only after calling a function that "forces" the stack * (i.e. lazily instantiates it as needed). */ # define JS_REQUIRES_STACK __attribute__((user("JS_REQUIRES_STACK")))
--- a/js/src/tests/jstests.py +++ b/js/src/tests/jstests.py @@ -144,23 +144,16 @@ class ResultsSink: def list(self): for label, paths in sorted(self.groups.items()): if label == '': continue print label for path in paths: print ' %s'%path - if OPTIONS.failure_file: - failure_file = open(OPTIONS.failure_file, 'w') - if not self.all_passed(): - for path in self.groups['REGRESSIONS']: - print >> failure_file, path - failure_file.close() - suffix = '' if self.finished else ' (partial run -- interrupted by user)' if self.all_passed(): print 'PASS' + suffix else: print 'FAIL' + suffix def all_passed(self): return 'REGRESSIONS' not in self.groups @@ -220,18 +213,16 @@ if __name__ == '__main__': op.add_option('-g', '--debug', dest='debug', action='store_true', help='run test in debugger') op.add_option('--valgrind', dest='valgrind', action='store_true', help='run tests in valgrind') op.add_option('--valgrind-args', dest='valgrind_args', help='extra args to pass to valgrind') op.add_option('-c', '--check-manifest', dest='check_manifest', action='store_true', help='check for test files not listed in the manifest') - op.add_option('--failure-file', dest='failure_file', - help='write tests that have not passed to the given file') (OPTIONS, args) = op.parse_args() if len(args) < 1: if not OPTIONS.check_manifest: op.error('missing JS_SHELL argument') JS, args = None, [] else: JS, args = args[0], args[1:] # Convert to an absolute path so we can run JS from a different directory.