зеркало из https://github.com/mozilla/pjs.git
Backed out changeset 1070cd7a9da0
This commit is contained in:
Родитель
3221c69bae
Коммит
918b1787fc
|
@ -48,7 +48,6 @@ PROGRAM = jsapi-tests$(BIN_SUFFIX)
|
|||
CPPSRCS = \
|
||||
tests.cpp \
|
||||
selfTest.cpp \
|
||||
testConservativeGC.cpp \
|
||||
testContexts.cpp \
|
||||
testDebugger.cpp \
|
||||
testDefineGetterSetterNonEnumerable.cpp \
|
||||
|
|
|
@ -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)
|
|
@ -5,7 +5,6 @@ static JSGCCallback oldGCCallback;
|
|||
|
||||
static void **checkPointers;
|
||||
static jsuint checkPointersLength;
|
||||
static size_t checkPointersStaticStrings;
|
||||
|
||||
static JSBool
|
||||
TestAboutToBeFinalizedCallback(JSContext *cx, JSGCStatus status)
|
||||
|
@ -22,54 +21,7 @@ TestAboutToBeFinalizedCallback(JSContext *cx, JSGCStatus status)
|
|||
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);
|
||||
|
||||
|
@ -88,22 +40,23 @@ cls_testIsAboutToBeFinalized_bug528645::createAndTestRooted()
|
|||
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);
|
||||
|
||||
/*
|
||||
|
@ -112,5 +65,25 @@ cls_testIsAboutToBeFinalized_bug528645::createAndTestRooted()
|
|||
*/
|
||||
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)
|
||||
|
|
|
@ -842,16 +842,11 @@ 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;
|
||||
|
@ -862,16 +857,11 @@ 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
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
#include "jsiter.h"
|
||||
#include "jslock.h"
|
||||
#include "jsmath.h"
|
||||
#include "jsnativestack.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
|
@ -72,6 +71,7 @@
|
|||
#include "jsstaticcheck.h"
|
||||
#include "jsstr.h"
|
||||
#include "jstracer.h"
|
||||
#include "jsnativestack.h"
|
||||
|
||||
#include "jscntxtinlines.h"
|
||||
|
||||
|
@ -524,7 +524,6 @@ JSThreadData::finish()
|
|||
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)
|
||||
|
|
|
@ -1033,8 +1033,6 @@ struct JSThreadData {
|
|||
/* Base address of the native stack for the current thread. */
|
||||
jsuword *nativeStackBase;
|
||||
|
||||
js::ConservativeGCThreadData conservativeGC;
|
||||
|
||||
bool init();
|
||||
void finish();
|
||||
void mark(JSTracer *trc);
|
||||
|
|
442
js/src/jsgc.cpp
442
js/src/jsgc.cpp
|
@ -809,14 +809,6 @@ GetFinalizableArenaTraceKind(JSGCArenaInfo *ainfo)
|
|||
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)
|
||||
{
|
||||
|
@ -874,7 +866,9 @@ js_GetGCThingTraceKind(void *thing)
|
|||
return JSTRACE_STRING;
|
||||
|
||||
JSGCArenaInfo *ainfo = JSGCArenaInfo::fromGCThing(thing);
|
||||
return GetArenaTraceKind(ainfo);
|
||||
if (!ainfo->list)
|
||||
return JSTRACE_DOUBLE;
|
||||
return GetFinalizableArenaTraceKind(ainfo);
|
||||
}
|
||||
|
||||
JSRuntime *
|
||||
|
@ -934,405 +928,6 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes)
|
|||
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
|
||||
|
@ -1498,8 +1093,6 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp)
|
|||
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
|
||||
|
@ -2619,6 +2212,7 @@ 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);
|
||||
|
@ -2630,8 +2224,8 @@ js_TraceRuntime(JSTracer *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())
|
||||
|
@ -2639,14 +2233,6 @@ js_TraceRuntime(JSTracer *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
|
||||
|
@ -3418,7 +3004,6 @@ LetOtherGCFinish(JSContext *cx)
|
|||
* 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
|
||||
|
@ -3430,7 +3015,6 @@ LetOtherGCFinish(JSContext *cx)
|
|||
JS_AWAIT_GC_DONE(rt);
|
||||
} while (rt->gcThread);
|
||||
|
||||
JS_THREAD_DATA(cx)->conservativeGC.disable();
|
||||
cx->thread->gcWaiting = false;
|
||||
rt->requestCount += requestDebit;
|
||||
}
|
||||
|
@ -3557,17 +3141,6 @@ GCUntilDone(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
|
|||
|
||||
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;
|
||||
|
@ -3586,9 +3159,6 @@ GCUntilDone(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
|
|||
// - 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);
|
||||
|
||||
|
|
|
@ -42,9 +42,6 @@
|
|||
/*
|
||||
* JS Garbage Collector.
|
||||
*/
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "jstypes.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jsdhash.h"
|
||||
|
@ -377,10 +374,10 @@ struct JSWeakRoots {
|
|||
|
||||
#define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots)))
|
||||
|
||||
namespace js {
|
||||
|
||||
#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
|
||||
|
@ -423,30 +420,8 @@ class BackgroundSweepTask : public JSBackgroundTask {
|
|||
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);
|
||||
|
@ -458,25 +433,6 @@ const bool JS_WANT_GC_METER_PRINT = true;
|
|||
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 {
|
||||
|
@ -525,8 +481,6 @@ struct JSGCStats {
|
|||
|
||||
JSGCArenaStats arenaStats[FINALIZE_LIMIT];
|
||||
JSGCArenaStats doubleArenaStats;
|
||||
|
||||
JSConservativeGCStats conservative;
|
||||
};
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
|
|
|
@ -5531,7 +5531,7 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)
|
|||
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,
|
||||
|
|
|
@ -204,16 +204,6 @@
|
|||
# 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
|
||||
|
|
|
@ -149,13 +149,6 @@ class ResultsSink:
|
|||
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
|
||||
|
@ -225,8 +218,6 @@ if __name__ == '__main__':
|
|||
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:
|
||||
|
|
Загрузка…
Ссылка в новой задаче