Bug 662646 - GC topcrash diagnostics [needs backout before Aurora merge!] (r=dmandelin)

This commit is contained in:
Bill McCloskey 2011-07-07 17:31:24 -07:00
parent b8f0545e72
commit 2a3d5215a6
10 changed files with 104 additions and 14 deletions

View File

@ -412,6 +412,12 @@ struct JSRuntime {
/* Compartment that is currently involved in per-compartment GC */
JSCompartment *gcCurrentCompartment;
/*
* If this is non-NULL, all marked objects must belong to this compartment.
* This is used to look for compartment bugs.
*/
JSCompartment *gcCheckCompartment;
/*
* We can pack these flags as only the GC thread writes to them. Atomic
* updates to packed bytes are not guaranteed, so stores issued by one

View File

@ -100,6 +100,11 @@ struct CrashRing
char buffer[crash_buffer_size];
};
/* These are the tag values for each entry in the CrashRing. */
enum {
JS_CRASH_TAG_GC = 0x200
};
} /* namespace crash */
} /* namespace js */

View File

@ -268,7 +268,7 @@ js_SaveCrashData(uint64 tag, void *ptr, size_t size)
JS_PUBLIC_API(void)
JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback)
{
#if 0
#if 1
if (!gInitialized) {
gInitialized = true;
(*callback)(&gGCStack, sizeof(gGCStack));

View File

@ -60,6 +60,8 @@
#include "jsapi.h"
#include "jsatom.h"
#include "jscompartment.h"
#include "jscrashreport.h"
#include "jscrashformat.h"
#include "jscntxt.h"
#include "jsversion.h"
#include "jsdbgapi.h"
@ -239,9 +241,7 @@ Arena::finalize(JSContext *cx)
if (!newFreeSpanStart)
newFreeSpanStart = thing;
t->finalize(cx);
#ifdef DEBUG
memset(t, JS_FREE_PATTERN, sizeof(T));
#endif
}
}
}
@ -2662,6 +2662,12 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIMER_P
(*c)->setGCLastBytes((*c)->gcBytes, gckind);
}
struct GCCrashData
{
int isRegen;
int isCompartment;
};
void
js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
{
@ -2683,6 +2689,11 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
RecordNativeStackTopForGC(cx);
GCCrashData crashData;
crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT;
crashData.isCompartment = !!comp;
js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
GCTIMER_BEGIN(rt, comp);
do {
@ -2721,6 +2732,8 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
rt->gcChunkAllocationSinceLastGC = false;
GCTIMER_END(gckind == GC_LAST_CONTEXT);
js_SnapshotGCStack();
}
namespace js {

View File

@ -115,6 +115,12 @@ Mark(JSTracer *trc, T *thing)
JS_ASSERT(thing->arenaHeader()->compartment);
JS_ASSERT(thing->arenaHeader()->compartment->rt == rt);
if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment &&
thing->compartment() != rt->atomsCompartment)
{
JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
}
/*
* Don't mark things outside a compartment if we are in a per-compartment
* GC.
@ -158,6 +164,16 @@ MarkObject(JSTracer *trc, JSObject &obj, const char *name)
Mark(trc, &obj);
}
void
MarkCrossCompartmentObject(JSTracer *trc, JSObject &obj, const char *name)
{
JSRuntime *rt = trc->context->runtime;
if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != obj.compartment())
return;
MarkObject(trc, obj, name);
}
void
MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
const void *arg, size_t index)
@ -351,6 +367,22 @@ MarkValue(JSTracer *trc, const js::Value &v, const char *name)
MarkValueRaw(trc, v);
}
void
MarkCrossCompartmentValue(JSTracer *trc, const js::Value &v, const char *name)
{
if (v.isMarkable()) {
js::gc::Cell *cell = (js::gc::Cell *)v.toGCThing();
unsigned kind = v.gcKind();
if (kind == JSTRACE_STRING && ((JSString *)cell)->isStaticAtom())
return;
JSRuntime *rt = trc->context->runtime;
if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment)
return;
MarkValue(trc, v, name);
}
}
void
MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name)
{
@ -748,6 +780,9 @@ MarkChildren(JSTracer *trc, JSXML *xml)
void
GCMarker::drainMarkStack()
{
JSRuntime *rt = context->runtime;
rt->gcCheckCompartment = rt->gcCurrentCompartment;
while (!isMarkStackEmpty()) {
while (!ropeStack.isEmpty())
ScanRope(this, ropeStack.pop());
@ -772,6 +807,8 @@ GCMarker::drainMarkStack()
markDelayedChildren();
}
}
rt->gcCheckCompartment = NULL;
}
} /* namespace js */

View File

@ -62,6 +62,13 @@ MarkString(JSTracer *trc, JSString *str, const char *name);
void
MarkObject(JSTracer *trc, JSObject &obj, const char *name);
/*
* Mark an object that may be in a different compartment from the compartment
* being GC'd. (Although it won't be marked if it's in the wrong compartment.)
*/
void
MarkCrossCompartmentObject(JSTracer *trc, JSObject &obj, const char *name);
void
MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
const void *arg, size_t index);
@ -102,6 +109,13 @@ MarkValueRaw(JSTracer *trc, const js::Value &v);
void
MarkValue(JSTracer *trc, const js::Value &v, const char *name);
/*
* Mark a value that may be in a different compartment from the compartment
* being GC'd. (Although it won't be marked if it's in the wrong compartment.)
*/
void
MarkCrossCompartmentValue(JSTracer *trc, const js::Value &v, const char *name);
void
MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name);

View File

@ -980,11 +980,11 @@ static void
proxy_TraceObject(JSTracer *trc, JSObject *obj)
{
obj->getProxyHandler()->trace(trc, obj);
MarkValue(trc, obj->getProxyPrivate(), "private");
MarkValue(trc, obj->getProxyExtra(), "extra");
MarkCrossCompartmentValue(trc, obj->getProxyPrivate(), "private");
MarkCrossCompartmentValue(trc, obj->getProxyExtra(), "extra");
if (obj->isFunctionProxy()) {
MarkValue(trc, GetCall(obj), "call");
MarkValue(trc, GetConstruct(obj), "construct");
MarkCrossCompartmentValue(trc, GetCall(obj), "call");
MarkCrossCompartmentValue(trc, GetConstruct(obj), "construct");
}
}
@ -992,8 +992,8 @@ static void
proxy_TraceFunction(JSTracer *trc, JSObject *obj)
{
proxy_TraceObject(trc, obj);
MarkValue(trc, GetCall(obj), "call");
MarkValue(trc, GetConstruct(obj), "construct");
MarkCrossCompartmentValue(trc, GetCall(obj), "call");
MarkCrossCompartmentValue(trc, GetConstruct(obj), "construct");
}
static JSBool

View File

@ -45,6 +45,7 @@
#define jsutil_h___
#include "jstypes.h"
#include "jscrashreport.h"
#include "mozilla/Util.h"
#include <stdlib.h>
#include <string.h>
@ -59,6 +60,8 @@ JS_BEGIN_EXTERN_C
} \
JS_END_MACRO
#define JS_FREE_PATTERN 0xDA
#ifdef DEBUG
#define JS_ASSERT(expr) \
@ -80,8 +83,6 @@ JS_BEGIN_EXTERN_C
# define JS_THREADSAFE_ASSERT(expr) ((void) 0)
# endif
#define JS_FREE_PATTERN 0xDA
#else
#define JS_ASSERT(expr) ((void) 0)
@ -220,6 +221,12 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */
#define JS_OOM_POSSIBLY_FAIL() do {} while(0)
#endif
static JS_INLINE void *js_record_oom(void *p) {
if (!p)
js_SnapshotErrorStack();
return p;
}
/*
* SpiderMonkey code should not be calling these allocation functions directly.
* Instead, all calls should go through JSRuntime, JSContext or OffTheBooks.
@ -227,17 +234,17 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */
*/
static JS_INLINE void* js_malloc(size_t bytes) {
JS_OOM_POSSIBLY_FAIL();
return malloc(bytes);
return js_record_oom(malloc(bytes));
}
static JS_INLINE void* js_calloc(size_t bytes) {
JS_OOM_POSSIBLY_FAIL();
return calloc(bytes, 1);
return js_record_oom(calloc(bytes, 1));
}
static JS_INLINE void* js_realloc(void* p, size_t bytes) {
JS_OOM_POSSIBLY_FAIL();
return realloc(p, bytes);
return js_record_oom(realloc(p, bytes));
}
static JS_INLINE void js_free(void* p) {

View File

@ -768,4 +768,10 @@ JSCrossCompartmentWrapper::defaultValue(JSContext *cx, JSObject *wrapper, JSType
return call.origin->wrap(cx, vp);
}
void
JSCrossCompartmentWrapper::trace(JSTracer *trc, JSObject *wrapper)
{
MarkCrossCompartmentObject(trc, *wrappedObject(wrapper), "wrappedObject");
}
JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0u);

View File

@ -153,6 +153,8 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper {
virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent);
virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, js::Value *vp);
virtual void trace(JSTracer *trc, JSObject *wrapper);
static JSCrossCompartmentWrapper singleton;
};