bug 516832 - temporary landing to collect tinderbox logs

This commit is contained in:
Igor Bukanov 2010-05-26 13:40:49 +02:00
parent e68891e7c5
commit 8492ffb327
9 changed files with 565 additions and 45 deletions

View File

@ -48,6 +48,7 @@ PROGRAM = jsapi-tests$(BIN_SUFFIX)
CPPSRCS = \
tests.cpp \
selfTest.cpp \
testConservativeGC.cpp \
testContexts.cpp \
testDebugger.cpp \
testDefineGetterSetterNonEnumerable.cpp \

View File

@ -5,6 +5,7 @@ static JSGCCallback oldGCCallback;
static void **checkPointers;
static jsuint checkPointersLength;
static size_t checkPointersStaticStrings;
static JSBool
TestAboutToBeFinalizedCallback(JSContext *cx, JSGCStatus status)
@ -21,7 +22,54 @@ 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);
@ -40,23 +88,22 @@ BEGIN_TEST(testIsAboutToBeFinalized_bug528645)
JSBool ok = JS_GetArrayLength(cx, array, &checkPointersLength);
CHECK(ok);
void **elems = (void **) malloc(sizeof(void *) * checkPointersLength);
CHECK(elems);
checkPointers = (void **) malloc(sizeof(void *) * checkPointersLength);
CHECK(checkPointers);
size_t staticStrings = 0;
checkPointersStaticStrings = 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));
elems[i] = JSVAL_TO_GCTHING(v);
if (JSString::isStatic(elems[i]))
++staticStrings;
checkPointers[i] = JSVAL_TO_GCTHING(v);
if (JSString::isStatic(checkPointers[i]))
++checkPointersStaticStrings;
}
oldGCCallback = JS_SetGCCallback(cx, TestAboutToBeFinalizedCallback);
checkPointers = elems;
JS_GC(cx);
/*
@ -65,25 +112,5 @@ BEGIN_TEST(testIsAboutToBeFinalized_bug528645)
*/
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)

View File

@ -839,11 +839,16 @@ JS_SuspendRequest(JSContext *cx)
{
#ifdef JS_THREADSAFE
jsrefcount saveDepth = cx->requestDepth;
if (saveDepth == 0)
return 0;
while (cx->requestDepth) {
do {
cx->outstandingRequests++; /* compensate for JS_EndRequest */
JS_EndRequest(cx);
}
} while (cx->requestDepth);
JS_THREAD_DATA(cx)->conservativeGC.enable();
return saveDepth;
#else
return 0;
@ -854,11 +859,16 @@ JS_PUBLIC_API(void)
JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth)
{
#ifdef JS_THREADSAFE
JS_ASSERT(!cx->requestDepth);
while (--saveDepth >= 0) {
if (saveDepth == 0)
return;
JS_THREAD_DATA(cx)->conservativeGC.disable();
JS_ASSERT(cx->outstandingRequests != 0);
do {
JS_BeginRequest(cx);
cx->outstandingRequests--; /* compensate for JS_BeginRequest */
}
} while (--saveDepth != 0);
#endif
}

View File

@ -61,6 +61,7 @@
#include "jsiter.h"
#include "jslock.h"
#include "jsmath.h"
#include "jsnativestack.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
@ -71,7 +72,6 @@
#include "jsstaticcheck.h"
#include "jsstr.h"
#include "jstracer.h"
#include "jsnativestack.h"
#include "jscntxtinlines.h"
@ -524,6 +524,7 @@ 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)

View File

@ -1032,6 +1032,8 @@ 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);

View File

@ -809,6 +809,14 @@ 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)
{
@ -866,9 +874,7 @@ js_GetGCThingTraceKind(void *thing)
return JSTRACE_STRING;
JSGCArenaInfo *ainfo = JSGCArenaInfo::fromGCThing(thing);
if (!ainfo->list)
return JSTRACE_DOUBLE;
return GetFinalizableArenaTraceKind(ainfo);
return GetArenaTraceKind(ainfo);
}
JSRuntime *
@ -928,6 +934,405 @@ 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()
{
/* 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 (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
@ -1093,6 +1498,8 @@ 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
@ -2212,7 +2619,6 @@ 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);
@ -2224,8 +2630,8 @@ js_TraceRuntime(JSTracer *trc)
js_TraceRuntimeNumberState(trc);
js_MarkTraps(trc);
iter = NULL;
while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL)
JSContext *iter = NULL;
while (JSContext *acx = js_ContextIterator(rt, JS_TRUE, &iter))
js_TraceContext(trc, acx);
for (ThreadDataIter i(rt); !i.empty(); i.popFront())
@ -2233,6 +2639,14 @@ 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
@ -2983,6 +3397,7 @@ LetOtherGCFinish(JSContext *cx)
* finish before we wait.
*/
JS_ASSERT(rt->gcThread);
JS_THREAD_DATA(cx)->conservativeGC.enable();
/*
* Wait for GC to finish on the other thread, even if requestDebit is 0
@ -2994,6 +3409,7 @@ LetOtherGCFinish(JSContext *cx)
JS_AWAIT_GC_DONE(rt);
} while (rt->gcThread);
JS_THREAD_DATA(cx)->conservativeGC.disable();
cx->thread->gcWaiting = false;
rt->requestCount += requestDebit;
}
@ -3120,6 +3536,10 @@ GCUntilDone(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
METER(rt->gcStats.poke++);
/* Do not scan the current thread when on the shutdown. */
if (rt->state != JSRTS_LANDING)
JS_THREAD_DATA(cx)->conservativeGC.enable();
bool firstRun = true;
do {
rt->gcPoke = false;
@ -3138,6 +3558,9 @@ 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);

View File

@ -42,6 +42,9 @@
/*
* JS Garbage Collector.
*/
#include <setjmp.h>
#include "jstypes.h"
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsdhash.h"
@ -358,10 +361,10 @@ struct JSWeakRoots {
#define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots)))
#ifdef JS_THREADSAFE
namespace js {
#ifdef JS_THREADSAFE
/*
* During the finalization we do not free immediately. Rather we add the
* corresponding pointers to a buffer which we later release on the
@ -404,8 +407,30 @@ class BackgroundSweepTask : public JSBackgroundTask {
virtual void run();
};
}
#endif
#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();
void disable();
};
} /* namespace js */
#define JS_DUMP_CONSERVATIVE_GC_ROOTS 1
extern void
js_FinalizeStringRT(JSRuntime *rt, JSString *str);
@ -417,6 +442,25 @@ 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 {
@ -465,6 +509,8 @@ struct JSGCStats {
JSGCArenaStats arenaStats[FINALIZE_LIMIT];
JSGCArenaStats doubleArenaStats;
JSConservativeGCStats conservative;
};
extern JS_FRIEND_API(void)

View File

@ -5531,7 +5531,7 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)
return ucs4Char;
}
#ifdef DEBUG
#if defined DEBUG || defined JS_DUMP_CONSERVATIVE_GC_ROOTS
JS_FRIEND_API(size_t)
js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp,

View File

@ -204,6 +204,16 @@
# 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