Bug 1341317 - Require runtimes to be single threaded while the Gecko profiler is in use, r=shu.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 02 Mar 2017 05:11:28 -0700
changeset 345621 c385308aa7722dbc787b6c54bd623f3ff1b3e8f9
parent 345620 59ebf8130c6d6b8d50cd1170e5f1b401dad2100c
child 345622 d60dce18350aa8a8a7932d949cd0fcf8ae5722c0
push id31441
push userkwierso@gmail.com
push dateThu, 02 Mar 2017 22:57:54 +0000
treeherdermozilla-central@b23d6277acca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1341317
milestone54.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 1341317 - Require runtimes to be single threaded while the Gecko profiler is in use, r=shu.
js/src/shell/js.cpp
js/src/vm/GeckoProfiler.cpp
js/src/vm/GeckoProfiler.h
js/src/vm/Runtime.cpp
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5376,22 +5376,23 @@ static bool
 EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     ShellContext* sc = GetShellContext(cx);
 
     // Disable before re-enabling; see the assertion in |GeckoProfiler::setProfilingStack|.
     if (cx->runtime()->geckoProfiler().installed())
-        cx->runtime()->geckoProfiler().enable(false);
+        MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
 
     SetContextProfilingStack(cx, sc->geckoProfilingStack, &sc->geckoProfilingStackSize,
                              ShellContext::GeckoProfilingMaxStackSize);
     cx->runtime()->geckoProfiler().enableSlowAssertions(false);
-    cx->runtime()->geckoProfiler().enable(true);
+    if (!cx->runtime()->geckoProfiler().enable(true))
+        JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler");
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 EnableGeckoProfilingWithSlowAssertions(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -5403,37 +5404,38 @@ EnableGeckoProfilingWithSlowAssertions(J
     if (cx->runtime()->geckoProfiler().enabled()) {
         // If profiling already enabled with slow assertions disabled,
         // this is a no-op.
         if (cx->runtime()->geckoProfiler().slowAssertionsEnabled())
             return true;
 
         // Slow assertions are off.  Disable profiling before re-enabling
         // with slow assertions on.
-        cx->runtime()->geckoProfiler().enable(false);
+        MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
     }
 
     // Disable before re-enabling; see the assertion in |GeckoProfiler::setProfilingStack|.
     if (cx->runtime()->geckoProfiler().installed())
-        cx->runtime()->geckoProfiler().enable(false);
+        MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
 
     SetContextProfilingStack(cx, sc->geckoProfilingStack, &sc->geckoProfilingStackSize,
                              ShellContext::GeckoProfilingMaxStackSize);
     cx->runtime()->geckoProfiler().enableSlowAssertions(true);
-    cx->runtime()->geckoProfiler().enable(true);
+    if (!cx->runtime()->geckoProfiler().enable(true))
+        JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler");
 
     return true;
 }
 
 static bool
 DisableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (cx->runtime()->geckoProfiler().installed())
-        cx->runtime()->geckoProfiler().enable(false);
+        MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
     args.rval().setUndefined();
     return true;
 }
 
 // Global mailbox that is used to communicate a SharedArrayBuffer
 // value from one worker to another.
 //
 // For simplicity we store only the SharedArrayRawBuffer; retaining
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -58,23 +58,34 @@ GeckoProfiler::setProfilingStack(Profile
 }
 
 void
 GeckoProfiler::setEventMarker(void (*fn)(const char*))
 {
     eventMarker_ = fn;
 }
 
-void
+bool
 GeckoProfiler::enable(bool enabled)
 {
     MOZ_ASSERT(installed());
 
     if (enabled_ == enabled)
-        return;
+        return true;
+
+    // Execution in the runtime must be single threaded if the Gecko profiler
+    // is enabled. There is only a single profiler stack in the runtime, from
+    // which entries must be added/removed in a LIFO fashion.
+    JSContext* cx = rt->activeContextFromOwnThread();
+    if (enabled) {
+        if (!rt->beginSingleThreadedExecution(cx))
+            return false;
+    } else {
+        rt->endSingleThreadedExecution(cx);
+    }
 
     /*
      * Ensure all future generated code will be instrumented, or that all
      * currently instrumented code is discarded
      */
     ReleaseAllJITCode(rt->defaultFreeOp());
 
     // This function is called when the Gecko profiler makes a new Sampler
@@ -124,16 +135,18 @@ GeckoProfiler::enable(bool enabled)
                 while (jitActivation) {
                     jitActivation->setLastProfilingFrame(nullptr);
                     jitActivation->setLastProfilingCallSite(nullptr);
                     jitActivation = jitActivation->prevJitActivation();
                 }
             }
         }
     }
+
+    return true;
 }
 
 /* Lookup the string for the function/script, creating one if necessary */
 const char*
 GeckoProfiler::profileString(JSScript* script, JSFunction* maybeFun)
 {
     auto locked = strings.lock();
     MOZ_ASSERT(locked->initialized());
@@ -535,17 +548,18 @@ JS_FRIEND_API(void)
 js::SetContextProfilingStack(JSContext* cx, ProfileEntry* stack, uint32_t* size, uint32_t max)
 {
     cx->runtime()->geckoProfiler().setProfilingStack(stack, size, max);
 }
 
 JS_FRIEND_API(void)
 js::EnableContextProfilingStack(JSContext* cx, bool enabled)
 {
-    cx->runtime()->geckoProfiler().enable(enabled);
+    if (!cx->runtime()->geckoProfiler().enable(enabled))
+        MOZ_CRASH("Execution in this runtime should already be single threaded");
 }
 
 JS_FRIEND_API(void)
 js::RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*))
 {
     MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
     cx->runtime()->geckoProfiler().setEventMarker(fn);
 }
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -161,17 +161,17 @@ class GeckoProfiler
     uint32_t* sizePointer() { return size_; }
     uint32_t maxSize() { return max_; }
     uint32_t size() { MOZ_ASSERT(installed()); return *size_; }
     ProfileEntry* stack() { return stack_; }
 
     /* management of whether instrumentation is on or off */
     bool enabled() { MOZ_ASSERT_IF(enabled_, installed()); return enabled_; }
     bool installed() { return stack_ != nullptr && size_ != nullptr; }
-    void enable(bool enabled);
+    MOZ_MUST_USE bool enable(bool enabled);
     void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
     bool slowAssertionsEnabled() { return slowAssertions; }
 
     /*
      * Functions which are the actual instrumentation to track run information
      *
      *   - enter: a function has started to execute
      *   - updatePC: updates the pc information about where a function
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -307,17 +307,17 @@ JSRuntime::destroyRuntime()
         JS::PrepareForFullGC(cx);
         gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME);
     }
 
     AutoNoteSingleThreadedRegion anstr;
 
     MOZ_ASSERT(ionLazyLinkListSize_ == 0);
     MOZ_ASSERT(ionLazyLinkList().isEmpty());
-    MOZ_ASSERT(!singleThreadedExecutionRequired_);
+    MOZ_ASSERT_IF(!geckoProfiler().enabled(), !singleThreadedExecutionRequired_);
 
     MOZ_ASSERT(!hasHelperThreadZones());
     AutoLockForExclusiveAccess lock(this);
 
     /*
      * Even though all objects in the compartment are dead, we may have keep
      * some filenames around because of gcKeepAtoms.
      */