Commits

Andrew Sutherland committed 931b14f

it works!

Comments (0)

Files changed (3)

js-runtime-fixups

+# HG changeset patch
+# Parent 9851298587333738e41d22f88b6142f747d1976a
+
+diff --git a/js/src/jsprobemgr.cpp b/js/src/jsprobemgr.cpp
+--- a/js/src/jsprobemgr.cpp
++++ b/js/src/jsprobemgr.cpp
+@@ -763,38 +763,61 @@ public:
+ 
+     virtual Result process(JSContext *cx) =0;
+     virtual ~Event() { }
+ };
+ 
+ class RunScriptEvent : public Event {
+ 
+ private:
+-    RunScriptEvent(JSScript* script, ScriptCookie cookie)
+-        : mScript(script), mCookie(cookie) { }
++    RunScriptEvent(const ScriptDescriptor &fdesc, ScriptCookie cookie)
++        : mCookie(cookie) {
++        mBodyLength = fdesc.length;
++        mBody = (jschar *)malloc(mBodyLength * sizeof(jschar));
++        memcpy(mBody, fdesc.body, mBodyLength * sizeof(jschar));
++        mFilename = strdup(fdesc.filename);
++        mLineno = fdesc.lineno;
++    }
+ 
+ public:
+-    JSScript *mScript;
++    jschar *mBody;
++    size_t mBodyLength;
++    char *mFilename;
++    uintN mLineno;
+     ScriptCookie mCookie;
+ 
+-    static RunScriptEvent *create(JSScript *script, ScriptCookie cookie) {
+-        return new RunScriptEvent(script, cookie);
++    static RunScriptEvent *create(const ScriptDescriptor &fdesc, ScriptCookie cookie) {
++        return new RunScriptEvent(fdesc, cookie);
++    }
++
++    ~RunScriptEvent() {
++        free(mBody);
++        free(mFilename);
+     }
+ 
+     virtual Result process(JSContext *cx) {
+         ProbeManager *mgr = static_cast<ProbeManager *>(JS_GetContextPrivate(cx));
+         JS_ASSERT(mgr != NULL);
+ 
+         jsval retval;
+         JSAutoRequest req(cx);
+ 
++        JSScript *script = JS_CompileUCScript(cx, JS_GetGlobalObject(cx),
++                                              mBody, mBodyLength, mFilename, mLineno);
++        if (!script)
++            return fail;
++
++        /* root this script until after executed */
++        JS_AddNamedScriptRoot(cx, &script, "runScriptOnce");
++
+         mgr->scriptStarted(mCookie);
+-        JS_ExecuteScript(cx, JS_GetGlobalObject(cx), mScript, &retval);
++        JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &retval);
+         mgr->scriptFinished(mCookie);
+-        JS_RemoveScriptRoot(cx, &mScript);
++
++        JS_RemoveScriptRoot(cx, &script);
+         return ok;
+     }
+ };
+ 
+ class ProbeFireEvent : public Event {
+     ProbePoint mProbe;
+     ProbeDataPtr mPayload;
+ 
+@@ -1083,78 +1106,99 @@ ProbeManager::~ProbeManager()
+ 
+ ProbeManager* ProbeManager::create()
+ {
+     return new ProbeManager();
+ }
+ 
+ bool ProbeManager::start()
+ {
+-    JSObject *global;
++    JSObject *handlerGlobal, *adminGlobal;
+     HandlerDescList dummyList = HandlerDescList();
+ 
+ #ifdef PR_LOGGING
+     gProbeManagerLog = PR_NewLogModule("ProbeManager");
+ #endif
+ 
+     registry = std::vector<HandlerDescList>((int)ProbePoint_Final, dummyList);
+     masterOrders = std::vector<PickOrder*>((int)ProbePoint_Final, NULL);
+ 
+-    /* create runtime */
+-    rt = JS_NewRuntime(160L * 1024L * 1024L);
+-    if (!rt)
++    /* create runtimes */
++    adminRt = JS_NewRuntime(160L * 1024L * 1024L);
++    if (!adminRt)
++        return false;
++
++    handlerRt = JS_NewRuntime(160L * 1024L * 1024L);
++    if (!handlerRt)
+         return false;
+ 
+     /* create handler context -- this is associated with
+        handlerThread, and dispatches/runs all handler functions */
+-    handlerCx = JS_NewContext(rt, 8 * 1024);
++    handlerCx = JS_NewContext(handlerRt, 8 * 1024);
+     if (!handlerCx)
+         return false;
+ 
+     JS_SetOptions(handlerCx, JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
+     JS_SetVersion(handlerCx, JSVERSION_LATEST);
+     JS_SetErrorReporter(handlerCx, js::probes::reportError);
+     /* stuff this here so events can mess with handler vector */
+     JS_SetContextPrivate(handlerCx, static_cast<void *>(this));
+ 
+     /* create admin context -- this is only used by the main thread,
+        and is used to allocate new JSScripts and JSFunctions */
+-    adminCx = JS_NewContext(rt, 8 * 1024);
++    adminCx = JS_NewContext(adminRt, 8 * 1024);
+     if (!adminCx)
+         return false;
+ 
+     JS_SetOptions(adminCx, JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
+     JS_SetVersion(adminCx, JSVERSION_LATEST);
+     JS_SetErrorReporter(adminCx, js::probes::reportError);
+ 
+     JS_SetContextThread(handlerCx);
+     /* scope + auto request used to cover side exits */
+     {
+         JSAutoRequest req(handlerCx);
+         /* Create the global object in a new compartment. */
+-        global = JS_NewCompartmentAndGlobalObject(handlerCx, &global_class, NULL);
+-        if (global == NULL)
++        handlerGlobal = JS_NewCompartmentAndGlobalObject(handlerCx,
++                                                         &global_class, NULL);
++        if (handlerGlobal == NULL)
+             return false;
+ 
+         /* Populate the global object with rt the standard globals, like
+            Object and Array. Also add some shell builtins, custom helpers. */
+-        if (!JS_InitStandardClasses(handlerCx, global))
++        if (!JS_InitStandardClasses(handlerCx, handlerGlobal))
+             return false;
+ 
+-        if (!JS_DefineFunctions(handlerCx, global, builtin_helper_functions)) {
++        if (!JS_DefineFunctions(handlerCx, handlerGlobal,
++                                builtin_helper_functions)) {
+             return NULL;
+         }
+     }
+     JS_ClearContextThread(handlerCx);
++    JS_ClearRuntimeThread(handlerRt);
+ 
+-    /* set global object in other context, set thread affinity */
++    /* create a global object for the admin context too */
+     JS_SetContextThread(adminCx);
+-    JS_BeginRequest(adminCx);
+-    JS_SetGlobalObject(adminCx, global);
+-    JS_EndRequest(adminCx);
++    {
++        JSAutoRequest req(adminCx);
++        /* Create the global object in a new compartment. */
++        adminGlobal = JS_NewCompartmentAndGlobalObject(adminCx,
++                                                       &global_class, NULL);
++        if (adminGlobal == NULL)
++            return false;
++
++        /* Populate the global object with rt the standard globals, like
++           Object and Array. Also add some shell builtins, custom helpers. */
++        if (!JS_InitStandardClasses(adminCx, adminGlobal))
++            return false;
++
++        if (!JS_DefineFunctions(adminCx, adminGlobal, builtin_helper_functions)) {
++            return NULL;
++        }
++    }
+ 
+     m_handlerMap = new HandlerMap(adminCx);
+     m_handlerMap->init();
+ 
+     /* create queue */
+     JS_ASSERT(!eq);
+     eq = new EventQueue();
+     if (!eq || !eq->initThreadSafeQueue()) {
+@@ -1199,26 +1243,29 @@ void ProbeManager::shutdown()
+     }
+ 
+     delete eq;
+     eq = NULL;
+ 
+     delete mq;
+     mq = NULL;
+ 
++    JS_SetRuntimeThread(handlerRt);
+     JS_SetContextThread(handlerCx);
+     JS_DestroyContext(handlerCx);
++    JS_DestroyRuntime(handlerRt);
+     JS_DestroyContext(adminCx);
+-    JS_DestroyRuntime(rt);
++    JS_DestroyRuntime(adminRt);
+ }
+ 
+ bool ProbeManager::doWork()
+ {
+-    /* necessary since this thread just started, but context was
++    /* necessary since this thread just started, but context/runtime was
+        allocated by the "main" thread in ProbeManager::start() */
++    JS_SetRuntimeThread(handlerRt);
+     JS_SetContextThread(handlerCx);
+     AutoLock hold(eq->getLock());
+ 
+     Event *event = NULL;
+     Event::Result result = Event::ok;
+ 
+     while (eq->take(&event)) {
+         JS_RELEASE_LOCK(eq->getLock());
+@@ -1230,18 +1277,19 @@ bool ProbeManager::doWork()
+         JS_ACQUIRE_LOCK(eq->getLock());
+         eq->drop(event);
+ 
+         delete event;
+         if (result  != Event::ok)
+             break;
+     }
+ 
+-    /* release the context from this thread since it will die now. */
++    /* release the context/runtime from this thread since it will die now. */
+     JS_ClearContextThread(handlerCx);
++    JS_ClearRuntimeThread(handlerRt);
+     return result == Event::ok;
+ }
+ 
+ 
+ bool ProbeManager::dispatchEvent(JSContext *cx, ProbePoint probe, ProbeDataPtr data) {
+     HandlerDescList hlist;
+     jsval *argv, *unpackedArgs;
+     jsval rval;
+@@ -1372,60 +1420,65 @@ bool ProbeManager::dispatchEvent(JSConte
+                     goto cleanup;
+                 }
+ 
+                 curObj = OBJECT_TO_JSVAL(newObj);
+             }
+         }
+     }  
+ 
+-    /* call handlers */
++    /* call handlers, compiling if this is their first time */
++    /* (they need to be compiled on this thread/runtime/compartment) */
+     hlist = handlers(probe);
+     globalObj = JS_GetGlobalObject(cx);
+ 
+     for (HandlerDescList::iterator it = hlist.begin(); it != hlist.end(); it++) {
+-        JS_CallFunction(cx, globalObj, (*it)->handlerFun, argc, argv, &rval);
++        HandlerDescriptor &desc = **it;
++        if (!desc.handlerFun) {
++            desc.handlerFun =
++                JS_CompileUCFunction(cx, globalObj, NULL,
++                                     desc.argc, desc.argnames,
++                                     desc.handlerDef.body,
++                                     desc.handlerDef.length,
++                                     desc.handlerDef.filename,
++                                     desc.handlerDef.lineno);
++            /* this should not happen because we try and compile it on add */
++            if (!desc.handlerFun)
++                continue;
++            JSObject *handlerFunObj = JS_GetFunctionObject(desc.handlerFun);
++            JS_AddNamedObjectRoot(cx, &handlerFunObj, "probe");
++        }
++        JS_CallFunction(cx, globalObj, desc.handlerFun, argc, argv, &rval);
+     }
+ 
+     cleanup:
+     JS_free(cx, argv);
+     JS_free(cx, unpackedArgs);
+     JS_LeaveLocalRootScope(cx);
+ 
+     if (!didFail)
+         return true;
+ 
+     JS_ReportError(cx, "ProbeManager::dispatchEvent: encountered error when creating object graph");
+     return false;
+ }
+ 
+-bool ProbeManager::runScriptOnce(ScriptDescriptor fdesc, ScriptCookie *cookie)
++bool ProbeManager::runScriptOnce(const ScriptDescriptor &fdesc, ScriptCookie *cookie)
+ {
+-    JSScript *script;
+-    JSAutoRequest req(adminCx);
+-
+-    script = JS_CompileUCScript(adminCx, JS_GetGlobalObject(adminCx), fdesc.body,
+-                                fdesc.length, fdesc.filename, fdesc.lineno);
+-    if (!script)
+-        return false;
+-
+     ScriptCookie newCookie = s_nextScriptCookie++;
+     *cookie = newCookie;
+ 
+-    RunScriptEvent *event = RunScriptEvent::create(script, newCookie);
++    RunScriptEvent *event = RunScriptEvent::create(fdesc, newCookie);
+     if (!event) return false;
+ 
+-    /* root this script until after executed */
+-    JS_AddNamedScriptRoot(adminCx, &event->mScript, "runScriptOnce");
+-
+     PR_ATOMIC_INCREMENT(&m_pendingScriptCount);
+     if (!eq->post(event)) {
+         delete event;
+         JS_ReportOutOfMemory(adminCx);
+-        return NULL;
++        return false;
+     }
+     return true;
+ }
+ 
+ bool ProbeManager::addProbeHandler(ProbePoint probe, 
+                                    ScriptDescriptor &handlerInfo, 
+                                    ScriptDescriptor &specInfo,
+                                    /* out */ HandlerCookie *cookie)
+@@ -1453,39 +1506,52 @@ bool ProbeManager::addProbeHandler(Probe
+     for (int i = 0; i < ProbePointData[probe].argc; i++) {
+         argnames[i+curOffset] = ProbePointData[probe].args[i].name;
+     }
+     curOffset += ProbePointData[probe].argc;
+     for (int i = 0; i < ProbePointData[GLOBAL_PROBE_ARGS].argc; i++) {
+         argnames[i+curOffset] = ProbePointData[GLOBAL_PROBE_ARGS].args[i].name;
+     }
+     
+-    /* compile handler function */
++    /* compile handler function just to test its syntactic validity; we will
++       discard it and re-compile from a string on the processing thread. */
+     handlerFun = JS_CompileUCFunction(adminCx, globalObj, NULL,
+                                       argc, argnames,
+                                       handlerInfo.body, handlerInfo.length, 
+                                       handlerInfo.filename, handlerInfo.lineno);
+-    JS_free(adminCx, argnames);
+-
+-    if (!handlerFun)
++    /* TODO: propagate exceptions for syntactic errors? */
++    if (!handlerFun) {
++        JS_free(adminCx, argnames);
+         return false;
++    }
+ 
+     order = createOrderFromSpec(adminCx, probe, specInfo);
+-    if (!order)
++    if (!order) {
++        JS_free(adminCx, argnames);
+         return false;
++    }
+ 
+     HandlerCookie newCookie = s_nextHandlerCookie++;
+ 
+     /* add as handler for this probe point */
+     handlerDesc = new HandlerDescriptor;
+-    handlerDesc->handlerFun = handlerFun;
++    handlerDesc->handlerFun = 0;
++    handlerDesc->argc = argc;
++    handlerDesc->argnames = argnames;
++    handlerDesc->handlerDef.body =
++        (jschar *)JS_malloc(adminCx, handlerInfo.length * sizeof(jschar));
++    memcpy(const_cast<jschar *>(handlerDesc->handlerDef.body), handlerInfo.body,
++           handlerInfo.length * sizeof(jschar));
++    handlerDesc->handlerDef.length = handlerInfo.length;
++    handlerDesc->handlerDef.filename = JS_strdup(adminCx, handlerInfo.filename);
++    handlerDesc->handlerDef.lineno = handlerInfo.lineno;
++
+     handlerDesc->probe = probe;
+     handlerDesc->order = order;
+     handlerDesc->cookie = newCookie;
+-    /* TODO: root handlerDesc->handlerFun? */
+     addHandler(handlerDesc);
+ 
+     /* register and return the handler's cookie */
+     if (!m_handlerMap->putNew(newCookie, handlerDesc))
+         return false;
+ 
+     *cookie = newCookie;
+     return true;
+@@ -1504,16 +1570,25 @@ bool ProbeManager::removeProbeHandler(Ha
+         hdesc = p->value;
+         m_handlerMap->remove(p);
+     } else {
+         PR_LOG(gProbeManagerLog, PR_LOG_ERROR, ("ProbeManager: Tried to remove handler with bad cookie: %d\n", cookie));
+         return false;
+     }
+ 
+     removeHandler(hdesc);
++    /* TODO: post a cleanup message to the processing thread so it can remove
++       the root:
++    JSObject *handlerFunObj = JS_GetFunctionObject(hdesc->handlerFun);
++    JS_RemoveObjectRoot(adminCx, &handlerFunObj);
++    */
++
++    JS_free(adminCx, hdesc->argnames);
++    JS_free(adminCx, const_cast<jschar *>(hdesc->handlerDef.body));
++    JS_free(adminCx, const_cast<char *>(hdesc->handlerDef.filename));
+ 
+     /* return status */
+     return true;
+ }
+ 
+ bool ProbeManager::clearProbeHandlers(ProbePoint probe)
+ {
+     /* steal handler queue lock */
+@@ -1553,17 +1628,19 @@ bool ProbeManager::fireProbe(ProbePoint 
+     /* are there any handlers registered for this probe id? */
+     if (!hasHandlers(probe))
+         return true;
+ 
+     PickOrder order = getPickOrder(probe);
+     void *payloadbuf;
+     void *bufp;
+ 
+-    payloadbuf = rt->malloc_(order.totalBytes);
++    /* the handler runtime is the one who is going to end up owning the data.
++       the owning thread is (currently) not checked */
++    payloadbuf = handlerRt->malloc_(order.totalBytes);
+ 
+     /* zero the memory for debugging niceness */
+     memset(payloadbuf, 0L, order.totalBytes);
+     bufp = payloadbuf; /* cursor */
+ 
+     va_list argp;
+     int lastArgn = -1;
+     ProbeValue curArg = ProbeValue(ProbeType_DummyGlobal);
+@@ -1595,17 +1672,17 @@ bool ProbeManager::fireProbe(ProbePoint 
+             }
+             curArg = ProbeValue(ty, &curArgp);
+         }
+ 
+         /* transform this argument */
+         ProbeValue tmp = curArg;
+         for (PickItem::TransformVec::iterator it = item->transforms.begin();
+              it != item->transforms.end(); it++) {
+-            if (!(ProbeFunctionData[*it].fn)(rt, &tmp)) {
++            if (!(ProbeFunctionData[*it].fn)(handlerRt, &tmp)) {
+                 PR_LOG(gProbeManagerLog, PR_LOG_ERROR, ("Error when applying transform %d (%s)\n", ProbeFunctionData[*it].transform, ProbeFunctionData[*it].fn_name));
+             }
+         }
+ 
+         /* serialize and shift */
+         tmp.serialize(bufp);
+         bufp = ((char *)bufp) + ProbeTypeData[tmp.ty].bytes;
+     }
+diff --git a/js/src/jsprobemgr.h b/js/src/jsprobemgr.h
+--- a/js/src/jsprobemgr.h
++++ b/js/src/jsprobemgr.h
+@@ -57,17 +57,22 @@ struct PickOrder;
+ struct ScriptDescriptor {
+     const jschar *body;
+     size_t length;
+     const char *filename;
+     uintN lineno;
+ };
+ 
+ struct HandlerDescriptor {
++    /* created on the processing thread the first time it is used */
+     JSFunction *handlerFun;
++    /* the script info to be used to compile handlerFun */
++    ScriptDescriptor handlerDef;
++    uintN argc;
++    const char **argnames;
+     ProbePoint probe;
+     PickOrder *order;
+     HandlerCookie cookie;
+ };
+ 
+ struct Message {
+ private:
+     const uint64_t *m_data;
+@@ -108,17 +113,18 @@ public:
+ private:
+     typedef HashMap<HandlerCookie, HandlerDescriptor*> HandlerMap;
+ 
+     static ProbeManager *s_lastManager;
+     static HandlerCookie s_nextHandlerCookie;
+     static ScriptCookie s_nextScriptCookie;
+ 
+     PRThread *handlerThread;
+-    JSRuntime *rt;
++    JSRuntime *adminRt;
++    JSRuntime *handlerRt;
+     EventQueue *eq;
+     MessageQueue *mq;
+ 
+     /* whether the handler thread is currently executing an async script.
+        this should only get/set from the handler thread, as it is not threadsafe */
+     bool m_scriptRunning; 
+     ScriptCookie m_currentScript;
+     PRInt32 m_pendingScriptCount;
+@@ -130,17 +136,18 @@ private:
+ 
+     JSContext *handlerCx;
+     JSContext *adminCx;
+     
+     JSObject *itemBuilderProto;
+ 
+     explicit ProbeManager() 
+     : handlerThread(NULL)
+-    , rt(NULL)
++    , adminRt(NULL)
++    , handlerRt(NULL)
+     , eq(NULL)
+     , mq(NULL)
+     , m_scriptRunning(false)
+     , m_currentScript(0)
+     , m_pendingScriptCount(0)
+     , m_handlerMap(NULL)
+     , handlerCx(NULL)
+     , adminCx(NULL)
+@@ -182,29 +189,29 @@ public:
+     /* main phases */
+     bool start();
+     void shutdown();
+     bool doWork();
+ 
+     /* main commands */
+     bool dispatchEvent(JSContext *cx, ProbePoint probe, ProbeDataPtr data);
+ 
+-    bool runScriptOnce(ScriptDescriptor fdesc, ScriptCookie *scriptCookie);
++    bool runScriptOnce(const ScriptDescriptor &fdesc, ScriptCookie *scriptCookie);
+     bool addProbeHandler(ProbePoint probe, ScriptDescriptor &fdesc, ScriptDescriptor &specInfo, HandlerCookie* handlerCookie);
+     bool removeProbeHandler(HandlerCookie cookie);
+     bool clearProbeHandlers(ProbePoint probe);
+ 
+     /* TODO: this should always be inlined into the Probes::whateverThing function
+      * to avoid an extra function call for each probe */
+     /* TODO: inline everything that this calls, as well (EventQueue.push) */
+     bool fireProbe(ProbePoint probe, ...);
+ 
+     /* use these to avoid reentrancy on a different thread (say, by
+      * triggering a GC or heap resize event on probe runtime) */
+-    bool isProbeRuntime(JSRuntime *runtime) const { return runtime == rt; }
++    bool isProbeRuntime(JSRuntime *runtime) const { return runtime == handlerRt; }
+ 
+     bool isScriptRunning() const { return m_scriptRunning; }
+     bool hasPendingScripts() const { return m_pendingScriptCount; }
+ 
+     bool hasMessages() const;
+     bool pushMessage(Message *message);
+     Message *popMessage();
+ };

put-back-runtime-for-gc

+# HG changeset patch
+# Parent 6e0416483370a27e238eac8f4682db2d2b77b129
+
+diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp
+--- a/js/src/gc/Statistics.cpp
++++ b/js/src/gc/Statistics.cpp
+@@ -184,17 +184,17 @@ Statistics::beginGC(JSCompartment *comp,
+ 
+     PodArrayZero(phaseStarts);
+     PodArrayZero(phaseEnds);
+     PodArrayZero(phaseTimes);
+ 
+     triggerReason = reason;
+ 
+     beginPhase(PHASE_GC);
+-    Probes::GCStart(compartment);
++    Probes::GCStart(runtime, compartment);
+ 
+     GCCrashData crashData;
+     crashData.isCompartment = !!compartment;
+     crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
+ }
+ 
+ double
+ Statistics::t(Phase phase)
+@@ -261,17 +261,17 @@ Statistics::printStats()
+                 t(PHASE_GC), t(PHASE_MARK), t(PHASE_SWEEP));
+     }
+     fflush(fp);
+ }
+ 
+ void
+ Statistics::endGC()
+ {
+-    Probes::GCEnd(compartment);
++    Probes::GCEnd(runtime, compartment);
+     endPhase(PHASE_GC);
+     crash::SnapshotGCStack();
+ 
+     for (int i = 0; i < PHASE_LIMIT; i++)
+         totals[i] += phaseTimes[i];
+ 
+     if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
+         (*cb)(JS_TELEMETRY_GC_REASON, triggerReason);
+diff --git a/js/src/jsprobes.h b/js/src/jsprobes.h
+--- a/js/src/jsprobes.h
++++ b/js/src/jsprobes.h
+@@ -193,18 +193,18 @@ bool releaseMemory(JSContext *cx, void *
+  * GC timing is tricky and at the time of this writing is changing frequently.
+  * GCStart(NULL)/GCEnd(NULL) are intended to bracket the entire garbage
+  * collection (either global or single-compartment), but a separate thread may
+  * continue doing work after GCEnd.
+  *
+  * Multiple compartments' GC will be interleaved during a global collection
+  * (eg, compartment 1 starts, compartment 2 starts, compartment 1 ends, ...)
+  */
+-bool GCStart(JSCompartment *compartment);
+-bool GCEnd(JSCompartment *compartment);
++bool GCStart(JSRuntime *rt, JSCompartment *compartment);
++bool GCEnd(JSRuntime *rt, JSCompartment *compartment);
+ 
+ bool GCStartMarkPhase(JSCompartment *compartment);
+ bool GCEndMarkPhase(JSCompartment *compartment);
+ 
+ bool GCStartSweepPhase(JSCompartment *compartment);
+ bool GCEndSweepPhase(JSCompartment *compartment);
+ 
+ /*
+@@ -655,55 +655,55 @@ Probes::releaseMemory(JSContext *cx, voi
+     if (ProfilingActive && !ETWReleaseMemory(cx, address, nbytes))
+         ok = false;
+ #endif
+ 
+     return ok;
+ }
+ 
+ inline bool
+-Probes::GCStart(JSCompartment *compartment)
++Probes::GCStart(JSRuntime *rt, JSCompartment *compartment)
+ {
+     bool ok = true;
+ 
+ #ifdef MOZ_ETW
+     if (ProfilingActive && !ETWGCStart(compartment))
+         ok = false;
+ #endif
+ #ifdef MOZ_JSPROBES
+     ProbeManager *mgr = ProbeManager::manager();
+     if (!compartment) {
+-        if (!mgr || !mgr->fireProbe(GLOBAL_GC_DID_START, compartment->rt))
++        if (!mgr || !mgr->fireProbe(GLOBAL_GC_DID_START, rt))
+             ok = false;
+     } else {
+-        if (!mgr || !mgr->fireProbe(COMPARTMENT_GC_DID_START, compartment->rt, compartment))
++        if (!mgr || !mgr->fireProbe(COMPARTMENT_GC_DID_START, rt, compartment))
+             ok = false;
+     }
+ #endif
+ 
+     return ok;
+ }
+ 
+ inline bool
+-Probes::GCEnd(JSCompartment *compartment)
++Probes::GCEnd(JSRuntime *rt, JSCompartment *compartment)
+ {
+     bool ok = true;
+ 
+ #ifdef MOZ_ETW
+     if (ProfilingActive && !ETWGCEnd(compartment))
+         ok = false;
+ #endif
+ 
+ #ifdef MOZ_JSPROBES
+     ProbeManager *mgr = ProbeManager::manager();
+     if (!compartment) {
+-        if (!mgr || !mgr->fireProbe(GLOBAL_GC_WILL_END, compartment->rt))
++        if (!mgr || !mgr->fireProbe(GLOBAL_GC_WILL_END, rt))
+             ok = false;
+     } else {
+-        if (!mgr || !mgr->fireProbe(COMPARTMENT_GC_WILL_END, compartment->rt, compartment))
++        if (!mgr || !mgr->fireProbe(COMPARTMENT_GC_WILL_END, rt, compartment))
+             ok = false;
+     }
+ #endif
+     return ok;
+ }
+ 
+ inline bool
+ Probes::GCStartMarkPhase(JSCompartment *compartment)
 jsprobes-dispatch-integration
 jsshell-integration #+shell
 xpcom-nsIProbeService
+js-runtime-fixups
+put-back-runtime-for-gc