Bug 1268992 - Assert that the heap is empty after a shutdown GC; r=jonco
authorTerrence Cole <terrence@mozilla.com>
Tue, 26 Apr 2016 15:25:29 -0700
changeset 297287 d82860087d47d10791757d56e289076800318ae1
parent 297286 3e1834b1d9ff2f6126557a563b1adec92c1e3798
child 297288 59dbcba81f37ad82f80bc6eab6197ccb310a44e7
push id19206
push usercbook@mozilla.com
push dateFri, 13 May 2016 10:06:58 +0000
treeherderfx-team@5a2deb5a9b09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1268992
milestone49.0a1
Bug 1268992 - Assert that the heap is empty after a shutdown GC; r=jonco
dom/base/nsXMLHttpRequest.h
js/src/gc/GCRuntime.h
js/src/gc/Heap.h
js/src/jsgc.cpp
js/src/jsgc.h
--- a/dom/base/nsXMLHttpRequest.h
+++ b/dom/base/nsXMLHttpRequest.h
@@ -136,17 +136,17 @@ public:
 
   IMPL_EVENT_HANDLER(loadstart)
   IMPL_EVENT_HANDLER(progress)
   IMPL_EVENT_HANDLER(abort)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(load)
   IMPL_EVENT_HANDLER(timeout)
   IMPL_EVENT_HANDLER(loadend)
-  
+
   virtual void DisconnectFromOwner() override;
 };
 
 class nsXMLHttpRequestUpload final : public nsXHREventTarget,
                                      public nsIXMLHttpRequestUpload
 {
 public:
   explicit nsXMLHttpRequestUpload(mozilla::DOMEventTargetHelper* aOwner)
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -802,18 +802,16 @@ class GCRuntime
     void incMajorGcNumber() { ++majorGCNumber; ++number; }
 
     int64_t defaultSliceBudget() const { return defaultTimeBudget_; }
 
     bool isIncrementalGc() const { return isIncremental; }
     bool isFullGc() const { return isFull; }
     bool isCompactingGc() const { return isCompacting; }
 
-    bool shouldCleanUpEverything() { return cleanUpEverything; }
-
     bool areGrayBitsValid() const { return grayBitsValid; }
     void setGrayBitsInvalid() { grayBitsValid = false; }
 
     bool minorGCRequested() const { return minorGCTriggerReason != JS::gcreason::NO_REASON; }
     bool majorGCRequested() const { return majorGCTriggerReason != JS::gcreason::NO_REASON; }
     bool isGcNeeded() { return minorGCRequested() || majorGCRequested(); }
 
     bool fullGCForAtomsRequested() const { return fullGCForAtomsRequested_; }
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -108,16 +108,44 @@ enum class AllocKind {
     STRING,
     EXTERNAL_STRING,
     SYMBOL,
     JITCODE,
     LIMIT,
     LAST = LIMIT - 1
 };
 
+#define FOR_EACH_ALLOCKIND(D) \
+ /* PrettyName             TypeName */ \
+    D(FUNCTION,            JSFunction) \
+    D(FUNCTION_EXTENDED,   JSFunction) \
+    D(OBJECT0,             JSObject) \
+    D(OBJECT0_BACKGROUND,  JSObject) \
+    D(OBJECT2,             JSObject) \
+    D(OBJECT2_BACKGROUND,  JSObject) \
+    D(OBJECT4,             JSObject) \
+    D(OBJECT4_BACKGROUND,  JSObject) \
+    D(OBJECT8,             JSObject) \
+    D(OBJECT8_BACKGROUND,  JSObject) \
+    D(OBJECT12,            JSObject) \
+    D(OBJECT12_BACKGROUND, JSObject) \
+    D(OBJECT16,            JSObject) \
+    D(OBJECT16_BACKGROUND, JSObject) \
+    D(SCRIPT,              JSScript) \
+    D(LAZY_SCRIPT,         js::LazyScript) \
+    D(SHAPE,               js::Shape) \
+    D(ACCESSOR_SHAPE,      js::AccessorShape) \
+    D(BASE_SHAPE,          js::BaseShape) \
+    D(OBJECT_GROUP,        js::ObjectGroup) \
+    D(FAT_INLINE_STRING,   JSFatInlineString) \
+    D(STRING,              JSString) \
+    D(EXTERNAL_STRING,     JSExternalString) \
+    D(SYMBOL,              JS::Symbol) \
+    D(JITCODE,             js::JitCode)
+
 static_assert(int(AllocKind::FIRST) == 0, "Various places depend on AllocKind starting at 0, "
                                           "please audit them carefully!");
 static_assert(int(AllocKind::OBJECT_FIRST) == 0, "Various places depend on AllocKind::OBJECT_FIRST "
                                                  "being 0, please audit them carefully!");
 
 inline bool
 IsObjectAllocKind(AllocKind kind)
 {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3883,33 +3883,71 @@ GCRuntime::sweepZones(FreeOp* fop, bool 
         Zone* zone = *read++;
 
         if (zone->wasGCStarted()) {
             MOZ_ASSERT(!zone->isQueuedForBackgroundSweep());
             const bool zoneIsDead = zone->arenas.arenaListsAreEmpty() &&
                                     !zone->hasMarkedCompartments();
             if (zoneIsDead || destroyingRuntime)
             {
+                // We have just finished sweeping, so we should have freed any
+                // empty arenas back to their Chunk for future allocation.
                 zone->arenas.checkEmptyFreeLists();
 
+                // We are about to delete the Zone; this will leave the Zone*
+                // in the arena header dangling if there are any arenas
+                // remaining at this point.
+                zone->arenas.checkEmptyArenaLists();
+
                 if (callback)
                     callback(zone);
 
                 zone->sweepCompartments(fop, false, destroyingRuntime);
                 MOZ_ASSERT(zone->compartments.empty());
                 fop->delete_(zone);
                 continue;
             }
             zone->sweepCompartments(fop, true, destroyingRuntime);
         }
         *write++ = zone;
     }
     zones.shrinkTo(write - zones.begin());
 }
 
+#ifdef DEBUG
+static const char*
+AllocKindToAscii(AllocKind kind)
+{
+    switch(kind) {
+#define MAKE_CASE(name, _) case AllocKind:: name: return #name;
+      FOR_EACH_ALLOCKIND(MAKE_CASE)
+#undef MAKE_CASE
+      case AllocKind::LIMIT: MOZ_FALLTHROUGH;
+      default: MOZ_CRASH("Unknown AllocKind in AllocKindToAscii");
+    }
+}
+#endif // DEBUG
+
+void
+ArenaLists::checkEmptyArenaList(AllocKind kind)
+{
+#ifdef DEBUG
+    if (!arenaLists[kind].isEmpty()) {
+        for (Arena* current = arenaLists[kind].head(); current; current = current->next) {
+            for (ArenaCellIterUnderFinalize i(current); !i.done(); i.next()) {
+                Cell* t = i.get<Cell>();
+                MOZ_ASSERT(t->asTenured().isMarked(), "unmarked cells should have been finalized");
+                fprintf(stderr, "ERROR: GC found live Cell %p of kind %s at shutdown\n",
+                        t, AllocKindToAscii(kind));
+            }
+        }
+    }
+#endif // DEBUG
+}
+
 void
 GCRuntime::purgeRuntime()
 {
     for (GCCompartmentsIter comp(rt); !comp.done(); comp.next())
         comp->purge();
 
     freeUnusedLifoBlocksAfterSweeping(&rt->tempLifoAlloc);
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -734,20 +734,29 @@ class ArenaLists
 
     void checkEmptyFreeLists() {
 #ifdef DEBUG
         for (auto i : AllAllocKinds())
             checkEmptyFreeList(i);
 #endif
     }
 
+    void checkEmptyArenaLists() {
+#ifdef DEBUG
+        for (auto i : AllAllocKinds())
+            checkEmptyArenaList(i);
+#endif
+    }
+
     void checkEmptyFreeList(AllocKind kind) {
         MOZ_ASSERT(freeLists[kind]->isEmpty());
     }
 
+    void checkEmptyArenaList(AllocKind kind);
+
     bool relocateArenas(Zone* zone, Arena*& relocatedListOut, JS::gcreason::Reason reason,
                         SliceBudget& sliceBudget, gcstats::Statistics& stats);
 
     void queueForegroundObjectsForSweep(FreeOp* fop);
     void queueForegroundThingsForSweep(FreeOp* fop);
 
     void mergeForegroundSweptObjectArenas();