Bug 1381058 - Split full store buffer reasons into a separate reason for each store buffer r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 17 Jul 2017 10:54:12 +0100
changeset 369253 4d1a13d4e94f1bdf46a99838cae84eb7beba0646
parent 369252 baa7949eb81f019e2caa44a8d7302a9c5a2208b8
child 369254 d0c73b22611c17a86a26c934077f542df3b375cf
push id46631
push userkwierso@gmail.com
push dateTue, 18 Jul 2017 00:38:28 +0000
treeherderautoland@216a5bf264b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1381058
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1381058 - Split full store buffer reasons into a separate reason for each store buffer r=sfink
js/public/GCAPI.h
js/src/builtin/TestingFunctions.cpp
js/src/gc/Nursery.cpp
js/src/gc/StoreBuffer.cpp
js/src/gc/StoreBuffer.h
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -60,39 +60,39 @@ namespace JS {
     D(LAST_DITCH)                               \
     D(TOO_MUCH_MALLOC)                          \
     D(ALLOC_TRIGGER)                            \
     D(DEBUG_GC)                                 \
     D(COMPARTMENT_REVIVED)                      \
     D(RESET)                                    \
     D(OUT_OF_NURSERY)                           \
     D(EVICT_NURSERY)                            \
-    D(FULL_STORE_BUFFER)                        \
+    D(UNUSED0)                                  \
     D(SHARED_MEMORY_LIMIT)                      \
     D(UNUSED1)                                  \
     D(INCREMENTAL_TOO_SLOW)                     \
     D(ABORT_GC)                                 \
+    D(FULL_WHOLE_CELL_BUFFER)                   \
+    D(FULL_GENERIC_BUFFER)                      \
+    D(FULL_VALUE_BUFFER)                        \
+    D(FULL_CELL_PTR_BUFFER)                     \
+    D(FULL_SLOT_BUFFER)                         \
                                                 \
     /* These are reserved for future use. */    \
     D(RESERVED0)                                \
     D(RESERVED1)                                \
     D(RESERVED2)                                \
     D(RESERVED3)                                \
     D(RESERVED4)                                \
     D(RESERVED5)                                \
     D(RESERVED6)                                \
     D(RESERVED7)                                \
     D(RESERVED8)                                \
     D(RESERVED9)                                \
     D(RESERVED10)                               \
-    D(RESERVED11)                               \
-    D(RESERVED12)                               \
-    D(RESERVED13)                               \
-    D(RESERVED14)                               \
-    D(RESERVED15)                               \
                                                 \
     /* Reasons from Firefox */                  \
     D(DOM_WINDOW_UTILS)                         \
     D(COMPONENT_UTILS)                          \
     D(MEM_PRESSURE)                             \
     D(CC_WAITING)                               \
     D(CC_FORCED)                                \
     D(LOAD_END)                                 \
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -333,17 +333,17 @@ GC(JSContext* cx, unsigned argc, Value* 
     return true;
 }
 
 static bool
 MinorGC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.get(0) == BooleanValue(true))
-        cx->zone()->group()->storeBuffer().setAboutToOverflow();
+        cx->zone()->group()->storeBuffer().setAboutToOverflow(JS::gcreason::FULL_GENERIC_BUFFER);
 
     cx->minorGC(JS::gcreason::API);
     args.rval().setUndefined();
     return true;
 }
 
 #define FOR_EACH_GC_PARAM(_)                                                    \
     _("maxBytes",                   JSGC_MAX_BYTES,                      true)  \
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -546,16 +546,26 @@ js::Nursery::startProfile(ProfileKey key
 
 inline void
 js::Nursery::endProfile(ProfileKey key)
 {
     profileDurations_[key] = TimeStamp::Now() - startTimes_[key];
     totalDurations_[key] += profileDurations_[key];
 }
 
+static inline bool
+IsFullStoreBufferReason(JS::gcreason::Reason reason)
+{
+    return reason == JS::gcreason::FULL_WHOLE_CELL_BUFFER ||
+           reason == JS::gcreason::FULL_GENERIC_BUFFER ||
+           reason == JS::gcreason::FULL_VALUE_BUFFER ||
+           reason == JS::gcreason::FULL_CELL_PTR_BUFFER ||
+           reason == JS::gcreason::FULL_SLOT_BUFFER;
+}
+
 void
 js::Nursery::collect(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
 
     if (!isEnabled() || isEmpty()) {
         // Our barriers are not always exact, and there may be entries in the
         // storebuffer even when the nursery is disabled or empty. It's not safe
@@ -599,17 +609,17 @@ js::Nursery::collect(JS::gcreason::Reaso
     endProfile(ProfileKey::Resize);
 
     // If we are promoting the nursery, or exhausted the store buffer with
     // pointers to nursery things, which will force a collection well before
     // the nursery is full, look for object groups that are getting promoted
     // excessively and try to pretenure them.
     startProfile(ProfileKey::Pretenure);
     uint32_t pretenureCount = 0;
-    if (promotionRate > 0.8 || reason == JS::gcreason::FULL_STORE_BUFFER) {
+    if (promotionRate > 0.8 || IsFullStoreBufferReason(reason)) {
         JSContext* cx = TlsContext.get();
         for (auto& entry : tenureCounts.entries) {
             if (entry.count >= 3000) {
                 ObjectGroup* group = entry.group;
                 if (group->canPreTenure()) {
                     AutoCompartment ac(cx, group);
                     group->setShouldPreTenure(cx);
                     pretenureCount++;
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -80,23 +80,23 @@ StoreBuffer::clear()
     bufferGeneric.clear();
 
     for (ArenaCellSet* set = bufferWholeCell; set; set = set->next)
         set->arena->bufferedCells() = nullptr;
     bufferWholeCell = nullptr;
 }
 
 void
-StoreBuffer::setAboutToOverflow()
+StoreBuffer::setAboutToOverflow(JS::gcreason::Reason reason)
 {
     if (!aboutToOverflow_) {
         aboutToOverflow_ = true;
         runtime_->gc.stats().count(gcstats::STAT_STOREBUFFER_OVERFLOW);
     }
-    nursery_.requestMinorGC(JS::gcreason::FULL_STORE_BUFFER);
+    nursery_.requestMinorGC(reason);
 }
 
 void
 StoreBuffer::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::GCSizes
 *sizes)
 {
     sizes->storeBufferVals       += bufferVal.sizeOfExcludingThis(mallocSizeOf);
     sizes->storeBufferCells      += bufferCell.sizeOfExcludingThis(mallocSizeOf);
@@ -133,17 +133,17 @@ js::gc::AllocateWholeCellSet(Arena* aren
     Nursery& nursery = zone->group()->nursery();
     void* data = nursery.allocateBuffer(zone, sizeof(ArenaCellSet));
     if (!data) {
         oomUnsafe.crash("Failed to allocate WholeCellSet");
         return nullptr;
     }
 
     if (nursery.freeSpace() < ArenaCellSet::NurseryFreeThresholdBytes)
-        zone->group()->storeBuffer().setAboutToOverflow();
+        zone->group()->storeBuffer().setAboutToOverflow(JS::gcreason::FULL_WHOLE_CELL_BUFFER);
 
     auto cells = static_cast<ArenaCellSet*>(data);
     new (cells) ArenaCellSet(arena);
     arena->bufferedCells() = cells;
     zone->group()->storeBuffer().addToWholeCellBuffer(cells);
     return cells;
 }
 
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -116,17 +116,17 @@ class StoreBuffer
             if (last_) {
                 AutoEnterOOMUnsafeRegion oomUnsafe;
                 if (!stores_.put(last_))
                     oomUnsafe.crash("Failed to allocate for MonoTypeBuffer::put.");
             }
             last_ = T();
 
             if (MOZ_UNLIKELY(stores_.count() > MaxEntries))
-                owner->setAboutToOverflow();
+                owner->setAboutToOverflow(T::FullBufferReason);
         }
 
         bool has(StoreBuffer* owner, const T& v) {
             sinkStore(owner);
             return stores_.has(v);
         }
 
         /* Trace the source of all edges in the store buffer. */
@@ -183,17 +183,17 @@ class StoreBuffer
                 oomUnsafe.crash("Failed to allocate for GenericBuffer::put.");
             *sizep = size;
 
             T* tp = storage_->new_<T>(t);
             if (!tp)
                 oomUnsafe.crash("Failed to allocate for GenericBuffer::put.");
 
             if (isAboutToOverflow())
-                owner->setAboutToOverflow();
+                owner->setAboutToOverflow(JS::gcreason::FULL_GENERIC_BUFFER);
         }
 
         size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
             return storage_ ? storage_->sizeOfIncludingThis(mallocSizeOf) : 0;
         }
 
         bool isEmpty() {
             return !storage_ || storage_->isEmpty();
@@ -229,16 +229,18 @@ class StoreBuffer
 
         CellPtrEdge tagged() const { return CellPtrEdge((Cell**)(uintptr_t(edge) | 1)); }
         CellPtrEdge untagged() const { return CellPtrEdge((Cell**)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
 
         explicit operator bool() const { return edge != nullptr; }
 
         typedef PointerEdgeHasher<CellPtrEdge> Hasher;
+
+        static const auto FullBufferReason = JS::gcreason::FULL_CELL_PTR_BUFFER;
     };
 
     struct ValueEdge
     {
         JS::Value* edge;
 
         ValueEdge() : edge(nullptr) {}
         explicit ValueEdge(JS::Value* v) : edge(v) {}
@@ -256,16 +258,18 @@ class StoreBuffer
 
         ValueEdge tagged() const { return ValueEdge((JS::Value*)(uintptr_t(edge) | 1)); }
         ValueEdge untagged() const { return ValueEdge((JS::Value*)(uintptr_t(edge) & ~1)); }
         bool isTagged() const { return bool(uintptr_t(edge) & 1); }
 
         explicit operator bool() const { return edge != nullptr; }
 
         typedef PointerEdgeHasher<ValueEdge> Hasher;
+
+        static const auto FullBufferReason = JS::gcreason::FULL_VALUE_BUFFER;
     };
 
     struct SlotsEdge
     {
         // These definitions must match those in HeapSlot::Kind.
         const static int SlotKind = 0;
         const static int ElementKind = 1;
 
@@ -333,16 +337,18 @@ class StoreBuffer
 
         explicit operator bool() const { return objectAndKind_ != 0; }
 
         typedef struct {
             typedef SlotsEdge Lookup;
             static HashNumber hash(const Lookup& l) { return l.objectAndKind_ ^ l.start_ ^ l.count_; }
             static bool match(const SlotsEdge& k, const Lookup& l) { return k == l; }
         } Hasher;
+
+        static const auto FullBufferReason = JS::gcreason::FULL_SLOT_BUFFER;
     };
 
     template <typename Buffer, typename Edge>
     void unput(Buffer& buffer, const Edge& edge) {
         MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy());
         MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
         if (!isEnabled())
             return;
@@ -426,17 +432,17 @@ class StoreBuffer
     void traceCells(TenuringTracer& mover)             { bufferCell.trace(this, mover); }
     void traceSlots(TenuringTracer& mover)             { bufferSlot.trace(this, mover); }
     void traceGenericEntries(JSTracer *trc)            { bufferGeneric.trace(this, trc); }
 
     void traceWholeCells(TenuringTracer& mover);
     void traceWholeCell(TenuringTracer& mover, JS::TraceKind kind, Cell* cell);
 
     /* For use by our owned buffers and for testing. */
-    void setAboutToOverflow();
+    void setAboutToOverflow(JS::gcreason::Reason);
 
     void addToWholeCellBuffer(ArenaCellSet* set);
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::GCSizes* sizes);
 };
 
 // A set of cells in an arena used to implement the whole cell store buffer.
 class ArenaCellSet