Bug 1158133 - Add a way to disable async stacks, and disable by default on mobile platforms. r=bent, r=jimb, a=lizzard
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Tue, 09 Jun 2015 11:17:09 +0200
changeset 266237 bfba880deefd
parent 266236 90c96749f1a3
child 266238 5ac2c3ff9a6d
push id4796
push userryanvm@gmail.com
push date2015-06-11 16:30 +0000
treeherdermozilla-beta@ad1f0e1ddaf2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent, jimb, lizzard
bugs1158133
milestone39.0
Bug 1158133 - Add a way to disable async stacks, and disable by default on mobile platforms. r=bent, r=jimb, a=lizzard
docshell/test/browser/browser_timelineMarkers-frame-05.js
dom/bindings/test/test_promise_rejections_from_jsimplemented.html
dom/workers/RuntimeService.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js
modules/libpref/init/all.js
--- a/docshell/test/browser/browser_timelineMarkers-frame-05.js
+++ b/docshell/test/browser/browser_timelineMarkers-frame-05.js
@@ -73,29 +73,33 @@ let TESTS = [{
   check: function(markers) {
     markers = markers.filter(m => m.name == "ConsoleTime");
     ok(markers.length > 0, "ConsoleTime marker includes stack");
     ok(markers[0].stack.functionDisplayName == "testConsoleTime",
        "testConsoleTime is on the stack");
     ok(markers[0].endStack.functionDisplayName == "testConsoleTimeEnd",
        "testConsoleTimeEnd is on the stack");
   }
-}, {
-  desc: "Async stack trace on Promise",
-  searchFor: "ConsoleTime",
-  setup: function(docShell) {
-    let resolver = makePromise();
-    resolvePromise(resolver);
-  },
-  check: function(markers) {
-    markers = markers.filter(m => m.name == "ConsoleTime");
-    ok(markers.length > 0, "Promise marker includes stack");
-
-    let frame = markers[0].endStack;
-    ok(frame.parent.asyncParent !== null, "Parent frame has async parent");
-    is(frame.parent.asyncParent.asyncCause, "Promise",
-       "Async parent has correct cause");
-    is(frame.parent.asyncParent.functionDisplayName, "makePromise",
-       "Async parent has correct function name");
-  }
 }];
 
+if (Services.prefs.getBoolPref("javascript.options.asyncstack")) {
+  TESTS.push({
+    desc: "Async stack trace on Promise",
+    searchFor: "ConsoleTime",
+    setup: function(docShell) {
+      let resolver = makePromise();
+      resolvePromise(resolver);
+    },
+    check: function(markers) {
+      markers = markers.filter(m => m.name == "ConsoleTime");
+      ok(markers.length > 0, "Promise marker includes stack");
+
+      let frame = markers[0].endStack;
+      ok(frame.parent.asyncParent !== null, "Parent frame has async parent");
+      is(frame.parent.asyncParent.asyncCause, "Promise",
+         "Async parent has correct cause");
+      is(frame.parent.asyncParent.functionDisplayName, "makePromise",
+         "Async parent has correct function name");
+    }
+  });
+}
+
 timelineContentTest(TESTS);
--- a/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
+++ b/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
@@ -32,17 +32,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   }
 
   function ensurePromiseFail(testNumber, value) {
     ok(false, "Test " + testNumber + " should not have a fulfilled promise");
   }
 
   function doTest() {
     var t = new TestInterfaceJS();
-
+    var asyncStack = SpecialPowers.getBoolPref("javascript.options.asyncstack");
     var ourFile = "http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html";
 
     Promise.all([
       t.testPromiseWithThrowingChromePromiseInit().then(
           ensurePromiseFail.bind(null, 1),
           checkExn.bind(null, 44, "NS_ERROR_UNEXPECTED", "", undefined,
                         ourFile, 1,
                         "doTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:44:7\n")),
@@ -59,17 +59,17 @@ https://bugzilla.mozilla.org/show_bug.cg
           checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 3, "")),
       t.testPromiseWithThrowingContentThenFunction(function() {
           thereIsNoSuchContentFunction2();
         }).then(
           ensurePromiseFail.bind(null, 4),
           checkExn.bind(null, 61, "ReferenceError",
                         "thereIsNoSuchContentFunction2 is not defined",
                         undefined, ourFile, 4,
-                        "doTest/<@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:61:11\nAsync*doTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:60:7\n")),
+                        "doTest/<@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:61:11\n" + (asyncStack ? "Async*doTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:60:7\n" : ""))),
       t.testPromiseWithThrowingChromeThenable().then(
           ensurePromiseFail.bind(null, 5),
           checkExn.bind(null, 0, "NS_ERROR_UNEXPECTED", "", undefined, "", 5, "")),
       t.testPromiseWithThrowingContentThenable({
             then: function() { thereIsNoSuchContentFunction3(); }
         }).then(
           ensurePromiseFail.bind(null, 6),
           checkExn.bind(null, 72, "ReferenceError",
@@ -79,20 +79,20 @@ https://bugzilla.mozilla.org/show_bug.cg
       t.testPromiseWithDOMExceptionThrowingPromiseInit().then(
           ensurePromiseFail.bind(null, 7),
           checkExn.bind(null, 79, "NotFoundError",
                         "We are a second DOMException",
                         DOMException.NOT_FOUND_ERR, ourFile, 7,
                         "doTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:79:7\n")),
       t.testPromiseWithDOMExceptionThrowingThenFunction().then(
           ensurePromiseFail.bind(null, 8),
-          checkExn.bind(null, 85, "NetworkError",
-                        "We are a third DOMException",
-                        DOMException.NETWORK_ERR, ourFile, 8,
-                        "Async*doTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:85:7\n")),
+          checkExn.bind(null, asyncStack ? 85 : 0, "NetworkError",
+                         "We are a third DOMException",
+                        DOMException.NETWORK_ERR, asyncStack ? ourFile : "", 8,
+                        asyncStack ? "Async*doTest@http://mochi.test:8888/tests/dom/bindings/test/test_promise_rejections_from_jsimplemented.html:85:7\n" : "")),
       t.testPromiseWithDOMExceptionThrowingThenable().then(
           ensurePromiseFail.bind(null, 9),
           checkExn.bind(null, 0, "TypeMismatchError",
                         "We are a fourth DOMException",
                          DOMException.TYPE_MISMATCH_ERR, "", 9, "")),
     ]).then(SimpleTest.finish,
             function() {
               ok(false, "One of our catch statements totally failed");
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -329,35 +329,23 @@ LoadRuntimeOptions(const char* aPrefName
   if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) ||
       prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) {
     return;
   }
 #endif
 
   // Runtime options.
   JS::RuntimeOptions runtimeOptions;
-  if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs"))) {
-    runtimeOptions.setAsmJS(true);
-  }
-  if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit"))) {
-    runtimeOptions.setBaseline(true);
-  }
-  if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion"))) {
-    runtimeOptions.setIon(true);
-  }
-  if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp"))) {
-    runtimeOptions.setNativeRegExp(true);
-  }
-  if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror"))) {
-    runtimeOptions.setWerror(true);
-  }
-
-  if (GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict"))) {
-    runtimeOptions.setExtraWarnings(true);
-  }
+  runtimeOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
+                .setBaseline(GetWorkerPref<bool>(NS_LITERAL_CSTRING("baselinejit")))
+                .setIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("ion")))
+                .setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
+                .setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
+                .setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
+                .setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")));
 
   RuntimeService::SetDefaultRuntimeOptions(runtimeOptions);
 
   if (rts) {
     rts->UpdateAllWorkerRuntimeOptions();
   }
 }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4437,16 +4437,22 @@ JS_RestoreFrameChain(JSContext* cx)
 JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls(
   JSContext* cx, HandleObject stack, HandleString asyncCause)
   : cx(cx),
     oldAsyncStack(cx, cx->runtime()->asyncStackForNewActivations),
     oldAsyncCause(cx, cx->runtime()->asyncCauseForNewActivations)
 {
     CHECK_REQUEST(cx);
 
+    // The option determines whether we actually use the new values at this
+    // point. It will not affect restoring the previous values when the object
+    // is destroyed, so if the option changes it won't cause consistency issues.
+    if (!cx->runtime()->options().asyncStack())
+        return;
+
     SavedFrame* asyncStack = &stack->as<SavedFrame>();
     MOZ_ASSERT(!asyncCause->empty());
 
     cx->runtime()->asyncStackForNewActivations = asyncStack;
     cx->runtime()->asyncCauseForNewActivations = asyncCause;
 }
 
 JS::AutoSetAsyncStackForNewCalls::~AutoSetAsyncStackForNewCalls()
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1192,16 +1192,17 @@ namespace JS {
 class JS_PUBLIC_API(RuntimeOptions) {
   public:
     RuntimeOptions()
       : baseline_(false),
         ion_(false),
         asmJS_(false),
         nativeRegExp_(false),
         unboxedObjects_(false),
+        asyncStack_(true),
         werror_(false),
         strictMode_(false),
         extraWarnings_(false),
         varObjFix_(false)
     {
     }
 
     bool baseline() const { return baseline_; }
@@ -1241,16 +1242,22 @@ class JS_PUBLIC_API(RuntimeOptions) {
     }
 
     bool unboxedObjects() const { return unboxedObjects_; }
     RuntimeOptions& setUnboxedObjects(bool flag) {
         unboxedObjects_ = flag;
         return *this;
     }
 
+    bool asyncStack() const { return asyncStack_; }
+    RuntimeOptions& setAsyncStack(bool flag) {
+        asyncStack_ = flag;
+        return *this;
+    }
+
     bool werror() const { return werror_; }
     RuntimeOptions& setWerror(bool flag) {
         werror_ = flag;
         return *this;
     }
     RuntimeOptions& toggleWerror() {
         werror_ = !werror_;
         return *this;
@@ -1287,16 +1294,17 @@ class JS_PUBLIC_API(RuntimeOptions) {
     }
 
   private:
     bool baseline_ : 1;
     bool ion_ : 1;
     bool asmJS_ : 1;
     bool nativeRegExp_ : 1;
     bool unboxedObjects_ : 1;
+    bool asyncStack_ : 1;
     bool werror_ : 1;
     bool strictMode_ : 1;
     bool extraWarnings_ : 1;
     bool varObjFix_ : 1;
 };
 
 JS_PUBLIC_API(RuntimeOptions&)
 RuntimeOptionsRef(JSRuntime* rt);
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1591,27 +1591,30 @@ ReloadPrefsCallback(const char* pref, vo
     bool offthreadIonCompilation = Preferences::GetBool(JS_OPTIONS_DOT_STR
                                                        "ion.offthread_compilation");
     bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
                                                  "baselinejit.unsafe_eager_compilation");
     bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
 
     sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
 
+    bool useAsyncStack = Preferences::GetBool(JS_OPTIONS_DOT_STR "asyncstack");
+
     bool werror = Preferences::GetBool(JS_OPTIONS_DOT_STR "werror");
 
     bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
 #ifdef DEBUG
     sExtraWarningsForSystemJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict.debug");
 #endif
 
     JS::RuntimeOptionsRef(rt).setBaseline(useBaseline)
                              .setIon(useIon)
                              .setAsmJS(useAsmJS)
                              .setNativeRegExp(useNativeRegExp)
+                             .setAsyncStack(useAsyncStack)
                              .setWerror(werror)
                              .setExtraWarnings(extraWarnings);
 
     JS_SetParallelParsingEnabled(rt, parallelParsing);
     JS_SetOffthreadIonCompilationEnabled(rt, offthreadIonCompilation);
     JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
                                   useBaselineEager ? 0 : -1);
     JS_SetGlobalJitCompilerOption(rt, JSJITCOMPILER_ION_WARMUP_TRIGGER,
--- a/js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js
+++ b/js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js
@@ -1,9 +1,16 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 function run_test() {
+  if (!Services.prefs.getBoolPref("javascript.options.asyncstack")) {
+    do_print("Async stacks are disabled.");
+    return;
+  }
+
   function getAsyncStack() {
     return Components.stack;
   }
 
   // asyncCause may contain non-ASCII characters.
   let testAsyncCause = "Tes" + String.fromCharCode(355) + "String";
 
   Components.utils.callFunctionWithAsyncStack(function asyncCallback() {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1045,16 +1045,21 @@ pref("javascript.options.strict",       
 #ifdef DEBUG
 pref("javascript.options.strict.debug",     false);
 #endif
 pref("javascript.options.baselinejit",      true);
 pref("javascript.options.ion",              true);
 pref("javascript.options.asmjs",            true);
 pref("javascript.options.native_regexp",    true);
 pref("javascript.options.parallel_parsing", true);
+#if !defined(RELEASE_BUILD) && !defined(ANDROID) && !defined(MOZ_B2G) && !defined(XP_IOS)
+pref("javascript.options.asyncstack",       true);
+#else
+pref("javascript.options.asyncstack",       false);
+#endif
 pref("javascript.options.ion.offthread_compilation", true);
 // This preference instructs the JS engine to discard the
 // source of any privileged JS after compilation. This saves
 // memory, but makes things like Function.prototype.toSource()
 // fail.
 pref("javascript.options.discardSystemSource", false);
 // This preference limits the memory usage of javascript.
 // If you want to change these values for your device,