Bug 1395509 - Add APIs to track internal memory assocated with GC things r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 09 May 2019 13:13:09 +0100
changeset 535324 772b3ec0102d042221de27abe26841a08336f390
parent 535323 15583c97669a526418146062c36729ccb90c5609
child 535325 95399bf8d949c78b5d7d67019f203fea85fb755e
push id2082
push userffxbld-merge
push dateMon, 01 Jul 2019 08:34:18 +0000
treeherdermozilla-release@2fb19d0466d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1395509
milestone68.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 1395509 - Add APIs to track internal memory assocated with GC things r=sfink Differential Revision: https://phabricator.services.mozilla.com/D30514
js/src/gc/Scheduling.h
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jsapi.cpp
--- a/js/src/gc/Scheduling.h
+++ b/js/src/gc/Scheduling.h
@@ -308,18 +308,27 @@
 #define gc_Scheduling_h
 
 #include "mozilla/Atomics.h"
 
 #include "js/HashTable.h"
 
 namespace js {
 
-// This will eventually have internal reasons too.
-using JS::MemoryUse;
+#define JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
+
+#define JS_FOR_EACH_MEMORY_USE(_)               \
+  JS_FOR_EACH_PUBLIC_MEMORY_USE(_)              \
+  JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
+
+enum class MemoryUse : uint8_t {
+#define DEFINE_MEMORY_USE(Name) Name,
+  JS_FOR_EACH_MEMORY_USE(DEFINE_MEMORY_USE)
+#undef DEFINE_MEMORY_USE
+};
 
 namespace gc {
 
 struct Cell;
 
 enum TriggerKind { NoTrigger = 0, IncrementalTrigger, NonIncrementalTrigger };
 
 /*
@@ -657,23 +666,27 @@ class MemoryTracker {
  public:
 #ifdef DEBUG
   MemoryTracker();
   ~MemoryTracker();
   void fixupAfterMovingGC();
 #endif
 
   void addMemory(Cell* cell, size_t nbytes, MemoryUse use) {
+    MOZ_ASSERT(cell);
+    MOZ_ASSERT(nbytes);
 #ifdef DEBUG
     trackMemory(cell, nbytes, use);
 #endif
     MOZ_ASSERT(bytes_ + nbytes >= bytes_);
     bytes_ += nbytes;
   }
   void removeMemory(Cell* cell, size_t nbytes, MemoryUse use) {
+    MOZ_ASSERT(cell);
+    MOZ_ASSERT(nbytes);
 #ifdef DEBUG
     untrackMemory(cell, nbytes, use);
 #endif
     MOZ_ASSERT(bytes_ >= nbytes);
     bytes_ -= nbytes;
   }
 
   size_t bytes() const { return bytes_; }
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -521,72 +521,84 @@ void MemoryTracker::adopt(MemoryTracker&
     }
   }
   other.map.clear();
 #endif
 }
 
 #ifdef DEBUG
 
+static const char* MemoryUseName(MemoryUse use) {
+  switch (use) {
+#define DEFINE_CASE(Name) \
+    case MemoryUse::Name: return #Name;
+JS_FOR_EACH_MEMORY_USE(DEFINE_CASE)
+#undef DEFINE_CASE
+  }
+
+  MOZ_CRASH("Unknown memory use");
+}
+
 MemoryTracker::MemoryTracker() : mutex(mutexid::MemoryTracker) {}
 
 MemoryTracker::~MemoryTracker() {
   if (!TlsContext.get()->runtime()->gc.shutdownCollectedEverything()) {
     // Memory leak, suppress crashes.
     return;
   }
 
   if (map.empty()) {
     MOZ_ASSERT(bytes() == 0);
     return;
   }
 
   fprintf(stderr, "Missing calls to JS::RemoveAssociatedMemory:\n");
   for (auto r = map.all(); !r.empty(); r.popFront()) {
-    fprintf(stderr, "  %p 0x%zx %u\n", r.front().key().cell,
-            r.front().value(), unsigned(r.front().key().use));
+    fprintf(stderr, "  %p 0x%zx %s\n", r.front().key().cell,
+            r.front().value(),
+            MemoryUseName(r.front().key().use));
   }
 
   MOZ_CRASH();
 }
 
 void MemoryTracker::trackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
   MOZ_ASSERT(cell->isTenured());
 
   LockGuard<Mutex> lock(mutex);
 
   Key key{cell, use};
   AutoEnterOOMUnsafeRegion oomUnsafe;
   auto ptr = map.lookupForAdd(key);
   if (ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("Association already present: %p 0x%zx %u", cell,
-                            nbytes, unsigned(use));
+    MOZ_CRASH_UNSAFE_PRINTF("Association already present: %p 0x%zx %s", cell,
+                            nbytes, MemoryUseName(use));
   }
 
   if (!map.add(ptr, key, nbytes)) {
     oomUnsafe.crash("MemoryTracker::noteExternalAlloc");
   }
 }
 
 void MemoryTracker::untrackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
   MOZ_ASSERT(cell->isTenured());
 
   LockGuard<Mutex> lock(mutex);
 
   Key key{cell, use};
   auto ptr = map.lookup(key);
   if (!ptr) {
-    MOZ_CRASH_UNSAFE_PRINTF("Association not found: %p 0x%x %u", cell,
-                            unsigned(nbytes), unsigned(use));
+    MOZ_CRASH_UNSAFE_PRINTF("Association not found: %p 0x%x %s", cell,
+                            unsigned(nbytes), MemoryUseName(use));
   }
   if (ptr->value() != nbytes) {
     MOZ_CRASH_UNSAFE_PRINTF(
-        "Association for %p %u has different size: "
+        "Association for %p %s has different size: "
         "expected 0x%zx but got 0x%zx",
-        cell, unsigned(use), ptr->value(), nbytes);
+        cell, MemoryUseName(use), ptr->value(), nbytes);
   }
   map.remove(ptr);
 }
 
 void MemoryTracker::fixupAfterMovingGC() {
   // Update the table after we move GC things. We don't use MovableCellHasher
   // because that would create a difference between debug and release builds.
   for (Map::Enum e(map); !e.empty(); e.popFront()) {
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -448,17 +448,17 @@ class Zone : public JS::shadow::Zone,
   js::ZoneData<JS::WeakCache<TypeDescrObjectSet>> typeDescrObjects_;
 
   // Malloc counter to measure memory pressure for GC scheduling. This counter
   // is used for allocations where the size of the allocation is not known on
   // free. Currently this is used for all internal malloc allocations.
   js::gc::MemoryCounter gcMallocCounter;
 
   // Malloc counter used for allocations where size information is
-  // available. Currently this is used for external allocations only.
+  // available. Used for some internal and all tracked external allocations.
   js::gc::MemoryTracker gcMallocSize;
 
   // Counter of JIT code executable memory for GC scheduling. Also imprecise,
   // since wasm can generate code that outlives a zone.
   js::gc::MemoryCounter jitCodeCounter;
 
   void updateMemoryCounter(js::gc::MemoryCounter& counter, size_t nbytes) {
     JSRuntime* rt = runtimeFromAnyThread();
@@ -750,11 +750,42 @@ class ZoneAllocPolicy {
   }
   void reportAllocOverflow() const {}
 
   MOZ_MUST_USE bool checkSimulatedOOM() const {
     return !js::oom::ShouldFailWithOOM();
   }
 };
 
+// Convenience functions for memory accounting on the zone.
+
+// Associate malloc memory with a GC thing. This call must be matched by a
+// following call to RemoveCellMemory with the same size and use. The total
+// amount of malloc memory associated with a zone is used to trigger GC.
+inline void AddCellMemory(gc::TenuredCell* cell, size_t nbytes,
+                          MemoryUse use) {
+  if (nbytes) {
+    cell->zone()->addCellMemory(cell, nbytes, use);
+  }
+}
+inline void AddCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
+  if (cell->isTenured()) {
+    AddCellMemory(&cell->asTenured(), nbytes, use);
+  }
+}
+
+// Remove association between malloc memory and a GC thing. This call must
+// follow a call to AddCellMemory with the same size and use.
+inline void RemoveCellMemory(gc::TenuredCell* cell, size_t nbytes,
+                             MemoryUse use) {
+  if (nbytes) {
+    cell->zoneFromAnyThread()->removeCellMemory(cell, nbytes, use);
+  }
+}
+inline void RemoveCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
+  if (cell->isTenured()) {
+    RemoveCellMemory(&cell->asTenured(), nbytes, use);
+  }
+}
+
 }  // namespace js
 
 #endif  // gc_Zone_h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1157,28 +1157,28 @@ JS_PUBLIC_API void JS::AddAssociatedMemo
                                            JS::MemoryUse use) {
   MOZ_ASSERT(obj);
   if (!nbytes) {
     return;
   }
 
   Zone* zone = obj->zone();
   zone->updateMallocCounter(nbytes);
-  zone->addCellMemory(obj, nbytes, use);
+  zone->addCellMemory(obj, nbytes, js::MemoryUse(use));
   zone->runtimeFromMainThread()->gc.maybeAllocTriggerZoneGC(zone);
 }
 
 JS_PUBLIC_API void JS::RemoveAssociatedMemory(JSObject* obj, size_t nbytes,
                                               JS::MemoryUse use) {
   MOZ_ASSERT(obj);
   if (!nbytes) {
     return;
   }
 
-  obj->zoneFromAnyThread()->removeCellMemory(obj, nbytes, use);
+  obj->zoneFromAnyThread()->removeCellMemory(obj, nbytes, js::MemoryUse(use));
 }
 
 #undef JS_AddRoot
 
 JS_PUBLIC_API bool JS_AddExtraGCRootsTracer(JSContext* cx,
                                             JSTraceDataOp traceOp, void* data) {
   return cx->runtime()->gc.addBlackRootsTracer(traceOp, data);
 }