Bug 677773 - Allow JS crash diagnostics to be disabled; simplify them (r=dmandelin,ted)

This commit is contained in:
Bill McCloskey 2011-08-12 06:57:45 -07:00
parent 5a6712b2b5
commit d80b33e02c
14 changed files with 125 additions and 159 deletions

View File

@ -7593,6 +7593,18 @@ if test -n "$JS_GC_ZEAL"; then
AC_DEFINE(JS_GC_ZEAL)
fi
dnl ========================================================
dnl JS opt-mode assertions and minidump instrumentation
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(js-diagnostics,
[ --enable-js-diagnostics
Enable JS diagnostic assertions and breakpad data],
JS_CRASH_DIAGNOSTICS=1,
JS_CRASH_DIAGNOSTICS= )
if test -n "$JS_CRASH_DIAGNOSTICS"; then
AC_DEFINE(JS_CRASH_DIAGNOSTICS)
fi
dnl ======================================================
dnl = Enable compiling with ccache
dnl ======================================================

View File

@ -5022,6 +5022,18 @@ if test -n "$JS_GC_ZEAL"; then
AC_DEFINE(JS_GC_ZEAL)
fi
dnl ========================================================
dnl JS opt-mode assertions and minidump instrumentation
dnl ========================================================
MOZ_ARG_ENABLE_BOOL(js-diagnostics,
[ --enable-js-diagnostics
Enable JS diagnostic assertions and breakpad data],
JS_CRASH_DIAGNOSTICS=1,
JS_CRASH_DIAGNOSTICS= )
if test -n "$JS_CRASH_DIAGNOSTICS"; then
AC_DEFINE(JS_CRASH_DIAGNOSTICS)
fi
dnl ======================================================
dnl = Enable compiling with ccache
dnl ======================================================

View File

@ -562,8 +562,6 @@ JSCompartment::purge(JSContext *cx)
#endif
#ifdef JS_METHODJIT
js::CheckCompartmentScripts(this);
for (JSScript *script = (JSScript *)scripts.next;
&script->links != &scripts;
script = (JSScript *)script->links.next) {

View File

@ -245,37 +245,37 @@ static Stack gGCStack(JS_CRASH_STACK_GC);
static Stack gErrorStack(JS_CRASH_STACK_ERROR);
static Ring gRingBuffer(JS_CRASH_RING);
void
SnapshotGCStack()
{
if (gInitialized)
gGCStack.snapshot();
}
void
SnapshotErrorStack()
{
if (gInitialized)
gErrorStack.snapshot();
}
void
SaveCrashData(uint64 tag, void *ptr, size_t size)
{
if (gInitialized)
gRingBuffer.push(tag, ptr, size);
}
} /* namespace crash */
} /* namespace js */
using namespace js;
using namespace js::crash;
JS_FRIEND_API(void)
js_SnapshotGCStack()
{
if (gInitialized)
gGCStack.snapshot();
}
JS_FRIEND_API(void)
js_SnapshotErrorStack()
{
if (gInitialized)
gErrorStack.snapshot();
}
JS_FRIEND_API(void)
js_SaveCrashData(uint64 tag, void *ptr, size_t size)
{
if (gInitialized)
gRingBuffer.push(tag, ptr, size);
}
JS_PUBLIC_API(void)
JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback)
{
#if 1
#ifdef JS_CRASH_DIAGNOSTICS
if (!gInitialized) {
gInitialized = true;
(*callback)(&gGCStack, sizeof(gGCStack));

View File

@ -42,18 +42,46 @@
#define jscrashreport_h___
#include "jstypes.h"
#include "jsutil.h"
JS_BEGIN_EXTERN_C
namespace js {
namespace crash {
JS_FRIEND_API(void)
js_SnapshotGCStack();
void
SnapshotGCStack();
JS_FRIEND_API(void)
js_SnapshotErrorStack();
void
SnapshotErrorStack();
JS_FRIEND_API(void)
js_SaveCrashData(uint64 tag, void *ptr, size_t size);
void
SaveCrashData(uint64 tag, void *ptr, size_t size);
JS_END_EXTERN_C
template<size_t size, char marker>
class StackBuffer {
private:
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
volatile char buffer[size + 4];
public:
StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) {
JS_GUARD_OBJECT_NOTIFIER_INIT;
buffer[0] = marker;
buffer[1] = '[';
for (size_t i = 0; i < size; i++) {
if (data)
buffer[i + 2] = ((char *)data)[i];
else
buffer[i + 2] = 0;
}
buffer[size - 2] = ']';
buffer[size - 1] = marker;
}
};
} /* namespace crash */
} /* namespace js */
#endif /* jscrashreport_h___ */

View File

@ -241,7 +241,7 @@ Arena::finalize(JSContext *cx)
if (!newFreeSpanStart)
newFreeSpanStart = thing;
t->finalize(cx);
memset(t, JS_FREE_PATTERN, sizeof(T));
JS_POISON(t, JS_FREE_PATTERN, sizeof(T));
}
}
}
@ -2399,9 +2399,6 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes);
}
#endif
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
js::CheckCompartmentScripts(*c);
}
#ifdef JS_THREADSAFE
@ -2693,7 +2690,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
GCCrashData crashData;
crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT;
crashData.isCompartment = !!comp;
js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
GCTIMER_BEGIN(rt, comp);
@ -2744,7 +2741,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
rt->gcChunkAllocationSinceLastGC = false;
GCTIMER_END(gckind == GC_LAST_CONTEXT);
js_SnapshotGCStack();
crash::SnapshotGCStack();
}
namespace js {

View File

@ -100,13 +100,6 @@ PushMarkStack(GCMarker *gcmarker, JSShortString *thing);
static inline void
PushMarkStack(GCMarker *gcmarker, JSString *thing);
static void
volatile_memcpy(volatile unsigned char *dst, const void *src, size_t n)
{
for (size_t i = 0; i < n; i++)
dst[i] = ((char *)src)[i];
}
template<typename T>
void
Mark(JSTracer *trc, T *thing)
@ -122,15 +115,9 @@ Mark(JSTracer *trc, T *thing)
JS_ASSERT(thing->compartment());
JS_ASSERT(thing->compartment()->rt == rt);
if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment &&
thing->compartment() != rt->atomsCompartment)
{
volatile unsigned char dbg[sizeof(T) + 2];
dbg[0] = 0xab;
dbg[1] = 0xcd;
volatile_memcpy(dbg + 2, thing, sizeof(T));
JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
}
JS_OPT_ASSERT_IF(rt->gcCheckCompartment,
thing->compartment() == rt->gcCheckCompartment ||
thing->compartment() == rt->atomsCompartment);
/*
* Don't mark things outside a compartment if we are in a per-compartment

View File

@ -3417,24 +3417,6 @@ CopySlots(JSContext *cx, JSObject *from, JSObject *to)
return true;
}
static void
CheckProxy(JSObject *obj)
{
if (!obj->isProxy())
return;
JSProxyHandler *handler = obj->getProxyHandler();
if (handler->isCrossCompartment())
return;
Value priv = obj->getProxyPrivate();
if (!priv.isObject())
return;
if (priv.toObject().compartment() != obj->compartment())
JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__);
}
JSObject *
JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent)
{
@ -3472,8 +3454,6 @@ JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent)
return NULL;
}
CheckProxy(clone);
return clone;
}
@ -3586,11 +3566,6 @@ JSObject::swap(JSContext *cx, JSObject *other)
TradeGuts(this, otherClone);
TradeGuts(other, thisClone);
CheckProxy(this);
CheckProxy(other);
CheckProxy(thisClone);
CheckProxy(otherClone);
return true;
}

View File

@ -1184,11 +1184,6 @@ NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObje
else
clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass;
if (!handler->isCrossCompartment() && priv.isObject()) {
if (priv.toObject().compartment() != cx->compartment)
JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__);
}
JSObject *obj = NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
if (!obj || !obj->ensureInstanceReservedSlots(cx, 0))
return NULL;

View File

@ -92,10 +92,6 @@ class JS_FRIEND_API(JSProxyHandler) {
return false;
}
virtual bool isCrossCompartment() {
return false;
}
inline void *family() {
return mFamily;
}

View File

@ -46,6 +46,7 @@
#include "jstypes.h"
#include "jsstdint.h"
#include "jsutil.h"
#include "jscrashreport.h"
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
@ -287,44 +288,26 @@ Bindings::trace(JSTracer *trc)
} /* namespace js */
static void
volatile_memcpy(volatile char *dst, void *src, size_t n)
{
for (size_t i = 0; i < n; i++)
dst[i] = ((char *)src)[i];
}
static void
CheckScript(JSScript *script, JSScript *prev)
{
volatile char dbg1[sizeof(JSScript)], dbg2[sizeof(JSScript)];
#ifdef JS_CRASH_DIAGNOSTICS
if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) {
volatile_memcpy(dbg1, script, sizeof(JSScript));
if (prev)
volatile_memcpy(dbg2, prev, sizeof(JSScript));
crash::StackBuffer<sizeof(JSScript), 0x87> buf1(script);
crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
JS_OPT_ASSERT(false);
}
JS_OPT_ASSERT(script->cookie1 == JS_SCRIPT_COOKIE && script->cookie2 == JS_SCRIPT_COOKIE);
#endif
}
static void
CheckScriptOwner(JSScript *script, JSObject *owner)
{
if (script->ownerObject != owner) {
volatile char scriptData[sizeof(JSScript)];
volatile char owner1Data[sizeof(JSObject)], owner2Data[sizeof(JSObject)];
volatile char savedOwner[sizeof(JSObject *)];
volatile_memcpy(scriptData, script, sizeof(JSScript));
volatile_memcpy(savedOwner, &owner, sizeof(JSObject *));
if (script->ownerObject != JS_NEW_SCRIPT && script->ownerObject != JS_CACHED_SCRIPT)
volatile_memcpy(owner1Data, script->ownerObject, sizeof(JSObject));
if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
volatile_memcpy(owner2Data, owner, sizeof(JSObject));
}
#ifdef JS_CRASH_DIAGNOSTICS
JS_OPT_ASSERT(script->ownerObject == owner);
if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
JS_OPT_ASSERT(script->compartment == owner->compartment());
#endif
}
#if JS_HAS_XDR
@ -981,8 +964,10 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
return NULL;
PodZero(script);
#ifdef JS_CRASH_DIAGNOSTICS
script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE;
script->ownerObject = JS_NEW_SCRIPT;
#endif
script->length = length;
script->version = version;
new (&script->bindings) Bindings(cx, emptyCallShape);
@ -1286,8 +1271,10 @@ JSScript::totalSize()
void
JSScript::setOwnerObject(JSObject *owner)
{
#ifdef JS_CRASH_DIAGNOSTICS
CheckScriptOwner(this, JS_NEW_SCRIPT);
ownerObject = owner;
#endif
}
/*
@ -1328,22 +1315,6 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
JS_ClearScriptTraps(cx, script);
}
namespace js {
void
CheckCompartmentScripts(JSCompartment *comp)
{
JSScript *prev = NULL;
for (JSScript *script = (JSScript *)comp->scripts.next;
&script->links != &comp->scripts;
prev = script, script = (JSScript *)script->links.next)
{
CheckScript(script, prev);
}
}
} /* namespace js */
static void
DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
{
@ -1408,7 +1379,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
if (script->sourceMap)
cx->free_(script->sourceMap);
memset(script, 0xdb, script->totalSize());
JS_POISON(script, 0xdb, sizeof(JSScript));
*(uint32 *)script = caller;
cx->free_(script);
}
@ -1443,9 +1414,10 @@ js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner)
if (owner)
CheckScriptOwner(script, owner);
#ifdef JS_CRASH_DIAGNOSTICS
JSRuntime *rt = trc->context->runtime;
if (rt->gcCheckCompartment && script->compartment != rt->gcCheckCompartment)
JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment);
#endif
JSAtomMap *map = &script->atomMap;
MarkAtomRange(trc, map->length, map->vector, "atomMap");

View File

@ -449,7 +449,9 @@ struct JSScript {
jsbytecode *code; /* bytecodes and their immediate operands */
uint32 length; /* length of code vector */
#ifdef JS_CRASH_DIAGNOSTICS
uint32 cookie1;
#endif
private:
uint16 version; /* JS version under which script was compiled */
@ -507,7 +509,9 @@ struct JSScript {
JSPrincipals *principals;/* principals for this script */
jschar *sourceMap; /* source map file or null */
#ifdef JS_CRASH_DIAGNOSTICS
JSObject *ownerObject;
#endif
void setOwnerObject(JSObject *owner);
@ -541,7 +545,9 @@ struct JSScript {
/* array of execution counters for every JSOp in the script, by runmode */
JSPCCounters pcCounters;
#ifdef JS_CRASH_DIAGNOSTICS
uint32 cookie2;
#endif
public:
#ifdef JS_METHODJIT
@ -747,19 +753,6 @@ js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner);
extern void
js_DestroyCachedScript(JSContext *cx, JSScript *script);
namespace js {
/*
* This diagnostic function checks that a compartment's list of scripts
* contains only valid scripts. It also searches for the target script
* in the list. If expected is true, it asserts that the target script
* is found. If expected is false, it asserts that it's not found.
*/
void
CheckCompartmentScripts(JSCompartment *comp);
} /* namespace js */
extern void
js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner);

View File

@ -45,7 +45,6 @@
#define jsutil_h___
#include "jstypes.h"
#include "jscrashreport.h"
#include "mozilla/Util.h"
#include <stdlib.h>
#include <string.h>
@ -62,12 +61,24 @@ JS_BEGIN_EXTERN_C
#define JS_FREE_PATTERN 0xDA
#ifdef JS_CRASH_DIAGNOSTICS
#define JS_POISON(p, val, size) memset((p), (val), (size))
#define JS_OPT_ASSERT(expr) \
((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
#define JS_OPT_ASSERT_IF(cond, expr) \
((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
#else
#define JS_POISON(p, val, size) ((void) 0)
#define JS_OPT_ASSERT(expr) ((void) 0)
#define JS_OPT_ASSERT_IF(cond, expr) ((void) 0)
#endif /* JS_CRASH_DIAGNOSTICS */
#ifdef DEBUG
#define JS_ASSERT(expr) \
@ -227,12 +238,6 @@ 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.
@ -240,17 +245,17 @@ static JS_INLINE void *js_record_oom(void *p) {
*/
static JS_INLINE void* js_malloc(size_t bytes) {
JS_OOM_POSSIBLY_FAIL();
return js_record_oom(malloc(bytes));
return malloc(bytes);
}
static JS_INLINE void* js_calloc(size_t bytes) {
JS_OOM_POSSIBLY_FAIL();
return js_record_oom(calloc(bytes, 1));
return calloc(bytes, 1);
}
static JS_INLINE void* js_realloc(void* p, size_t bytes) {
JS_OOM_POSSIBLY_FAIL();
return js_record_oom(realloc(p, bytes));
return realloc(p, bytes);
}
static JS_INLINE void js_free(void* p) {

View File

@ -155,10 +155,6 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper {
virtual void trace(JSTracer *trc, JSObject *wrapper);
virtual bool isCrossCompartment() {
return true;
}
static JSCrossCompartmentWrapper singleton;
};