Bug 1448563 - Part 3b: Add memory reporting for JS helper threads. r=jandem
authorTed Campbell <tcampbell@mozilla.com>
Wed, 11 Apr 2018 14:30:46 -0400
changeset 468670 7cf8b356100c8afc5ff6f839e0d6769ea41361d8
parent 468669 0d3b241f3634887b37db68b2b0114743dc27e28f
child 468671 d873c3d7b7bfebb7fb4590ce0e9010e606c69ef9
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1448563
milestone61.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 1448563 - Part 3b: Add memory reporting for JS helper threads. r=jandem MozReview-Commit-ID: JbOY0QRn0Wl
js/public/MemoryMetrics.h
js/src/ds/Fifo.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/MemoryMetrics.cpp
js/xpconnect/src/XPCJSRuntime.cpp
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -499,31 +499,52 @@ struct NotableScriptSourceInfo : public 
     }
 
     char* filename_;
 
   private:
     NotableScriptSourceInfo(const NotableScriptSourceInfo& info) = delete;
 };
 
+struct HelperThreadStats
+{
+#define FOR_EACH_SIZE(macro) \
+    macro(_, MallocHeap, stateData)
+
+    explicit HelperThreadStats()
+      : FOR_EACH_SIZE(ZERO_SIZE)
+        idleThreadCount(0),
+        activeThreadCount(0)
+    { }
+
+    FOR_EACH_SIZE(DECL_SIZE)
+
+    unsigned idleThreadCount;
+    unsigned activeThreadCount;
+
+#undef FOR_EACH_SIZE
+};
+
 /**
  * Measurements that not associated with any individual runtime.
  */
 struct GlobalStats
 {
 #define FOR_EACH_SIZE(macro) \
     macro(_, MallocHeap, tracelogger)
 
     explicit GlobalStats(mozilla::MallocSizeOf mallocSizeOf)
       : FOR_EACH_SIZE(ZERO_SIZE)
         mallocSizeOf_(mallocSizeOf)
     { }
 
     FOR_EACH_SIZE(DECL_SIZE)
 
+    HelperThreadStats helperThread;
+
     mozilla::MallocSizeOf mallocSizeOf_;
 
 #undef FOR_EACH_SIZE
 };
 
 /**
  * These measurements relate directly to the JSRuntime, and not to zones,
  * compartments, and realms within it.
--- a/js/src/ds/Fifo.h
+++ b/js/src/ds/Fifo.h
@@ -172,13 +172,21 @@ class Fifo
     // Clear all elements for which the given predicate returns 'true'. Return
     // the number of elements removed.
     template <class Pred>
     size_t eraseIf(Pred pred) {
         size_t erased = EraseIf(front_, pred);
         erased += EraseIf(rear_, pred);
         return erased;
     }
+
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return front_.sizeOfExcludingThis(mallocSizeOf) +
+               rear_.sizeOfExcludingThis(mallocSizeOf);
+    }
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+    }
 };
 
 } // namespace js
 
 #endif /* js_Fifo_h */
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -986,17 +986,17 @@ GlobalHelperThreadState::lock()
 void
 GlobalHelperThreadState::unlock()
 {
     helperLock.unlock();
 }
 
 #ifdef DEBUG
 bool
-GlobalHelperThreadState::isLockedByCurrentThread()
+GlobalHelperThreadState::isLockedByCurrentThread() const
 {
     return helperLock.ownedByCurrentThread();
 }
 #endif // DEBUG
 
 void
 GlobalHelperThreadState::wait(AutoLockHelperThreadState& locked, CondVar which,
                               TimeDuration timeout /* = TimeDuration::Forever() */)
@@ -1115,16 +1115,60 @@ IsHelperThreadSimulatingOOM(js::ThreadTy
 {
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     return js::oom::targetThread == threadType;
 #else
     return false;
 #endif
 }
 
+void
+GlobalHelperThreadState::addSizeOfIncludingThis(JS::GlobalStats* stats,
+                                                AutoLockHelperThreadState& lock) const
+{
+    MOZ_ASSERT(isLockedByCurrentThread());
+
+    mozilla::MallocSizeOf mallocSizeOf = stats->mallocSizeOf_;
+    JS::HelperThreadStats& htStats = stats->helperThread;
+
+    htStats.stateData += mallocSizeOf(this);
+
+    if (threads)
+        htStats.stateData += threads->sizeOfIncludingThis(mallocSizeOf);
+
+    // Report memory used by various containers
+    htStats.stateData +=
+        ionWorklist_.sizeOfExcludingThis(mallocSizeOf) +
+        ionFinishedList_.sizeOfExcludingThis(mallocSizeOf) +
+        ionFreeList_.sizeOfExcludingThis(mallocSizeOf) +
+        wasmWorklist_tier1_.sizeOfExcludingThis(mallocSizeOf) +
+        wasmWorklist_tier2_.sizeOfExcludingThis(mallocSizeOf) +
+        wasmTier2GeneratorWorklist_.sizeOfExcludingThis(mallocSizeOf) +
+        promiseHelperTasks_.sizeOfExcludingThis(mallocSizeOf) +
+        parseWorklist_.sizeOfExcludingThis(mallocSizeOf) +
+        parseFinishedList_.sizeOfExcludingThis(mallocSizeOf) +
+        parseWaitingOnGC_.sizeOfExcludingThis(mallocSizeOf) +
+        compressionPendingList_.sizeOfExcludingThis(mallocSizeOf) +
+        compressionWorklist_.sizeOfExcludingThis(mallocSizeOf) +
+        compressionFinishedList_.sizeOfExcludingThis(mallocSizeOf) +
+        gcHelperWorklist_.sizeOfExcludingThis(mallocSizeOf) +
+        gcParallelWorklist_.sizeOfExcludingThis(mallocSizeOf);
+
+    // Report number of helper threads.
+    MOZ_ASSERT(htStats.idleThreadCount == 0);
+    if (threads) {
+        for (auto& thread : *threads) {
+            if (thread.idle())
+                htStats.idleThreadCount++;
+            else
+                htStats.activeThreadCount++;
+        }
+    }
+}
+
 size_t
 GlobalHelperThreadState::maxIonCompilationThreads() const
 {
     if (IsHelperThreadSimulatingOOM(js::THREAD_TYPE_ION))
         return 1;
     return threadCount;
 }
 
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -135,16 +135,20 @@ class GlobalHelperThreadState
     GCHelperStateVector gcHelperWorklist_;
 
     // GC tasks needing to be done in parallel.
     GCParallelTaskVector gcParallelWorklist_;
 
     ParseTask* removeFinishedParseTask(ParseTaskKind kind, void* token);
 
   public:
+
+    void addSizeOfIncludingThis(JS::GlobalStats* stats,
+                                AutoLockHelperThreadState& lock) const;
+
     size_t maxIonCompilationThreads() const;
     size_t maxWasmCompilationThreads() const;
     size_t maxWasmTier2GeneratorThreads() const;
     size_t maxPromiseHelperThreads() const;
     size_t maxParseThreads() const;
     size_t maxCompressionThreads() const;
     size_t maxGCHelperThreads() const;
     size_t maxGCParallelThreads() const;
@@ -153,17 +157,17 @@ class GlobalHelperThreadState
 
     bool ensureInitialized();
     void finish();
     void finishThreads();
 
     void lock();
     void unlock();
 #ifdef DEBUG
-    bool isLockedByCurrentThread();
+    bool isLockedByCurrentThread() const;
 #endif
 
     enum CondVar {
         // For notifying threads waiting for work that they may be able to make
         // progress, ie, a work item has been completed by a helper thread and
         // the thread that created the work item can now consume it.
         CONSUMER,
 
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -8,16 +8,17 @@
 
 #include "gc/GC.h"
 #include "gc/Heap.h"
 #include "gc/Nursery.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "vm/ArrayObject.h"
+#include "vm/HelperThreads.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Runtime.h"
 #include "vm/Shape.h"
 #include "vm/StringType.h"
 #include "vm/SymbolType.h"
 #include "vm/WrapperObject.h"
@@ -843,16 +844,22 @@ CollectRuntimeStatsHelper(JSContext* cx,
                                   rtStats->zTotals.gcHeapArenaAdmin -
                                   rtStats->gcHeapGCThings;
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS::CollectGlobalStats(GlobalStats *gStats)
 {
+    AutoLockHelperThreadState lock;
+
+    // HelperThreadState holds data that is not part of a Runtime. This does
+    // not include data is is currently being processed by a HelperThread.
+    HelperThreadState().addSizeOfIncludingThis(gStats, lock);
+
 #ifdef JS_TRACE_LOGGING
     // Global data used by TraceLogger
     gStats->tracelogger += SizeOfTraceLogState(gStats->mallocSizeOf_);
     gStats->tracelogger += SizeOfTraceLogGraphState(gStats->mallocSizeOf_);
 #endif
 
     return true;
 }
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2312,16 +2312,27 @@ JSReporter::CollectReports(WindowPaths* 
     ReportZoneStats(rtStats.zTotals, zExtrasTotal, handleReport, data,
                     anonymize);
 
     // Report the sum of the runtime/ numbers.
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
         KIND_OTHER, rtTotal,
         "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
 
+    // Report the number of HelperThread
+
+    REPORT(NS_LITERAL_CSTRING("js-helper-threads/idle"),
+        KIND_OTHER, UNITS_COUNT, gStats.helperThread.idleThreadCount,
+        "The current number of idle JS HelperThreads.");
+
+    REPORT(NS_LITERAL_CSTRING("js-helper-threads/active"),
+        KIND_OTHER, UNITS_COUNT, gStats.helperThread.activeThreadCount,
+        "The current number of active JS HelperThreads. Memory held by these is"
+        " not reported.");
+
     // Report the numbers for memory used by wasm Runtime state.
     REPORT_BYTES(NS_LITERAL_CSTRING("wasm-runtime"),
         KIND_OTHER, rtStats.runtime.wasmRuntime,
         "The memory used for wasm runtime bookkeeping.");
 
     // Report the numbers for memory outside of compartments.
 
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
@@ -2469,16 +2480,22 @@ JSReporter::CollectReports(WindowPaths* 
         KIND_HEAP, jsComponentLoaderSize,
         "XPConnect's JS component loader.");
 
     // Report tracelogger (global).
 
     REPORT_BYTES(NS_LITERAL_CSTRING("explicit/js-non-window/tracelogger"),
         KIND_HEAP, gStats.tracelogger,
         "The memory used for the tracelogger, including the graph and events.");
+
+    // Report HelperThreadState.
+
+    REPORT_BYTES(NS_LITERAL_CSTRING("explicit/js-non-window/helper-thread/heap-other"),
+        KIND_HEAP, gStats.helperThread.stateData,
+        "Memory used by HelperThreadState.");
 }
 
 static nsresult
 JSSizeOfTab(JSObject* objArg, size_t* jsObjectsSize, size_t* jsStringsSize,
             size_t* jsPrivateSize, size_t* jsOtherSize)
 {
     JSContext* cx = XPCJSContext::Get()->Context();
     JS::RootedObject obj(cx, objArg);