Bug 1244841 - Don't measure SharedArrayBuffer objects multiple times. r=lth.
authorNicholas Nethercote <nnethercote@mozilla.com>
Fri, 12 Feb 2016 09:18:46 +1100
changeset 330565 9b7c58c2ea3e3c215ae299a1ccc1a1f96d19a50f
parent 330564 67f91747f48e531ecfc5d0974b794d9658c0974a
child 330566 7ecd788a20f1de92f075b3984406c7f556e1b473
push id10773
push userjyavenard@mozilla.com
push dateThu, 11 Feb 2016 23:46:51 +0000
reviewerslth
bugs1244841
milestone47.0a1
Bug 1244841 - Don't measure SharedArrayBuffer objects multiple times. r=lth. Redoes object element measurement and reporting: - Adds "non-heap/elements/shared", which reports a (size / refcount) measurement. Previously these measurements went into "non-heap/elements/mapped" and the full size would be erroneously reported for every thread sharing the buffer. - Renames "non-heap/elements/mapped" as "non-heap/elements/normal". - Renames "malloc-heap/elements/non-asm.js" as "malloc-heap/elements/normal". - Leaves "{malloc,non}-heap/elements/asm.js" unchanged.
js/public/MemoryMetrics.h
js/src/jsobj.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedArrayObject.h
js/xpconnect/src/XPCJSRuntime.cpp
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -161,20 +161,21 @@ struct CStringHashPolicy
 
 namespace JS {
 
 struct ClassInfo
 {
 #define FOR_EACH_SIZE(macro) \
     macro(Objects, GCHeapUsed, objectsGCHeap) \
     macro(Objects, MallocHeap, objectsMallocHeapSlots) \
-    macro(Objects, MallocHeap, objectsMallocHeapElementsNonAsmJS) \
+    macro(Objects, MallocHeap, objectsMallocHeapElementsNormal) \
     macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \
+    macro(Objects, NonHeap,    objectsNonHeapElementsNormal) \
     macro(Objects, NonHeap,    objectsNonHeapElementsAsmJS) \
-    macro(Objects, NonHeap,    objectsNonHeapElementsMapped) \
+    macro(Objects, NonHeap,    objectsNonHeapElementsShared) \
     macro(Objects, NonHeap,    objectsNonHeapCodeAsmJS) \
     macro(Objects, MallocHeap, objectsMallocHeapMisc) \
     \
     macro(Other,   GCHeapUsed, shapesGCHeapTree) \
     macro(Other,   GCHeapUsed, shapesGCHeapDict) \
     macro(Other,   GCHeapUsed, shapesGCHeapBase) \
     macro(Other,   MallocHeap, shapesMallocHeapTreeTables) \
     macro(Other,   MallocHeap, shapesMallocHeapDictTables) \
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3699,17 +3699,17 @@ void
 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info)
 {
     if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots())
         info->objectsMallocHeapSlots += mallocSizeOf(as<NativeObject>().slots_);
 
     if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
         js::ObjectElements* elements = as<NativeObject>().getElementsHeader();
         if (!elements->isCopyOnWrite() || elements->ownerObject() == this)
-            info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(elements);
+            info->objectsMallocHeapElementsNormal += mallocSizeOf(elements);
     }
 
     // Other things may be measured in the future if DMD indicates it is worthwhile.
     if (is<JSFunction>() ||
         is<PlainObject>() ||
         is<ArrayObject>() ||
         is<CallObject>() ||
         is<RegExpObject>() ||
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -749,20 +749,20 @@ ArrayBufferObject::addSizeOfExcludingThi
 {
     ArrayBufferObject& buffer = AsArrayBuffer(obj);
 
     if (!buffer.ownsData())
         return;
 
     switch (buffer.bufferKind()) {
       case PLAIN:
-        info->objectsMallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer());
+        info->objectsMallocHeapElementsNormal += mallocSizeOf(buffer.dataPointer());
         break;
       case MAPPED:
-        info->objectsNonHeapElementsMapped += buffer.byteLength();
+        info->objectsNonHeapElementsNormal += buffer.byteLength();
         break;
       case WASM_MALLOCED:
         info->objectsMallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
         break;
       case WASM_MAPPED:
         info->objectsNonHeapElementsAsmJS += buffer.byteLength();
         break;
     }
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -159,25 +159,25 @@ SharedArrayRawBuffer::New(JSContext* cx,
     SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(buffer, length);
     MOZ_ASSERT(rawbuf->length == length); // Deallocation needs this
     return rawbuf;
 }
 
 void
 SharedArrayRawBuffer::addReference()
 {
-    MOZ_ASSERT(this->refcount > 0);
-    ++this->refcount; // Atomic.
+    MOZ_ASSERT(this->refcount_ > 0);
+    ++this->refcount_; // Atomic.
 }
 
 void
 SharedArrayRawBuffer::dropReference()
 {
     // Drop the reference to the buffer.
-    uint32_t refcount = --this->refcount; // Atomic.
+    uint32_t refcount = --this->refcount_; // Atomic.
 
     // If this was the final reference, release the buffer.
     if (refcount == 0) {
         SharedMem<uint8_t*> p = this->dataPointerShared() - gc::SystemPageSize();
 
         MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
 
         uint8_t* address = p.unwrap(/*safe - only reference*/);
@@ -325,17 +325,25 @@ SharedArrayBufferObject::Finalize(FreeOp
         buf.dropRawBuffer();
     }
 }
 
 /* static */ void
 SharedArrayBufferObject::addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
                                                 JS::ClassInfo* info)
 {
-    info->objectsNonHeapElementsMapped += obj->as<SharedArrayBufferObject>().byteLength();
+    // Divide the buffer size by the refcount to get the fraction of the buffer
+    // owned by this thread. It's conceivable that the refcount might change in
+    // the middle of memory reporting, in which case the amount reported for
+    // some threads might be to high (if the refcount goes up) or too low (if
+    // the refcount goes down). But that's unlikely and hard to avoid, so we
+    // just live with the risk.
+    const SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
+    info->objectsNonHeapElementsShared +=
+        buf.byteLength() / buf.rawBufferObject()->refcount();
 }
 
 const Class SharedArrayBufferObject::protoClass = {
     "SharedArrayBufferPrototype",
     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer)
 };
 
 const Class SharedArrayBufferObject::class_ = {
--- a/js/src/vm/SharedArrayObject.h
+++ b/js/src/vm/SharedArrayObject.h
@@ -39,26 +39,26 @@ class FutexWaiter;
  * shared memory.  (That would get rid of ~4KB of waste, as well.)  Very little
  * else would have to change throughout the engine, the SARB would point to
  * the data array using a constant pointer, instead of computing its
  * address.
  */
 class SharedArrayRawBuffer
 {
   private:
-    mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount;
+    mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_;
     uint32_t length;
 
     // A list of structures representing tasks waiting on some
     // location within this buffer.
     FutexWaiter* waiters_;
 
   protected:
     SharedArrayRawBuffer(uint8_t* buffer, uint32_t length)
-      : refcount(1),
+      : refcount_(1),
         length(length),
         waiters_(nullptr)
     {
         MOZ_ASSERT(buffer == dataPointerShared());
     }
 
   public:
     static SharedArrayRawBuffer* New(JSContext* cx, uint32_t length);
@@ -79,16 +79,18 @@ class SharedArrayRawBuffer
         uint8_t* ptr = reinterpret_cast<uint8_t*>(const_cast<SharedArrayRawBuffer*>(this));
         return SharedMem<uint8_t*>::shared(ptr + sizeof(SharedArrayRawBuffer));
     }
 
     uint32_t byteLength() const {
         return length;
     }
 
+    uint32_t refcount() const { return refcount_; }
+
     void addReference();
     void dropReference();
 };
 
 /*
  * SharedArrayBufferObject
  *
  * When transferred to a WebWorker, the buffer is not detached on the
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2155,20 +2155,20 @@ ReportClassStats(const ClassInfo& classI
     }
 
     if (classInfo.objectsMallocHeapSlots > 0) {
         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
             KIND_HEAP, classInfo.objectsMallocHeapSlots,
             "Non-fixed object slots.");
     }
 
-    if (classInfo.objectsMallocHeapElementsNonAsmJS > 0) {
-        REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/non-asm.js"),
-            KIND_HEAP, classInfo.objectsMallocHeapElementsNonAsmJS,
-            "Non-asm.js indexed elements.");
+    if (classInfo.objectsMallocHeapElementsNormal > 0) {
+        REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/normal"),
+            KIND_HEAP, classInfo.objectsMallocHeapElementsNormal,
+            "Normal (non-asm.js) indexed elements.");
     }
 
     // asm.js arrays are heap-allocated on some platforms and
     // non-heap-allocated on others.  We never put them under sundries,
     // because (a) in practice they're almost always larger than the sundries
     // threshold, and (b) we'd need a third category of sundries ("non-heap"),
     // which would be a pain.
     size_t mallocHeapElementsAsmJS = classInfo.objectsMallocHeapElementsAsmJS;
@@ -2181,20 +2181,28 @@ ReportClassStats(const ClassInfo& classI
     }
     if (nonHeapElementsAsmJS > 0) {
         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/asm.js"),
             KIND_NONHEAP, nonHeapElementsAsmJS,
             "asm.js array buffer elements outside both the malloc heap and "
             "the GC heap.");
     }
 
-    if (classInfo.objectsNonHeapElementsMapped > 0) {
-        REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/mapped"),
-            KIND_NONHEAP, classInfo.objectsNonHeapElementsMapped,
-            "Memory-mapped array buffer elements.");
+    if (classInfo.objectsNonHeapElementsNormal > 0) {
+        REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/normal"),
+            KIND_NONHEAP, classInfo.objectsNonHeapElementsNormal,
+            "Memory-mapped non-shared array buffer elements.");
+    }
+
+    if (classInfo.objectsNonHeapElementsShared > 0) {
+        REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/shared"),
+            KIND_NONHEAP, classInfo.objectsNonHeapElementsShared,
+            "Memory-mapped shared array buffer elements. These elements are "
+            "shared between one or more runtimes; the reported size is divided "
+            "by the buffer's refcount.");
     }
 
     if (classInfo.objectsNonHeapCodeAsmJS > 0) {
         REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/code/asm.js"),
             KIND_NONHEAP, classInfo.objectsNonHeapCodeAsmJS,
             "AOT-compiled asm.js code.");
     }