Bug 1491037 - Report JSContext memory for idle helper threads r=nbp
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 18 Sep 2018 13:56:45 +0100
changeset 437046 a1502c0643fc8b88c5ae61977333c3deef46b2e2
parent 437045 9a1cfe1a7bee97931a282bfd0000b8aaa351e7fd
child 437047 153d6dc389626a489be3b29e9ec4ddb4c4744566
push id34668
push useraiakab@mozilla.com
push dateWed, 19 Sep 2018 02:19:16 +0000
treeherdermozilla-central@a9e339b3e5d8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1491037
milestone64.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 1491037 - Report JSContext memory for idle helper threads r=nbp
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/src/vm/JSContext.cpp
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1326,24 +1326,27 @@ GlobalHelperThreadState::addSizeOfInclud
     // Report wasm::CompileTasks on wait lists
     for (auto task : wasmWorklist_tier1_) {
         htStats.wasmCompile += task->sizeOfExcludingThis(mallocSizeOf);
     }
     for (auto task : wasmWorklist_tier2_) {
         htStats.wasmCompile += task->sizeOfExcludingThis(mallocSizeOf);
     }
 
-    // Report number of helper threads.
+    // Report number of helper threads and memory usage of idle threads.
     MOZ_ASSERT(htStats.idleThreadCount == 0);
     if (threads) {
         for (auto& thread : *threads) {
             if (thread.idle()) {
                 htStats.idleThreadCount++;
+                htStats.stateData += thread.context()->sizeOfExcludingThis(mallocSizeOf);
             } else {
                 htStats.activeThreadCount++;
+                // We can't safely access active thread data, so skip reporting
+                // for now.
             }
         }
     }
 }
 
 size_t
 GlobalHelperThreadState::maxIonCompilationThreads() const
 {
@@ -2227,18 +2230,17 @@ HelperThread::handleParseWorkload(AutoLo
 #ifdef DEBUG
     runtime->incOffThreadParsesRunning();
 #endif
 
     {
         AutoUnlockHelperThreadState unlock(locked);
         AutoSetContextRuntime ascr(runtime);
 
-        JSContext* cx = TlsContext.get();
-
+        JSContext* cx = context();
         Zone* zone = task->parseGlobal->zoneFromAnyThread();
         zone->setHelperThreadOwnerContext(cx);
         auto resetOwnerContext = mozilla::MakeScopeExit([&] {
             zone->setHelperThreadOwnerContext(nullptr);
         });
 
         AutoRealm ar(cx, task->parseGlobal);
 
@@ -2533,35 +2535,52 @@ HelperThread::AutoProfilerLabel::AutoPro
 
 HelperThread::AutoProfilerLabel::~AutoProfilerLabel()
 {
     if (profilingStack) {
         profilingStack->pop();
     }
 }
 
+class HelperThread::AutoInitContext
+{
+    HelperThread* thread_;
+
+  public:
+    AutoInitContext(HelperThread* thread, const AutoLockHelperThreadState& lock)
+      : thread_(thread)
+    {
+        thread_->context_.emplace(nullptr, JS::ContextOptions());
+
+        JSContext* cx = thread_->context();
+        AutoEnterOOMUnsafeRegion oomUnsafe;
+        if (!cx->init(ContextKind::HelperThread)) {
+            oomUnsafe.crash("HelperThread JSContext::init()");
+        }
+
+        cx->setHelperThread(thread_);
+        JS_SetNativeStackQuota(cx, HELPER_STACK_QUOTA);
+    }
+
+    ~AutoInitContext() {
+        thread_->context_.reset();
+    }
+};
+
 void
 HelperThread::threadLoop()
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
     JS::AutoSuppressGCAnalysis nogc;
     AutoLockHelperThreadState lock;
 
     ensureRegisteredWithProfiler();
 
-    JSContext cx(nullptr, JS::ContextOptions());
-    {
-        AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!cx.init(ContextKind::HelperThread)) {
-            oomUnsafe.crash("HelperThread cx.init()");
-        }
-    }
-    cx.setHelperThread(this);
-    JS_SetNativeStackQuota(&cx, HELPER_STACK_QUOTA);
+    AutoInitContext init(this, lock);
 
     while (!terminate) {
         MOZ_ASSERT(idle());
 
         if (mozilla::recordreplay::IsRecordingOrReplaying()) {
             // Unlock the helper thread state lock before potentially
             // blocking while the main thread waits for all threads to
             // become idle. Otherwise we would need to see if we need to
@@ -2573,17 +2592,17 @@ HelperThread::threadLoop()
             }
             // Now that we are holding the helper thread state lock again,
             // notify the record/replay system that we might block soon.
             // The helper thread state lock may not be released until the
             // block occurs.
             mozilla::recordreplay::NotifyUnrecordedWait(WakeupAll);
         }
 
-        maybeFreeUnusedMemory(&cx);
+        maybeFreeUnusedMemory();
 
         // The selectors may depend on the HelperThreadState not changing
         // between task selection and task execution, in particular, on new
         // tasks not being added (because of the lifo structure of the work
         // lists). Unlocking the HelperThreadState between task selection and
         // execution is not well-defined.
 
         const TaskSpec* task = findHighestPriorityTask(lock);
@@ -2611,19 +2630,20 @@ HelperThread::findHighestPriorityTask(co
             return &task;
         }
     }
 
     return nullptr;
 }
 
 void
-HelperThread::maybeFreeUnusedMemory(JSContext* cx)
+HelperThread::maybeFreeUnusedMemory()
 {
     MOZ_ASSERT(idle());
 
+    JSContext* cx = context();
     cx->tempLifoAlloc().releaseAll();
 
     if (shouldFreeUnusedMemory) {
         cx->tempLifoAlloc().freeAll();
         shouldFreeUnusedMemory = false;
     }
 }
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -354,31 +354,37 @@ typedef mozilla::Variant<jit::IonBuilder
                          SourceCompressionTask*,
                          GCParallelTask*> HelperTaskUnion;
 
 /* Individual helper thread, one allocated per core. */
 struct HelperThread
 {
     mozilla::Maybe<Thread> thread;
 
+    mozilla::Maybe<JSContext> context_;
+
     /*
      * Indicate to a thread that it should terminate itself. This is only read
      * or written with the helper thread state lock held.
      */
     bool terminate;
 
     /*
      * Indicates that this thread should free its unused memory when it is next
      * idle.
      */
     bool shouldFreeUnusedMemory;
 
     /* The current task being executed by this thread, if any. */
     mozilla::Maybe<HelperTaskUnion> currentTask;
 
+    JSContext* context() {
+        return context_.ptr();
+    }
+
     bool idle() const {
         return currentTask.isNothing();
     }
 
     /* Any builder currently being compiled by Ion on this thread. */
     jit::IonBuilder* ionBuilder() {
         return maybeCurrentTaskAs<jit::IonBuilder*>();
     }
@@ -455,29 +461,32 @@ struct HelperThread
     T maybeCurrentTaskAs() {
         if (currentTask.isSome() && currentTask->is<T>()) {
             return currentTask->as<T>();
         }
 
         return nullptr;
     }
 
-    void maybeFreeUnusedMemory(JSContext* cx);
+    void maybeFreeUnusedMemory();
 
     void handleWasmWorkload(AutoLockHelperThreadState& locked, wasm::CompileMode mode);
 
     void handleWasmTier1Workload(AutoLockHelperThreadState& locked);
     void handleWasmTier2Workload(AutoLockHelperThreadState& locked);
     void handleWasmTier2GeneratorWorkload(AutoLockHelperThreadState& locked);
     void handlePromiseHelperTaskWorkload(AutoLockHelperThreadState& locked);
     void handleIonWorkload(AutoLockHelperThreadState& locked);
     void handleIonFreeWorkload(AutoLockHelperThreadState& locked);
     void handleParseWorkload(AutoLockHelperThreadState& locked);
     void handleCompressionWorkload(AutoLockHelperThreadState& locked);
     void handleGCParallelWorkload(AutoLockHelperThreadState& locked);
+
+    class AutoInitContext;
+    friend class AutoInitContext;
 };
 
 /* Methods for interacting with helper threads. */
 
 // Create data structures used by helper threads.
 bool
 CreateHelperThreadsState();
 
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1557,22 +1557,28 @@ void
 JSContext::updateJITEnabled()
 {
     jitIsBroken = IsJITBrokenHere();
 }
 
 size_t
 JSContext::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
+#ifdef DEBUG
+    HelperThread* helper = helperThread_.refNoCheck();
+    MOZ_ASSERT_IF(helper, helper->idle());
+#endif
+
     /*
      * There are other JSContext members that could be measured; the following
      * ones have been found by DMD to be worth measuring.  More stuff may be
      * added later.
      */
-    return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf);
+    return tempLifoAlloc_.refNoCheck().sizeOfExcludingThis(mallocSizeOf) +
+           cycleDetectorVector_.refNoCheck().sizeOfExcludingThis(mallocSizeOf);
 }
 
 #ifdef DEBUG
 bool
 JSContext::inAtomsZone() const
 {
     return zone_->isAtomsZone();
 }