Bug 1615988 - Fix memory reporting of wasm memory. r=sfink

This commit makes several tweaks to memory reporting for wasm memory.
  * Add a field for shared wasm memory and track it in SAB.
    - Before this commit, shared wasm memory wouldn't report its guard pages but should.
  * Track wasm guard pages in RuntimeSizes, not ClassInfo
    - We want wasm guard pages to show as a top-level memory report item, similar to vmem,
      and not under the owning array object. Displaying under the owning object bloats the
      tree of memory usage with large amounts of memory that's only reserved and not
      committed, which is confusing. Before this commit the class info reporter would
      try to make this happen, but this approach was broken and the simplest fix is to
      track this on RuntimeSizes and report the value from their.
  * Only add wasm memory if the buffer is attached. Detached buffers may still be live
    but no longer own the wasm heap and shouldn't report their old size.

Differential Revision: https://phabricator.services.mozilla.com/D124390
This commit is contained in:
Ryan Hunt 2021-09-07 21:03:48 +00:00
Родитель 49f844414e
Коммит c871f74315
9 изменённых файлов: 58 добавлений и 27 удалений

Просмотреть файл

@ -192,6 +192,7 @@ struct ClassInfo {
MACRO(Objects, NonHeap, objectsNonHeapElementsNormal) \
MACRO(Objects, NonHeap, objectsNonHeapElementsShared) \
MACRO(Objects, NonHeap, objectsNonHeapElementsWasm) \
MACRO(Objects, NonHeap, objectsNonHeapElementsWasmShared) \
MACRO(Objects, NonHeap, objectsNonHeapCodeWasm)
ClassInfo() = default;
@ -225,8 +226,6 @@ struct ClassInfo {
FOR_EACH_SIZE(DECL_SIZE_ZERO);
size_t wasmGuardPages = 0;
#undef FOR_EACH_SIZE
};
@ -512,6 +511,7 @@ struct RuntimeSizes {
MACRO(_, MallocHeap, scriptData) \
MACRO(_, MallocHeap, tracelogger) \
MACRO(_, MallocHeap, wasmRuntime) \
MACRO(_, Ignore, wasmGuardPages) \
MACRO(_, MallocHeap, jitLazyLink)
RuntimeSizes() { allScriptSources.emplace(); }

Просмотреть файл

@ -1537,7 +1537,8 @@ ArrayBufferObject::extractStructuredCloneContents(
/* static */
void ArrayBufferObject::addSizeOfExcludingThis(
JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info) {
JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info,
JS::RuntimeSizes* runtimeSizes) {
auto& buffer = obj->as<ArrayBufferObject>();
switch (buffer.bufferKind()) {
case INLINE_DATA:
@ -1568,9 +1569,14 @@ void ArrayBufferObject::addSizeOfExcludingThis(
info->objectsNonHeapElementsNormal += buffer.byteLength();
break;
case WASM:
info->objectsNonHeapElementsWasm += buffer.byteLength();
MOZ_ASSERT(buffer.wasmMappedSize() >= buffer.byteLength());
info->wasmGuardPages += buffer.wasmMappedSize() - buffer.byteLength();
if (!buffer.isDetached()) {
info->objectsNonHeapElementsWasm += buffer.byteLength();
if (runtimeSizes) {
MOZ_ASSERT(buffer.wasmMappedSize() >= buffer.byteLength());
runtimeSizes->wasmGuardPages +=
buffer.wasmMappedSize() - buffer.byteLength();
}
}
break;
case BAD1:
MOZ_CRASH("bad bufferKind()");

Просмотреть файл

@ -384,7 +384,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared {
static void addSizeOfExcludingThis(JSObject* obj,
mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo* info);
JS::ClassInfo* info,
JS::RuntimeSizes* runtimeSizes);
// ArrayBufferObjects (strongly) store the first view added to them, while
// later views are (weakly) stored in the compartment's InnerViewTable

Просмотреть файл

@ -3522,7 +3522,8 @@ js::gc::AllocKind JSObject::allocKindForTenure(
}
void JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo* info) {
JS::ClassInfo* info,
JS::RuntimeSizes* runtimeSizes) {
if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots()) {
info->objectsMallocHeapSlots +=
mallocSizeOf(as<NativeObject>().getSlotsHeader());
@ -3563,9 +3564,10 @@ void JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
info->objectsMallocHeapMisc +=
as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
} else if (is<ArrayBufferObject>()) {
ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info,
runtimeSizes);
} else if (is<SharedArrayBufferObject>()) {
SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info, runtimeSizes);
} else if (is<GlobalObject>()) {
as<GlobalObject>().addSizeOfData(mallocSizeOf, info);
} else if (is<WeakCollectionObject>()) {
@ -3617,7 +3619,7 @@ JS::ubi::Node::Size JS::ubi::Concrete<JSObject>::size(
}
JS::ClassInfo info;
obj.addSizeOfExcludingThis(mallocSizeOf, &info);
obj.addSizeOfExcludingThis(mallocSizeOf, &info, nullptr);
return obj.tenuredSizeOfThis() + info.sizeOfAllThings();
}

Просмотреть файл

@ -299,7 +299,8 @@ class JSObject
}
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo* info);
JS::ClassInfo* info,
JS::RuntimeSizes* runtimeSizes);
// We can only use addSizeOfExcludingThis on tenured objects: it assumes it
// can apply mallocSizeOf to bits and pieces of the object, whereas objects

Просмотреть файл

@ -340,7 +340,8 @@ static void StatsCellCallback(JSRuntime* rt, void* data, JS::GCCellPtr cellptr,
info.objectsGCHeap += Nursery::nurseryCellHeaderSize();
}
obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info);
obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &info,
&rtStats->runtime);
// These classes require special handling due to shared resources which
// we must be careful not to report twice.

Просмотреть файл

@ -347,7 +347,8 @@ void SharedArrayBufferObject::Finalize(JSFreeOp* fop, JSObject* obj) {
/* static */
void SharedArrayBufferObject::addSizeOfExcludingThis(
JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info) {
JSObject* obj, mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info,
JS::RuntimeSizes* runtimeSizes) {
// 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
@ -355,8 +356,17 @@ void SharedArrayBufferObject::addSizeOfExcludingThis(
// 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();
size_t owned = buf.byteLength() / buf.rawBufferObject()->refcount();
if (buf.isWasm()) {
info->objectsNonHeapElementsWasmShared += owned;
if (runtimeSizes) {
size_t ownedGuardPages = (buf.wasmMappedSize() - buf.byteLength()) /
buf.rawBufferObject()->refcount();
runtimeSizes->wasmGuardPages += ownedGuardPages;
}
} else {
info->objectsNonHeapElementsShared += owned;
}
}
/* static */

Просмотреть файл

@ -227,7 +227,8 @@ class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared {
static void addSizeOfExcludingThis(JSObject* obj,
mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo* info);
JS::ClassInfo* info,
JS::RuntimeSizes* runtimeSizes);
static void copyData(Handle<SharedArrayBufferObject*> toBuffer,
size_t toIndex,

Просмотреть файл

@ -1732,22 +1732,21 @@ static void ReportClassStats(const ClassInfo& classInfo, const nsACString& path,
"wasm/asm.js array buffer elements allocated outside both the "
"malloc heap and the GC heap.");
}
if (classInfo.objectsNonHeapElementsWasmShared > 0) {
REPORT_BYTES(
path + "objects/non-heap/elements/wasm-shared"_ns, KIND_NONHEAP,
classInfo.objectsNonHeapElementsWasmShared,
"wasm/asm.js array buffer elements allocated outside both the "
"malloc heap and the GC heap. These elements are shared between "
"one or more runtimes; the reported size is divided by the "
"buffer's refcount.");
}
if (classInfo.objectsNonHeapCodeWasm > 0) {
REPORT_BYTES(path + "objects/non-heap/code/wasm"_ns, KIND_NONHEAP,
classInfo.objectsNonHeapCodeWasm,
"AOT-compiled wasm/asm.js code.");
}
// Although wasm guard pages aren't committed in memory they can be very
// large and contribute greatly to vsize and so are worth reporting.
if (classInfo.wasmGuardPages > 0) {
REPORT_BYTES(
"wasm-guard-pages"_ns, KIND_OTHER, classInfo.wasmGuardPages,
"Guard pages mapped after the end of wasm memories, reserved for "
"optimization tricks, but not committed and thus never contributing"
" to RSS, only vsize.");
}
}
static void ReportRealmStats(const JS::RealmStats& realmStats,
@ -2330,6 +2329,16 @@ void JSReporter::CollectReports(WindowPaths* windowPaths,
REPORT_BYTES("wasm-runtime"_ns, KIND_OTHER, rtStats.runtime.wasmRuntime,
"The memory used for wasm runtime bookkeeping.");
// Although wasm guard pages aren't committed in memory they can be very
// large and contribute greatly to vsize and so are worth reporting.
if (rtStats.runtime.wasmGuardPages > 0) {
REPORT_BYTES(
"wasm-guard-pages"_ns, KIND_OTHER, rtStats.runtime.wasmGuardPages,
"Guard pages mapped after the end of wasm memories, reserved for "
"optimization tricks, but not committed and thus never contributing"
" to RSS, only vsize.");
}
// Report the numbers for memory outside of realms.
REPORT_BYTES("js-main-runtime/gc-heap/unused-chunks"_ns, KIND_OTHER,