diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index b662b230cbdc..f84e934d9c2e 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -816,7 +816,7 @@ nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener) } #endif - nsJSContext::GarbageCollectNow(); + nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS); nsJSContext::CycleCollectNow(aListener); return NS_OK; diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index cb166608436f..76b9d3f0975b 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -2272,7 +2272,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, newInnerWindow->mChromeEventHandler = mChromeEventHandler; } - mContext->GC(); + mContext->GC(js::gcreason::SET_NEW_DOCUMENT); mContext->DidInitializeContext(); if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { @@ -2429,7 +2429,7 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) } if (mContext) { - mContext->GC(); + mContext->GC(js::gcreason::SET_DOC_SHELL); mContext->FinalizeContext(); mContext = nsnull; } diff --git a/dom/base/nsIScriptContext.h b/dom/base/nsIScriptContext.h index 6b16b0702dc8..c235ede9dddf 100644 --- a/dom/base/nsIScriptContext.h +++ b/dom/base/nsIScriptContext.h @@ -43,6 +43,7 @@ #include "nsISupports.h" #include "nsCOMPtr.h" #include "nsIProgrammingLanguage.h" +#include "jsfriendapi.h" #include "jspubtd.h" class nsIScriptGlobalObject; @@ -354,7 +355,7 @@ public: * * @return NS_OK if the method is successful */ - virtual void GC() = 0; + virtual void GC(js::gcreason::Reason aReason) = 0; /** * Inform the context that a script was evaluated. diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 43e16b117ac1..971fa58ebbf8 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -203,7 +203,7 @@ nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { if (sGCOnMemoryPressure) { - nsJSContext::GarbageCollectNow(true); + nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, nsGCShrinking); nsJSContext::CycleCollectNow(); } return NS_OK; @@ -1111,7 +1111,7 @@ nsJSContext::DestroyJSContext() js_options_dot_str, this); if (mGCOnDestruction) { - PokeGC(); + PokeGC(js::gcreason::NSJSCONTEXT_DESTROY); } // Let xpconnect destroy the JSContext when it thinks the time is right. @@ -3220,7 +3220,7 @@ nsJSContext::ScriptExecuted() //static void -nsJSContext::GarbageCollectNow(bool shrinkingGC) +nsJSContext::GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind) { NS_TIME_FUNCTION_MIN(1.0); SAMPLE_LABEL("GC", "GarbageCollectNow"); @@ -3238,7 +3238,7 @@ nsJSContext::GarbageCollectNow(bool shrinkingGC) sLoadingInProgress = false; if (nsContentUtils::XPConnect()) { - nsContentUtils::XPConnect()->GarbageCollect(shrinkingGC); + nsContentUtils::XPConnect()->GarbageCollect(reason, gckind); } } @@ -3276,7 +3276,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener) // If we collected a substantial amount of cycles, poke the GC since more objects // might be unreachable now. if (sCCollectedWaitingForGC > 250) { - PokeGC(); + PokeGC(js::gcreason::CC_WAITING); } PRTime now = PR_Now(); @@ -3315,7 +3315,8 @@ GCTimerFired(nsITimer *aTimer, void *aClosure) { NS_RELEASE(sGCTimer); - nsJSContext::GarbageCollectNow(); + uintptr_t reason = reinterpret_cast(aClosure); + nsJSContext::GarbageCollectNow(static_cast(reason), nsGCNormal); } void @@ -3359,12 +3360,12 @@ nsJSContext::LoadEnd() // Its probably a good idea to GC soon since we have finished loading. sLoadingInProgress = false; - PokeGC(); + PokeGC(js::gcreason::LOAD_END); } // static void -nsJSContext::PokeGC() +nsJSContext::PokeGC(js::gcreason::Reason aReason) { if (sGCTimer) { // There's already a timer for GC'ing, just return @@ -3380,7 +3381,7 @@ nsJSContext::PokeGC() static bool first = true; - sGCTimer->InitWithFuncCallback(GCTimerFired, nsnull, + sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast(aReason), first ? NS_FIRST_GC_DELAY : NS_GC_DELAY, @@ -3473,9 +3474,9 @@ nsJSContext::KillCCTimer() } void -nsJSContext::GC() +nsJSContext::GC(js::gcreason::Reason aReason) { - PokeGC(); + PokeGC(aReason); } static void @@ -3513,7 +3514,7 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) // probably a time of heavy activity and we want to delay // the full GC, but we do want it to happen eventually. if (comp) { - nsJSContext::PokeGC(); + nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT); // We poked the GC, so we can kill any pending CC here. nsJSContext::KillCCTimer(); diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 3f43bff80406..8deda4b3545a 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -41,10 +41,12 @@ #include "nsIScriptRuntime.h" #include "nsCOMPtr.h" #include "jsapi.h" +#include "jsfriendapi.h" #include "nsIObserver.h" #include "nsIXPCScriptNotify.h" #include "prtime.h" #include "nsCycleCollectionParticipant.h" +#include "nsIXPConnect.h" class nsIXPConnectJSObjectHolder; class nsRootedJSValueArray; @@ -179,11 +181,11 @@ public: static void LoadStart(); static void LoadEnd(); - static void GarbageCollectNow(bool shrinkingGC = false); + static void GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind = nsGCNormal); static void ShrinkGCBuffersNow(); static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull); - static void PokeGC(); + static void PokeGC(js::gcreason::Reason aReason); static void KillGCTimer(); static void PokeShrinkGCBuffers(); @@ -193,7 +195,7 @@ public: static void MaybePokeCC(); static void KillCCTimer(); - virtual void GC(); + virtual void GC(js::gcreason::Reason aReason); protected: nsresult InitializeExternalClasses(); diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 5494908162eb..c81f30fd0545 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -800,14 +800,14 @@ ContentChild::GetIndexedDBPath() bool ContentChild::RecvGarbageCollect() { - nsJSContext::GarbageCollectNow(); + nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC); return true; } bool ContentChild::RecvCycleCollect() { - nsJSContext::GarbageCollectNow(); + nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC); nsJSContext::CycleCollectNow(); return true; } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index d5cf0279ca5e..10b2bce6acef 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -3794,10 +3794,10 @@ WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking, AssertIsOnWorkerThread(); if (aShrinking) { - JS_ShrinkingGC(aCx); + js::ShrinkingGC(aCx, js::gcreason::DOM_WORKER); } else { - JS_GC(aCx); + js::GCForReason(aCx, js::gcreason::DOM_WORKER); } if (aCollectChildren) { diff --git a/js/src/gc/Statistics.cpp b/js/src/gc/Statistics.cpp index b03b7020bb4f..b1a1197ca116 100644 --- a/js/src/gc/Statistics.cpp +++ b/js/src/gc/Statistics.cpp @@ -38,6 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include +#include #include "jscntxt.h" #include "jscrashformat.h" @@ -52,6 +53,22 @@ namespace js { namespace gcstats { +static const char * +ExplainReason(gcreason::Reason reason) +{ + switch (reason) { +#define SWITCH_REASON(name) \ + case gcreason::name: \ + return #name; + GCREASONS(SWITCH_REASON) + + default: + JS_NOT_REACHED("bad GC reason"); + return "?"; +#undef SWITCH_REASON + } +} + Statistics::ColumnInfo::ColumnInfo(const char *title, double t, double total) : title(title) { @@ -117,8 +134,8 @@ Statistics::makeTable(ColumnInfo *cols) } Statistics::Statistics(JSRuntime *rt) - : runtime(rt) - , triggerReason(PUBLIC_API) //dummy reason to satisfy makeTable + : runtime(rt), + triggerReason(gcreason::NO_REASON) { PodArrayZero(counts); PodArrayZero(totals); @@ -178,7 +195,7 @@ struct GCCrashData }; void -Statistics::beginGC(JSCompartment *comp, Reason reason) +Statistics::beginGC(JSCompartment *comp, gcreason::Reason reason) { compartment = comp; @@ -276,7 +293,6 @@ Statistics::endGC() if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { (*cb)(JS_TELEMETRY_GC_REASON, triggerReason); (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0); - (*cb)(JS_TELEMETRY_GC_IS_SHAPE_REGEN, 0); (*cb)(JS_TELEMETRY_GC_MS, t(PHASE_GC)); (*cb)(JS_TELEMETRY_GC_MARK_MS, t(PHASE_MARK)); (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(PHASE_SWEEP)); diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 0597a94db72e..497efaca6072 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -42,6 +42,7 @@ #include +#include "jsfriendapi.h" #include "jspubtd.h" #include "jsutil.h" @@ -50,32 +51,6 @@ struct JSCompartment; namespace js { namespace gcstats { -enum Reason { - PUBLIC_API, - MAYBEGC, - LASTCONTEXT, - DESTROYCONTEXT, - LASTDITCH, - TOOMUCHMALLOC, - ALLOCTRIGGER, - CHUNK, - SHAPE, - REFILL -}; -static const int NUM_REASONS = REFILL + 1; - -static inline const char * -ExplainReason(Reason r) -{ - static const char *strs[] = {" API", "Maybe", "LastC", "DestC", "LastD", - "Mallc", "Alloc", "Chunk", "Shape", "Refil"}; - - JS_ASSERT(strcmp(strs[SHAPE], "Shape") == 0 && - sizeof(strs) / sizeof(strs[0]) == NUM_REASONS); - - return strs[r]; -} - enum Phase { PHASE_GC, PHASE_MARK, @@ -103,7 +78,7 @@ struct Statistics { Statistics(JSRuntime *rt); ~Statistics(); - void beginGC(JSCompartment *comp, Reason reason); + void beginGC(JSCompartment *comp, gcreason::Reason reason); void endGC(); void beginPhase(Phase phase); @@ -122,7 +97,7 @@ struct Statistics { FILE *fp; bool fullFormat; - Reason triggerReason; + gcreason::Reason triggerReason; JSCompartment *compartment; uint64_t phaseStarts[PHASE_LIMIT]; @@ -140,8 +115,8 @@ struct Statistics { struct ColumnInfo { const char *title; - char str[12]; - char totalStr[12]; + char str[32]; + char totalStr[32]; int width; ColumnInfo() {} @@ -155,7 +130,8 @@ struct Statistics { }; struct AutoGC { - AutoGC(Statistics &stats, JSCompartment *comp, Reason reason JS_GUARD_OBJECT_NOTIFIER_PARAM) + AutoGC(Statistics &stats, JSCompartment *comp, gcreason::Reason reason + JS_GUARD_OBJECT_NOTIFIER_PARAM) : stats(stats) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginGC(comp, reason); } ~AutoGC() { stats.endGC(); } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index bea809ec64b9..874cff974d37 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -735,6 +735,7 @@ JSRuntime::JSRuntime() gcIsNeeded(0), gcWeakMapList(NULL), gcStats(thisFromCtor()), + gcTriggerReason(gcreason::NO_REASON), gcTriggerCompartment(NULL), gcCurrentCompartment(NULL), gcCheckCompartment(NULL), @@ -2857,7 +2858,7 @@ JS_CompartmentGC(JSContext *cx, JSCompartment *comp) JS_ASSERT(comp != cx->runtime->atomsCompartment); js::gc::VerifyBarriers(cx, true); - js_GC(cx, comp, GC_NORMAL, gcstats::PUBLIC_API); + js_GC(cx, comp, GC_NORMAL, gcreason::API); } JS_PUBLIC_API(void) diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 816a426e7793..e781da8f3a3f 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -323,13 +323,13 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) #endif if (last) { - js_GC(cx, NULL, GC_NORMAL, gcstats::LASTCONTEXT); + js_GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); /* Take the runtime down, now that it has no contexts or atoms. */ JS_LOCK_GC(rt); } else { if (mode == JSDCM_FORCE_GC) - js_GC(cx, NULL, GC_NORMAL, gcstats::DESTROYCONTEXT); + js_GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); else if (mode == JSDCM_MAYBE_GC) JS_MaybeGC(cx); @@ -1179,7 +1179,7 @@ JSContext::runningWithTrustedPrincipals() const JS_FRIEND_API(void) JSRuntime::onTooMuchMalloc() { - TriggerGC(this, gcstats::TOOMUCHMALLOC); + TriggerGC(this, gcreason::TOO_MUCH_MALLOC); } JS_FRIEND_API(void *) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 73896ec5dd67..3bef1a46cfad 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -318,7 +318,7 @@ struct JSRuntime js::gcstats::Statistics gcStats; /* The reason that an interrupt-triggered GC should be called. */ - js::gcstats::Reason gcTriggerReason; + js::gcreason::Reason gcTriggerReason; /* Pre-allocated space for the GC mark stack. */ uintptr_t gcMarkStackArray[js::MARK_STACK_LENGTH]; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 3f92643de0ba..d05d02243adf 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -128,9 +128,15 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj } JS_FRIEND_API(void) -JS_ShrinkingGC(JSContext *cx) +js::GCForReason(JSContext *cx, gcreason::Reason reason) { - js_GC(cx, NULL, GC_SHRINK, gcstats::PUBLIC_API); + js_GC(cx, NULL, GC_NORMAL, reason); +} + +JS_FRIEND_API(void) +js::ShrinkingGC(JSContext *cx, gcreason::Reason reason) +{ + js_GC(cx, NULL, GC_SHRINK, reason); } JS_FRIEND_API(void) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 903e561d7cd7..f178d6f207ab 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -72,9 +72,6 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj extern JS_FRIEND_API(uint32_t) JS_ObjectCountDynamicSlots(JSObject *obj); -extern JS_FRIEND_API(void) -JS_ShrinkingGC(JSContext *cx); - extern JS_FRIEND_API(void) JS_ShrinkGCBuffers(JSRuntime *rt); @@ -101,7 +98,6 @@ JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape); enum { JS_TELEMETRY_GC_REASON, JS_TELEMETRY_GC_IS_COMPARTMENTAL, - JS_TELEMETRY_GC_IS_SHAPE_REGEN, JS_TELEMETRY_GC_MS, JS_TELEMETRY_GC_MARK_MS, JS_TELEMETRY_GC_SWEEP_MS @@ -572,6 +568,56 @@ GetRuntimeCompartments(JSRuntime *rt); extern JS_FRIEND_API(size_t) SizeOfJSContext(); +#define GCREASONS(D) \ + /* Reasons internal to the JS engine */ \ + D(API) \ + D(MAYBEGC) \ + D(LAST_CONTEXT) \ + D(DESTROY_CONTEXT) \ + D(LAST_DITCH) \ + D(TOO_MUCH_MALLOC) \ + D(ALLOC_TRIGGER) \ + D(UNUSED1) /* was CHUNK */ \ + D(UNUSED2) /* was SHAPE */ \ + D(UNUSED3) /* was REFILL */ \ + \ + /* Reasons from Firefox */ \ + D(DOM_WINDOW_UTILS) \ + D(COMPONENT_UTILS) \ + D(MEM_PRESSURE) \ + D(CC_WAITING) \ + D(CC_FORCED) \ + D(LOAD_END) \ + D(POST_COMPARTMENT) \ + D(PAGE_HIDE) \ + D(NSJSCONTEXT_DESTROY) \ + D(SET_NEW_DOCUMENT) \ + D(SET_DOC_SHELL) \ + D(DOM_UTILS) \ + D(DOM_IPC) \ + D(DOM_WORKER) \ + D(INTER_SLICE_GC) \ + D(REFRESH_FRAME) + +namespace gcreason { + +/* GCReasons will end up looking like JSGC_MAYBEGC */ +enum Reason { +#define MAKE_REASON(name) name, + GCREASONS(MAKE_REASON) +#undef MAKE_REASON + NO_REASON, + NUM_REASONS +}; + +} /* namespace gcreason */ + +extern JS_FRIEND_API(void) +GCForReason(JSContext *cx, gcreason::Reason reason); + +extern JS_FRIEND_API(void) +ShrinkingGC(JSContext *cx, gcreason::Reason reason); + extern JS_FRIEND_API(bool) IsIncrementalBarrierNeeded(JSRuntime *rt); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 176ce34ccd6c..08a1b7feeb32 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -742,7 +742,7 @@ Chunk::allocateArena(JSCompartment *comp, AllocKind thingKind) rt->gcBytes += ArenaSize; comp->gcBytes += ArenaSize; if (comp->gcBytes >= comp->gcTriggerBytes) - TriggerCompartmentGC(comp, gcstats::ALLOCTRIGGER); + TriggerCompartmentGC(comp, gcreason::ALLOC_TRIGGER); return aheader; } @@ -1647,7 +1647,7 @@ RunLastDitchGC(JSContext *cx) /* The last ditch GC preserves all atoms. */ AutoKeepAtoms keep(rt); - js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcstats::LASTDITCH); + js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcreason::LAST_DITCH); } /* static */ void * @@ -2137,7 +2137,7 @@ MarkRuntime(JSTracer *trc) } void -TriggerGC(JSRuntime *rt, gcstats::Reason reason) +TriggerGC(JSRuntime *rt, gcreason::Reason reason) { JS_ASSERT(rt->onOwnerThread()); @@ -2152,7 +2152,7 @@ TriggerGC(JSRuntime *rt, gcstats::Reason reason) } void -TriggerCompartmentGC(JSCompartment *comp, gcstats::Reason reason) +TriggerCompartmentGC(JSCompartment *comp, gcreason::Reason reason) { JSRuntime *rt = comp->rt; JS_ASSERT(!rt->gcRunning); @@ -2198,18 +2198,18 @@ MaybeGC(JSContext *cx) JS_ASSERT(rt->onOwnerThread()); if (rt->gcZeal()) { - js_GC(cx, NULL, GC_NORMAL, gcstats::MAYBEGC); + js_GC(cx, NULL, GC_NORMAL, gcreason::MAYBEGC); return; } JSCompartment *comp = cx->compartment; if (rt->gcIsNeeded) { - js_GC(cx, (comp == rt->gcTriggerCompartment) ? comp : NULL, GC_NORMAL, gcstats::MAYBEGC); + js_GC(cx, (comp == rt->gcTriggerCompartment) ? comp : NULL, GC_NORMAL, gcreason::MAYBEGC); return; } if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4)) { - js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL, gcstats::MAYBEGC); + js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL, gcreason::MAYBEGC); return; } @@ -2223,7 +2223,7 @@ MaybeGC(JSContext *cx) if (rt->gcChunkAllocationSinceLastGC || rt->gcNumArenasFreeCommitted > FreeCommittedArenasThreshold) { - js_GC(cx, NULL, GC_SHRINK, gcstats::MAYBEGC); + js_GC(cx, NULL, GC_SHRINK, gcreason::MAYBEGC); } else { rt->gcNextFullGCTime = now + GC_IDLE_FULL_SPAN; } @@ -2966,7 +2966,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) } void -js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcstats::Reason reason) +js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcreason::Reason reason) { JSRuntime *rt = cx->runtime; JS_AbortIfWrongThread(rt); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 594c1f628a6c..7863f1118ba1 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1382,11 +1382,11 @@ MarkContext(JSTracer *trc, JSContext *acx); /* Must be called with GC lock taken. */ extern void -TriggerGC(JSRuntime *rt, js::gcstats::Reason reason); +TriggerGC(JSRuntime *rt, js::gcreason::Reason reason); /* Must be called with GC lock taken. */ extern void -TriggerCompartmentGC(JSCompartment *comp, js::gcstats::Reason reason); +TriggerCompartmentGC(JSCompartment *comp, js::gcreason::Reason reason); extern void MaybeGC(JSContext *cx); @@ -1409,7 +1409,7 @@ typedef enum JSGCInvocationKind { /* Pass NULL for |comp| to get a full GC. */ extern void -js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcstats::Reason r); +js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcreason::Reason r); namespace js { diff --git a/js/xpconnect/idl/nsIXPConnect.idl b/js/xpconnect/idl/nsIXPConnect.idl index 04a58b17316e..4de0430fc80f 100644 --- a/js/xpconnect/idl/nsIXPConnect.idl +++ b/js/xpconnect/idl/nsIXPConnect.idl @@ -390,9 +390,15 @@ interface nsIXPCFunctionThisTranslator : nsISupports #define NS_XPCONNECT_CID \ { 0xcb6593e0, 0xf9b2, 0x11d2, \ { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } } + +enum nsGCType { + nsGCNormal, + nsGCShrinking, + nsGCIncremental +}; %} -[uuid(241e6db3-e018-4d99-b976-c782a05f9c77)] +[uuid(686bb1d0-4711-11e1-b86c-0800200c9a66)] interface nsIXPConnect : nsISupports { %{ C++ @@ -723,8 +729,10 @@ interface nsIXPConnect : nsISupports /** * Trigger a JS garbage collection. + * Use a js::gcreason::Reason from jsfriendapi.h for the kind. + * Use the nsGCType enum for the kind. */ - void GarbageCollect(in boolean shrinkingGC); + void GarbageCollect(in PRUint32 reason, in PRUint32 kind); /** * Define quick stubs on the given object, @a proto. diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 0b094d899a05..716cfdb78692 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -3600,7 +3600,7 @@ nsXPCComponents_Utils::GetWeakReference(const JS::Value &object, JSContext *cx, NS_IMETHODIMP nsXPCComponents_Utils::ForceGC(JSContext *cx) { - JS_GC(cx); + js::GCForReason(cx, js::gcreason::COMPONENT_UTILS); return NS_OK; } @@ -3608,7 +3608,7 @@ nsXPCComponents_Utils::ForceGC(JSContext *cx) NS_IMETHODIMP nsXPCComponents_Utils::ForceShrinkingGC(JSContext *cx) { - JS_ShrinkingGC(cx); + js::ShrinkingGC(cx, js::gcreason::COMPONENT_UTILS); return NS_OK; } @@ -3636,9 +3636,9 @@ class PreciseGCRunnable : public nsRunnable } if (mShrinking) - JS_ShrinkingGC(mCx); + js::ShrinkingGC(mCx, js::gcreason::COMPONENT_UTILS); else - JS_GC(mCx); + js::GCForReason(mCx, js::gcreason::COMPONENT_UTILS); mCallback->Callback(); return NS_OK; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 692462de1f5b..63b84fde9f32 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1870,9 +1870,6 @@ AccumulateTelemetryCallback(int id, uint32_t sample) case JS_TELEMETRY_GC_IS_COMPARTMENTAL: Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample); break; - case JS_TELEMETRY_GC_IS_SHAPE_REGEN: - Telemetry::Accumulate(Telemetry::GC_IS_SHAPE_REGEN, sample); - break; case JS_TELEMETRY_GC_MS: Telemetry::Accumulate(Telemetry::GC_MS, sample); break; diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index d02af55cebc1..5c8f27e3a49f 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -365,7 +365,7 @@ nsXPConnect::NeedCollect() } void -nsXPConnect::Collect(bool shrinkingGC) +nsXPConnect::Collect(PRUint32 reason, PRUint32 kind) { // We're dividing JS objects into 2 categories: // @@ -424,16 +424,20 @@ nsXPConnect::Collect(bool shrinkingGC) // XPCCallContext::Init we disable the conservative scanner if that call // has started the request on this thread. js::AutoSkipConservativeScan ascs(cx); - if (shrinkingGC) - JS_ShrinkingGC(cx); - else - JS_GC(cx); + MOZ_ASSERT(reason < js::gcreason::NUM_REASONS); + js::gcreason::Reason gcreason = (js::gcreason::Reason)reason; + if (kind == nsGCShrinking) { + js::ShrinkingGC(cx, gcreason); + } else { + MOZ_ASSERT(kind == nsGCNormal); + js::GCForReason(cx, gcreason); + } } NS_IMETHODIMP -nsXPConnect::GarbageCollect(bool shrinkingGC) +nsXPConnect::GarbageCollect(PRUint32 reason, PRUint32 kind) { - Collect(shrinkingGC); + Collect(reason, kind); return NS_OK; } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index f54f54c24012..d4cc30b708b0 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -549,7 +549,7 @@ public: virtual nsresult FinishCycleCollection(); virtual nsCycleCollectionParticipant *ToParticipant(void *p); virtual bool NeedCollect(); - virtual void Collect(bool shrinkingGC=false); + virtual void Collect(PRUint32 reason, PRUint32 kind); #ifdef DEBUG_CC virtual void PrintAllReferencesTo(void *p); #endif diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 486e5415ff19..bc0eb1673742 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -198,6 +198,8 @@ static const char sPrintOptionsContractID[] = "@mozilla.org/gfx/printset #include "mozilla/dom/Element.h" +#include "jsfriendapi.h" + using namespace mozilla; #ifdef NS_DEBUG @@ -1287,7 +1289,7 @@ DocumentViewerImpl::PageHide(bool aIsUnload) if (aIsUnload) { // Poke the GC. The window might be collectable garbage now. - nsJSContext::PokeGC(); + nsJSContext::PokeGC(js::gcreason::PAGE_HIDE); // if Destroy() was called during OnPageHide(), mDocument is nsnull. NS_ENSURE_STATE(mDocument); diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index 4b3feb5a8f71..3e7f7701c481 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -75,7 +75,6 @@ HISTOGRAM(CYCLE_COLLECTOR_TIME_BETWEEN, 1, 120, 50, EXPONENTIAL, "Time spent in */ HISTOGRAM(GC_REASON, 1, 20, 20, LINEAR, "Reason (enum value) for initiating a GC") HISTOGRAM_BOOLEAN(GC_IS_COMPARTMENTAL, "Is it a compartmental GC?") -HISTOGRAM_BOOLEAN(GC_IS_SHAPE_REGEN, "Is it a shape regenerating GC?") HISTOGRAM(GC_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC (ms)") HISTOGRAM(GC_MARK_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC mark phase (ms)") HISTOGRAM(GC_SWEEP_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC sweep phase (ms)") diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 6fd9b9b3ed78..d9aad7aa86c6 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -2823,7 +2823,7 @@ nsCycleCollector::GCIfNeeded(bool aForceGC) // rt->Collect() must be called from the main thread, // because it invokes XPCJSRuntime::GCCallback(cx, JSGC_BEGIN) // which returns false if not in the main thread. - rt->Collect(); + rt->Collect(js::gcreason::CC_FORCED, nsGCNormal); #ifdef COLLECT_TIME_DEBUG printf("cc: GC() took %lldms\n", (PR_Now() - start) / PR_USEC_PER_MSEC); #endif diff --git a/xpcom/base/nsCycleCollector.h b/xpcom/base/nsCycleCollector.h index 7dabe60e5dab..61a258355c2d 100644 --- a/xpcom/base/nsCycleCollector.h +++ b/xpcom/base/nsCycleCollector.h @@ -106,9 +106,10 @@ struct nsCycleCollectionJSRuntime : public nsCycleCollectionLanguageRuntime virtual bool NeedCollect() = 0; /** - * Runs the JavaScript GC. + * Runs the JavaScript GC. |reason| is a gcreason::Reason from jsfriendapi.h. + * |kind| is a nsGCType from nsIXPConnect.idl. */ - virtual void Collect(bool shrinkingGC = false) = 0; + virtual void Collect(PRUint32 reason, PRUint32 kind) = 0; }; #ifdef DEBUG