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.
--- 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.");
}