Bug 1405374 - Register JS threads with the profiler, r=jonco
authorSteve Fink <sfink@mozilla.com>
Thu, 07 Jun 2018 15:37:08 -0700
changeset 478776 aacc6666a49af6cd48972eec8ad62040b8ab20ae
parent 478775 1e833c6a3ba2a4d592868fd2167e94c1cb933004
child 478777 4ede0210acdbfeeaf7ec13e445618f94540eccf1
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1405374
milestone62.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 1405374 - Register JS threads with the profiler, r=jonco
js/public/ProfilingStack.h
js/src/vm/HelperThreads.cpp
js/src/vm/HelperThreads.h
js/xpconnect/src/nsXPConnect.cpp
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -310,16 +310,30 @@ SetContextProfilingStack(JSContext* cx, 
 JS_FRIEND_API(void)
 EnableContextProfilingStack(JSContext* cx, bool enabled);
 
 JS_FRIEND_API(void)
 RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
 
 } // namespace js
 
+namespace JS {
+
+typedef void
+(* RegisterThreadCallback)(const char* threadName, void* stackBase);
+
+typedef void
+(* UnregisterThreadCallback)();
+
+JS_FRIEND_API(void)
+SetProfilingThreadCallbacks(RegisterThreadCallback registerThread,
+                            UnregisterThreadCallback unregisterThread);
+
+} // namespace JS
+
 // Each thread has its own ProfilingStack. That thread modifies the ProfilingStack,
 // pushing and popping elements as necessary.
 //
 // The ProfilingStack is also read periodically by the profiler's sampler thread.
 // This happens only when the thread that owns the ProfilingStack is suspended.
 // So there are no genuine parallel accesses.
 //
 // However, it is possible for pushing/popping to be interrupted by a periodic
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -94,16 +94,24 @@ js::SetFakeCPUCount(size_t count)
 {
     // This must be called before the threads have been initialized.
     MOZ_ASSERT(!HelperThreadState().threads);
 
     HelperThreadState().cpuCount = count;
     HelperThreadState().threadCount = ThreadCountForCPUCount(count);
 }
 
+void
+JS::SetProfilingThreadCallbacks(JS::RegisterThreadCallback registerThread,
+                                JS::UnregisterThreadCallback unregisterThread)
+{
+    HelperThreadState().registerThread = registerThread;
+    HelperThreadState().unregisterThread = unregisterThread;
+}
+
 bool
 js::StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode)
 {
     AutoLockHelperThreadState lock;
 
     if (!HelperThreadState().wasmWorklist(lock, mode).pushBack(task))
         return false;
 
@@ -990,16 +998,18 @@ GlobalHelperThreadState::ensureInitializ
 
     return true;
 }
 
 GlobalHelperThreadState::GlobalHelperThreadState()
  : cpuCount(0),
    threadCount(0),
    threads(nullptr),
+   registerThread(nullptr),
+   unregisterThread(nullptr),
    wasmTier2GeneratorsFinished_(0),
    helperLock(mutexid::GlobalHelperThreadState)
 {
     cpuCount = ClampDefaultCPUCount(GetCPUCount());
     threadCount = ThreadCountForCPUCount(cpuCount);
 
     MOZ_ASSERT(cpuCount > 0, "GetCPUCount() seems broken");
 }
@@ -1859,16 +1869,42 @@ HelperThread::destroy()
             HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER, lock);
         }
 
         thread->join();
         thread.reset();
     }
 }
 
+void
+HelperThread::ensureRegisteredWithProfiler()
+{
+    if (registered)
+        return;
+
+    JS::RegisterThreadCallback callback = HelperThreadState().registerThread;
+    if (callback) {
+        callback("JS Helper", reinterpret_cast<void*>(GetNativeStackBase()));
+        registered = true;
+    }
+}
+
+void
+HelperThread::unregisterWithProfilerIfNeeded()
+{
+    if (!registered)
+        return;
+
+    JS::UnregisterThreadCallback callback = HelperThreadState().unregisterThread;
+    if (callback) {
+        callback();
+        registered = false;
+    }
+}
+
 /* static */
 void
 HelperThread::ThreadMain(void* arg)
 {
     ThisThread::SetName("JS Helper");
 
     static_cast<HelperThread*>(arg)->threadLoop();
     Mutex::ShutDown();
@@ -2356,16 +2392,18 @@ const HelperThread::TaskSpec HelperThrea
 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);
@@ -2384,16 +2422,18 @@ HelperThread::threadLoop()
             HelperThreadState().wait(lock, GlobalHelperThreadState::PRODUCER);
             continue;
         }
 
         js::oom::SetThreadType(task->type);
         (this->*(task->handleWorkload))(lock);
         js::oom::SetThreadType(js::THREAD_TYPE_NONE);
     }
+
+    unregisterWithProfilerIfNeeded();
 }
 
 const HelperThread::TaskSpec*
 HelperThread::findHighestPriorityTask(const AutoLockHelperThreadState& locked)
 {
     // Return the highest priority task that is ready to start, or nullptr.
 
     for (const auto& task : taskSpecs) {
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -99,16 +99,19 @@ class GlobalHelperThreadState
     typedef Vector<GCHelperState*, 0, SystemAllocPolicy> GCHelperStateVector;
     typedef Vector<GCParallelTask*, 0, SystemAllocPolicy> GCParallelTaskVector;
     typedef Vector<PromiseHelperTask*, 0, SystemAllocPolicy> PromiseHelperTaskVector;
 
     // List of available threads, or null if the thread state has not been initialized.
     using HelperThreadVector = Vector<HelperThread, 0, SystemAllocPolicy>;
     UniquePtr<HelperThreadVector> threads;
 
+    WriteOnceData<JS::RegisterThreadCallback> registerThread;
+    WriteOnceData<JS::UnregisterThreadCallback> unregisterThread;
+
   private:
     // The lists below are all protected by |lock|.
 
     // Ion compilation worklist and finished jobs.
     IonBuilderVector ionWorklist_, ionFinishedList_, ionFreeList_;
 
     // wasm worklists.
     wasm::CompileTaskPtrFifo wasmWorklist_tier1_;
@@ -370,16 +373,22 @@ struct HelperThread
     mozilla::Maybe<Thread> thread;
 
     /*
      * Indicate to a thread that it should terminate itself. This is only read
      * or written with the helper thread state lock held.
      */
     bool terminate;
 
+    /*
+     * Indicate that this thread has been registered and needs to be
+     * unregistered at shutdown.
+     */
+    bool registered;
+
     /* The current task being executed by this thread, if any. */
     mozilla::Maybe<HelperTaskUnion> currentTask;
 
     bool idle() const {
         return currentTask.isNothing();
     }
 
     /* Any builder currently being compiled by Ion on this thread. */
@@ -416,16 +425,19 @@ struct HelperThread
         return maybeCurrentTaskAs<GCParallelTask*>();
     }
 
     void destroy();
 
     static void ThreadMain(void* arg);
     void threadLoop();
 
+    void ensureRegisteredWithProfiler();
+    void unregisterWithProfilerIfNeeded();
+
   private:
     struct TaskSpec
     {
         using Selector = bool(GlobalHelperThreadState::*)(const AutoLockHelperThreadState&);
         using Handler = void(HelperThread::*)(AutoLockHelperThreadState&);
 
         js::ThreadType type;
         Selector canStart;
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -9,16 +9,18 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Unused.h"
 
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
 #include "jsfriendapi.h"
+#include "js/ProfilingStack.h"
+#include "GeckoProfiler.h"
 #include "nsJSEnvironment.h"
 #include "nsThreadUtils.h"
 #include "nsDOMJSUtils.h"
 
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 
 #include "mozilla/dom/BindingUtils.h"
@@ -32,17 +34,16 @@
 #include "nsICycleCollectorListener.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptError.h"
 #include "nsContentUtils.h"
 #include "nsScriptError.h"
-#include "jsfriendapi.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace xpc;
 using namespace JS;
 
 NS_IMPL_ISUPPORTS(nsXPConnect, nsIXPConnect)
 
@@ -67,16 +68,18 @@ const char XPC_XPCONNECT_CONTRACTID[]   
 // nsXPConnect and when creating a new cooperative (non-primary) XPCJSContext.
 static XPCJSContext* gPrimaryContext;
 
 nsXPConnect::nsXPConnect()
     : mShuttingDown(false)
 {
     XPCJSContext::InitTLS();
 
+    JS::SetProfilingThreadCallbacks(profiler_register_thread, profiler_unregister_thread);
+
     XPCJSContext* xpccx = XPCJSContext::NewXPCJSContext(nullptr);
     if (!xpccx) {
         MOZ_CRASH("Couldn't create XPCJSContext.");
     }
     gPrimaryContext = xpccx;
     mRuntime = xpccx->Runtime();
 }