merge mozilla-central to autoland. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Tue, 06 Jun 2017 11:30:08 +0200
changeset 412970 7d7a141c99e72ca7b486f5408c7d926803a0a7a7
parent 412969 01baa0629d259266ac5d5c7826e5bb8fb965e232 (current diff)
parent 412944 58ce95bc58ce4ba200413c8bed87786dccf3d105 (diff)
child 412971 78dcfa118bddbd18b987ae124552c00826a3826f
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone55.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
merge mozilla-central to autoland. r=merge a=merge
dom/network/tests/add_task.js
dom/network/tests/test_tcpsocket_default_permissions.html
dom/network/tests/test_tcpsocket_enabled_no_perm.html
dom/network/tests/test_tcpsocket_enabled_with_perm.html
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -131,18 +131,18 @@ EvaluationExceptionToNSResult(JSContext*
   }
   return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
 }
 
 nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx,
                                               JS::Handle<JSObject*> aGlobal)
   :
 #ifdef MOZ_GECKO_PROFILER
-    mProfilerRAII("nsJSUtils::ExecutionContext", /* PROFILER_LABEL */
-                  js::ProfileEntry::Category::JS, __LINE__),
+    mProfilerRAII("nsJSUtils::ExecutionContext", /* dynamicStr */ nullptr,
+                  __LINE__, js::ProfileEntry::Category::JS),
 #endif
     mCx(aCx)
   , mCompartment(aCx, aGlobal)
   , mRetValue(aCx)
   , mScopeChain(aCx)
   , mRv(NS_OK)
   , mSkip(false)
   , mCoerceToString(false)
deleted file mode 100644
--- a/dom/network/tests/add_task.js
+++ /dev/null
@@ -1,83 +0,0 @@
-// Temporary implementation of add_task for mochitest-plain until bug 1078657 is
-// implemented.
-SimpleTest.waitForExplicitFinish();
-(function(scope) {
-  var pendingTasks = [];
-  var pendingPromise = null;
-
-  // Strict spawn function that takes a known generatorFunc and assumes that
-  // every yielded value will be a Promise.  If nesting is desired, then yield*
-  // should be used!
-  function spawn(generatorFunc) {
-    return new Promise(function(resolve, reject) {
-      try {
-        var iterator = generatorFunc();
-      }
-      catch (ex) {
-        ok(false, 'Problem invoking generator func: ' + ex + ': ' + ex.stack);
-        return;
-      }
-      var stepResolved = function(result) {
-        try {
-          var iterStep = iterator.next(result);
-        }
-        catch (ex) {
-          ok(false, 'Problem invoking iterator step: ' + ex + ': ' + ex.stack);
-          return;
-        }
-        if (iterStep.done) {
-          resolve(iterStep.value);
-          return;
-        }
-        if (!iterStep.value || !iterStep.value.then) {
-          ok(false, 'Iterator step returned non-Promise: ' + iterStep.value);
-        }
-        iterStep.value.then(stepResolved, generalErrback);
-      };
-      stepResolved();
-    });
-  }
-
-  function maybeSpawn(promiseOrGenerator) {
-    if (promiseOrGenerator.then) {
-      return promiseOrGenerator;
-    }
-    return spawn(promiseOrGenerator);
-  }
-
-  scope.add_task = function(thing) {
-    pendingTasks.push(thing);
-  };
-
-  function generalErrback(ex) {
-    ok(false,
-       'A rejection happened: ' +
-       (ex ? (ex + ': ' + ex.stack) : ''));
-  }
-
-  function runNextTask() {
-    if (pendingTasks.length) {
-      pendingPromise = maybeSpawn(pendingTasks.shift());
-      pendingPromise.then(runNextTask, generalErrback);
-    } else {
-      SimpleTest.finish();
-    }
-  }
-
-  // Trigger runNextTask after we think all JS files have been loaded.
-  // The primary goal is that we can call SimpleTest.finish() after all test
-  // code has been loaded and run.  We gate this based on the document's
-  // readyState.
-  var running = false;
-  function maybeStartRunning() {
-    if (!running && document.readyState === 'complete') {
-      running = true;
-      document.removeEventListener('readystateChange', maybeStartRunning);
-      // Defer to a subsequent turn of the event loop to let micro-tasks and any
-      // other clever setTimeout(0) instances run first.
-      window.setTimeout(runNextTask, 0);
-    }
-  }
-  document.addEventListener('readystatechange', maybeStartRunning);
-  maybeStartRunning();
-})(this);
--- a/dom/network/tests/chrome.ini
+++ b/dom/network/tests/chrome.ini
@@ -1,12 +1,10 @@
 [DEFAULT]
 support-files =
   tcpsocket_test.jsm
   test_tcpsocket_client_and_server_basics.js
-  add_task.js
   file_udpsocket_iframe.html
 
 [test_tcpsocket_jsm.html]
 [test_tcpsocket_client_and_server_basics.html]
-[test_tcpsocket_enabled_with_perm.html]
 [test_tcpsocket_legacy.html]
 [test_udpsocket.html]
--- a/dom/network/tests/mochitest.ini
+++ b/dom/network/tests/mochitest.ini
@@ -1,11 +1,9 @@
 [DEFAULT]
 support-files =
-  add_task.js
   worker_network_basics.js
 
 [test_network_basics.html]
 skip-if = toolkit == 'android'
 [test_network_basics_worker.html]
 skip-if = toolkit == 'android'
-[test_tcpsocket_default_permissions.html]
-[test_tcpsocket_enabled_no_perm.html]
+[test_tcpsocket_not_exposed_to_content.html]
--- a/dom/network/tests/tcpsocket_test.jsm
+++ b/dom/network/tests/tcpsocket_test.jsm
@@ -1,20 +1,16 @@
 this.EXPORTED_SYMBOLS = [
-  'createSocket', 'createServer', 'enablePrefsAndPermissions',
+  'createSocket', 'createServer',
   'socketCompartmentInstanceOfArrayBuffer'];
 
 this.createSocket = function(host, port, options) {
   return new TCPSocket(host, port, options);
 }
 
 this.createServer = function(port, options, backlog) {
   return new TCPServerSocket(port, options, backlog);
 }
 
-this.enablePrefsAndPermissions = function() {
-  return false;
-}
-
 // See test_tcpsocket_client_and_server_basics.html's version for rationale.
 this.socketCompartmentInstanceOfArrayBuffer = function(obj) {
   return obj instanceof ArrayBuffer;
 }
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.html
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.html
@@ -6,31 +6,27 @@ separate xpcshell incarnations.  This mi
 of bug 1084245 in order to get coverage of the tests from content.
 
 https://bugzilla.mozilla.org/show_bug.cgi?id=1084245
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1084245</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-  <script type="application/javascript" src="add_task.js"></script>
   <script type="application/javascript">
     function createServer(port, options, backlog) {
       return new TCPServerSocket(port, options, backlog);
     }
 
     function createSocket(host, port, options) {
       return new TCPSocket(host, port, options);
     }
 
-    function enablePrefsAndPermissions() {
-      return true;
-    }
-
     // In the JSM case, ArrayBuffers will be created in the compartment of the
     // JSM with different globals than the
     // test_tcpsocket_client_and_server_basics.js test logic sees, so we (and
     // tcpsocket_test.jsm) need to do something.  To avoid complexity relating
     // to wrappers and the varying nuances of the module scope and global scope
     // in JSM's (they differ on B2G), we hardcode ArrayBuffer rather than taking
     // a string that we look up, etc.
     function socketCompartmentInstanceOfArrayBuffer(obj) {
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -1,10 +1,17 @@
 'use strict';
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+// Bug 788960 and later bug 1329245 have taught us that attempting to connect to
+// a port that is not listening or is no longer listening fails to consistently
+// result in the error (or any) event we expect on Darwin/OSX/"OS X".
+const isOSX = (Services.appinfo.OS === "Darwin");
+const testConnectingToNonListeningPort = !isOSX;
+
 const SERVER_BACKLOG = -1;
 
 const SOCKET_EVENTS = ['open', 'data', 'drain', 'error', 'close'];
 
 function concatUint8Arrays(a, b) {
   let newArr = new Uint8Array(a.length + b.length);
   newArr.set(a, 0);
   newArr.set(b, a.length);
@@ -406,18 +413,21 @@ function* test_basics() {
 
   is(serverReceived.length < (2 * bigUint8Array.length), true, 'Received array length less than sent array length');
 
   // -- Close the listening server (and try to connect)
   // We want to verify that the server actually closes / stops listening when
   // we tell it to.
   listeningServer.close();
 
-  // - try and connect, get an error
-  clientSocket = createSocket('127.0.0.1', serverPort,
-                              { binaryType: 'arraybuffer' });
-  clientQueue = listenForEventsOnSocket(clientSocket, 'client');
-  is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect');
-  is(clientSocket.readyState, 'closed',
-     'client readyState should be closed after the failure to connect');
+  // (We don't run this check on OS X where it's flakey; see definition up top.)
+  if (testConnectingToNonListeningPort) {
+    // - try and connect, get an error
+    clientSocket = createSocket('127.0.0.1', serverPort,
+                                { binaryType: 'arraybuffer' });
+    clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+    is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect');
+    is(clientSocket.readyState, 'closed',
+       'client readyState should be closed after the failure to connect');
+  }
 }
 
 add_task(test_basics);
deleted file mode 100644
--- a/dom/network/tests/test_tcpsocket_default_permissions.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test to ensure TCPSocket permission is disabled by default</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test to ensure TCPSocket permission is disabled by default **/
-
-var caught = false;
-try {
-  new TCPSocket("localhost", 80, {})
-} catch (e) {
-  caught = true;
-}
-
-ok(caught, "TCPSocket should not exist by default");
-
-var caught = false;
-try {
-  navigator.mozTCPSocket.open("localhost", 80, {})
-} catch (e) {
-  caught = true;
-}
-
-ok(caught, "navigator.mozTCPSocket.open should not exist by default");
-
-</script>
-</pre>
-</body>
-</html>
deleted file mode 100644
--- a/dom/network/tests/test_tcpsocket_enabled_with_perm.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test to ensure TCPSocket permission enabled and open works with tcp-socket perm</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test to ensure TCPSocket permission being turned on enables 
-  navigator.mozTCPSocket, and mozTCPSocket.open works when
-  the tcp-socket permission has been granted.
-**/
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest);
-
-function runTest() {
-  ok('TCPSocket' in this, "TCPSocket should be accessible if dom.mozTCPSocket.enabled is true");
-
-  ok(new TCPSocket('localhost', 80), "TCPSocket constructor should work for content that has the tcp-socket permission");
-  ok(navigator.mozTCPSocket.open('localhost', 80), "navigator.mozTCPSocket.open should work for content that has the tcp-socket permission");
-  // This just helps the test harness clean up quickly
-  SpecialPowers.forceCC();
-  SpecialPowers.forceGC();
-  SimpleTest.finish();
-}
-</script>
-</pre>
-</body>
-</html>
--- a/dom/network/tests/test_tcpsocket_jsm.html
+++ b/dom/network/tests/test_tcpsocket_jsm.html
@@ -1,21 +1,21 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for 1207090</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
     Components.utils.import("chrome://mochitests/content/chrome/dom/network/tests/tcpsocket_test.jsm");
   </script>
-  <script type="application/javascript" src="add_task.js"></script>
   <script type="application/javascript" src="test_tcpsocket_client_and_server_basics.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1207090">Mozilla Bug 1207090</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
--- a/dom/network/tests/test_tcpsocket_legacy.html
+++ b/dom/network/tests/test_tcpsocket_legacy.html
@@ -14,19 +14,16 @@ Test of legacy navigator interface for o
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script>
   SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv(
-    { set: [ ['dom.mozTCPSocket.enabled', true] ] },
-    runTest);
 
   function runTest() {
     // See bug 903830; in e10s mode we never get to find out the localPort if we
     // let it pick a free port by choosing 0.  This is the same port the xpcshell
     // test was using.
     var serverPort = 8085;
 
     var listeningServer = navigator.mozTCPSocket.listen(serverPort,
@@ -49,11 +46,12 @@ Test of legacy navigator interface for o
       setTimeout(function() {
         // This just helps the test harness clean up quickly
         SpecialPowers.forceCC();
         SpecialPowers.forceGC();
         SimpleTest.finish();
       }, 0);
     }
   }
+  runTest(); // we used to invoke this as part of a moot pref-setting callback
 </script>
 </body>
 </html>
rename from dom/network/tests/test_tcpsocket_enabled_no_perm.html
rename to dom/network/tests/test_tcpsocket_not_exposed_to_content.html
--- a/dom/network/tests/test_tcpsocket_enabled_no_perm.html
+++ b/dom/network/tests/test_tcpsocket_not_exposed_to_content.html
@@ -6,25 +6,20 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
+/**
+ * TCPSocket and its legacy mozTCPSocket variant should never be exposed to
+ * content.
+ */
 
-/** Test to ensure TCPSocket preference being turned on does not enable
-  navigator.mozTCPSocket.
-**/
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest);
-function runTest() {
-  is('TCPSocket' in this, false, "TCPSocket should not be accessible if dom.mozTCPSocket.enabled is true");
-  is('TCPServerSocket' in this, false, "TCPServerSocket should not be accessible if dom.mozTCPSocket.enabled is true");
-  is('mozTCPSocket' in navigator, false, "mozTCPSocket should not be accessible if dom.mozTCPSocket.enabled is true");
-
-  SimpleTest.finish();
-}
+is('TCPSocket' in this, false, "TCPSocket should not be accessible to content");
+is('TCPServerSocket' in this, false, "TCPServerSocket should not be accessible to content");
+is('mozTCPSocket' in navigator, false, "mozTCPSocket should not be accessible to content");
 </script>
 </pre>
 </body>
 </html>
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -24,45 +24,36 @@ namespace js {
 
 // A call stack can be specified to the JS engine such that all JS entry/exits
 // to functions push/pop an entry to/from the specified stack.
 //
 // For more detailed information, see vm/GeckoProfiler.h.
 //
 class ProfileEntry
 {
-    // All fields are marked volatile to prevent the compiler from re-ordering
-    // instructions. Namely this sequence:
-    //
-    //    entry[size] = ...;
-    //    size++;
-    //
-    // If the size modification were somehow reordered before the stores, then
-    // if a sample were taken it would be examining bogus information.
-    //
-    // A ProfileEntry represents both a C++ profile entry and a JS one.
+    // A ProfileEntry represents either a C++ profile entry or a JS one.
 
     // Descriptive label for this entry. Must be a static string! Can be an
     // empty string, but not a null pointer.
-    const char * volatile label_;
+    const char* label_;
 
     // An additional descriptive string of this entry which is combined with
     // |label_| in profiler output. Need not be (and usually isn't) static. Can
     // be null.
-    const char * volatile dynamicString_;
+    const char* dynamicString_;
 
     // Stack pointer for non-JS entries, the script pointer otherwise.
-    void * volatile spOrScript;
+    void* spOrScript;
 
     // Line number for non-JS entries, the bytecode offset otherwise.
-    int32_t volatile lineOrPcOffset;
+    int32_t lineOrPcOffset;
 
     // Bits 0..1 hold the Kind. Bits 2..3 are unused. Bits 4..12 hold the
     // Category.
-    uint32_t volatile kindAndCategory_;
+    uint32_t kindAndCategory_;
 
     static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
 
   public:
     enum class Kind : uint32_t {
         // A normal C++ frame.
         CPP_NORMAL = 0,
 
@@ -98,95 +89,90 @@ class ProfileEntry
         LAST     = EVENTS,
 
         CATEGORY_MASK = ~uint32_t(Kind::KIND_MASK),
     };
 
     static_assert((uint32_t(Category::FIRST) & uint32_t(Kind::KIND_MASK)) == 0,
                   "Category overlaps with Kind");
 
-    // All of these methods are marked with the 'volatile' keyword because the
-    // Gecko Profiler's representation of the stack is stored such that all
-    // ProfileEntry instances are volatile. These methods would not be
-    // available unless they were marked as volatile as well.
-
-    bool isCpp() const volatile
+    bool isCpp() const
     {
         Kind k = kind();
         return k == Kind::CPP_NORMAL || k == Kind::CPP_MARKER_FOR_JS;
     }
 
-    bool isJs() const volatile
+    bool isJs() const
     {
         Kind k = kind();
         return k == Kind::JS_NORMAL || k == Kind::JS_OSR;
     }
 
-    void setLabel(const char* aLabel) volatile { label_ = aLabel; }
-    const char* label() const volatile { return label_; }
+    void setLabel(const char* aLabel) { label_ = aLabel; }
+    const char* label() const { return label_; }
 
-    const char* dynamicString() const volatile { return dynamicString_; }
+    const char* dynamicString() const { return dynamicString_; }
 
     void initCppFrame(const char* aLabel, const char* aDynamicString, void* sp, uint32_t aLine,
-                      Kind aKind, Category aCategory) volatile
+                      Kind aKind, Category aCategory)
     {
         label_ = aLabel;
         dynamicString_ = aDynamicString;
         spOrScript = sp;
         lineOrPcOffset = static_cast<int32_t>(aLine);
         kindAndCategory_ = uint32_t(aKind) | uint32_t(aCategory);
         MOZ_ASSERT(isCpp());
     }
 
     void initJsFrame(const char* aLabel, const char* aDynamicString, JSScript* aScript,
-                     jsbytecode* aPc) volatile
+                     jsbytecode* aPc)
     {
         label_ = aLabel;
         dynamicString_ = aDynamicString;
         spOrScript = aScript;
         lineOrPcOffset = pcToOffset(aScript, aPc);
         kindAndCategory_ = uint32_t(Kind::JS_NORMAL) | uint32_t(Category::JS);
         MOZ_ASSERT(isJs());
     }
 
-    void setKind(Kind aKind) volatile {
+    void setKind(Kind aKind) {
         kindAndCategory_ = uint32_t(aKind) | uint32_t(category());
     }
 
-    Kind kind() const volatile {
+    Kind kind() const {
         return Kind(kindAndCategory_ & uint32_t(Kind::KIND_MASK));
     }
 
-    Category category() const volatile {
+    Category category() const {
         return Category(kindAndCategory_ & uint32_t(Category::CATEGORY_MASK));
     }
 
-    void* stackAddress() const volatile {
+    void* stackAddress() const {
         MOZ_ASSERT(!isJs());
         return spOrScript;
     }
 
-    JS_PUBLIC_API(JSScript*) script() const volatile;
+    JS_PUBLIC_API(JSScript*) script() const;
 
-    uint32_t line() const volatile {
+    uint32_t line() const {
         MOZ_ASSERT(!isJs());
         return static_cast<uint32_t>(lineOrPcOffset);
     }
 
     // Note that the pointer returned might be invalid.
-    JSScript* rawScript() const volatile {
+    JSScript* rawScript() const {
         MOZ_ASSERT(isJs());
         return (JSScript*)spOrScript;
     }
 
     // We can't know the layout of JSScript, so look in vm/GeckoProfiler.cpp.
-    JS_FRIEND_API(jsbytecode*) pc() const volatile;
-    void setPC(jsbytecode* pc) volatile;
+    JS_FRIEND_API(jsbytecode*) pc() const;
+    void setPC(jsbytecode* pc);
 
-    void trace(JSTracer* trc) volatile;
+    void trace(JSTracer* trc);
 
     // The offset of a pc into a script's code can actually be 0, so to
     // signify a nullptr pc, use a -1 index. This is checked against in
     // pc() and setPC() to set/get the right pc.
     static const int32_t NullPCOffset = -1;
 };
 
 JS_FRIEND_API(void)
@@ -195,19 +181,34 @@ 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
 
-// The PseudoStack members are accessed in parallel by multiple threads: the
-// profiler's sampler thread reads these members while other threads modify
-// them.
+// Each thread has its own PseudoStack. That thread modifies the PseudoStack,
+// pushing and popping elements as necessary.
+//
+// The PseudoStack is also read periodically by the profiler's sampler thread.
+// This happens only when the thread that owns the PseudoStack is suspended. So
+// there are no genuine parallel accesses.
+//
+// However, it is possible for pushing/popping to be interrupted by a periodic
+// sample. Because of this, we need pushing/popping to be effectively atomic.
+//
+// - When pushing a new entry, we increment the stack pointer -- making the new
+//   entry visible to the sampler thread -- only after the new entry has been
+//   fully written. The stack pointer is Atomic<> (with SequentiallyConsistent
+//   semantics) to ensure the incrementing is not reordered before the writes.
+//
+// - When popping an old entry, the only operation is the decrementing of the
+//   stack pointer, which is obviously atomic.
+//
 class PseudoStack
 {
   public:
     PseudoStack()
       : stackPointer(0)
     {}
 
     ~PseudoStack() {
@@ -250,18 +251,18 @@ class PseudoStack
     // No copying.
     PseudoStack(const PseudoStack&) = delete;
     void operator=(const PseudoStack&) = delete;
 
   public:
     static const uint32_t MaxEntries = 1024;
 
     // The stack entries.
-    js::ProfileEntry volatile entries[MaxEntries];
+    js::ProfileEntry entries[MaxEntries];
 
     // This may exceed MaxEntries, so instead use the stackSize() method to
     // determine the number of valid samples in entries. When this is less
     // than MaxEntries, it refers to the first free entry past the top of the
     // in-use stack (i.e. entries[stackPointer - 1] is the top stack entry).
-    mozilla::Atomic<uint32_t> stackPointer;
+    mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> stackPointer;
 };
 
 #endif  /* js_ProfilingStack_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1353356.js
@@ -0,0 +1,65 @@
+// |jit-test| allow-oom; --fuzzing-safe
+
+var lfLogBuffer = `
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-endofdata
+    setJitCompilerOption("ion.warmup.trigger", 4);
+    var g = newGlobal();
+    g.debuggeeGlobal = this;
+    g.eval("(" + function () {
+        dbg = new Debugger(debuggeeGlobal);
+        dbg.onExceptionUnwind = function (frame, exc) {
+            var s = '!';
+            for (var f = frame; f; f = f.older)
+            debuggeeGlobal.log += s;
+        };
+    } + ")();");
+      j('Number.prototype.toSource.call([])');
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-selectmode 4
+//corefuzz-dcd-endofdata
+}
+//corefuzz-dcd-endofdata
+//corefuzz-dcd-selectmode 5
+//corefuzz-dcd-endofdata
+oomTest(() => i({
+    new  : (true  ),
+    thisprops: true
+}));
+`;
+lfLogBuffer = lfLogBuffer.split('\n');
+var lfRunTypeId = -1;
+var lfCodeBuffer = "";
+while (true) {
+    var line = lfLogBuffer.shift();
+    if (line == null) {
+        break;
+    } else if (line == "//corefuzz-dcd-endofdata") {
+        loadFile(lfCodeBuffer);
+        lfCodeBuffer = "";
+        loadFile(line);
+    } else {
+        lfCodeBuffer += line + "\n";
+    }
+}
+if (lfCodeBuffer) loadFile(lfCodeBuffer);
+function loadFile(lfVarx) {
+    try {
+        if (lfVarx.indexOf("//corefuzz-dcd-selectmode ") === 0) {
+            lfRunTypeId = parseInt(lfVarx.split(" ")[1]) % 6;
+        } else {
+            switch (lfRunTypeId) {
+                case 4:
+                    oomTest(function() {
+                        let m = parseModule(lfVarx);
+                    });
+                    break;
+                default:
+                    evaluate(lfVarx);
+            }
+        }
+    } catch (lfVare) {}
+}
--- a/js/src/make-source-package.sh
+++ b/js/src/make-source-package.sh
@@ -149,16 +149,20 @@ case $cmd in
         ${TOPSRCDIR}/memory/fallible \
         ${TOPSRCDIR}/memory/mozalloc \
         ${TOPSRCDIR}/memory/mozjemalloc \
         ${tgtpath}/memory
 
     # remove *.pyc and *.pyo files if any
     find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" |xargs rm -f
 
+    # Remove non-JS Cargo.toml files (for example, the invalid Cargo.toml files
+    # used for some testing).
+    find ${tgtpath} -type f -name Cargo.toml | grep -v js | xargs rm -f
+
     # copy or create INSTALL
     if [ -e ${STAGING}/INSTALL ]; then
         cp ${STAGING}/INSTALL ${tgtpath}
     else
         cat <<INSTALL_EOF >${tgtpath}/INSTALL
 Full build documentation for SpiderMonkey is hosted on MDN:
   https://developer.mozilla.org/en-US/docs/SpiderMonkey/Build_Documentation
 
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -234,25 +234,25 @@ GeckoProfiler::exit(JSScript* script, JS
         // Bug 822041
         if (!pseudoStack_->entries[sp].isJs()) {
             fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
             fprintf(stderr, " entries=%p size=%u/%u\n",
                             (void*) pseudoStack_->entries,
                             uint32_t(pseudoStack_->stackPointer),
                             PseudoStack::MaxEntries);
             for (int32_t i = sp; i >= 0; i--) {
-                volatile ProfileEntry& entry = pseudoStack_->entries[i];
+                ProfileEntry& entry = pseudoStack_->entries[i];
                 if (entry.isJs())
                     fprintf(stderr, "  [%d] JS %s\n", i, entry.dynamicString());
                 else
                     fprintf(stderr, "  [%d] C line %d %s\n", i, entry.line(), entry.dynamicString());
             }
         }
 
-        volatile ProfileEntry& entry = pseudoStack_->entries[sp];
+        ProfileEntry& entry = pseudoStack_->entries[sp];
         MOZ_ASSERT(entry.isJs());
         MOZ_ASSERT(entry.script() == script);
         MOZ_ASSERT(strcmp((const char*) entry.dynamicString(), dynamicString) == 0);
     }
 #endif
 }
 
 /*
@@ -305,17 +305,17 @@ GeckoProfiler::allocProfileString(JSScri
     }
 
     MOZ_ASSERT(ret == len, "Computed length should match actual length!");
 
     return cstr;
 }
 
 void
-GeckoProfiler::trace(JSTracer* trc) volatile
+GeckoProfiler::trace(JSTracer* trc)
 {
     if (pseudoStack_) {
         size_t size = pseudoStack_->stackSize();
         for (size_t i = 0; i < size; i++)
             pseudoStack_->entries[i].trace(trc);
     }
 }
 
@@ -348,17 +348,17 @@ GeckoProfiler::checkStringsMapAfterMovin
         CheckGCThingAfterMovingGC(script);
         auto ptr = locked->lookup(script);
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
     }
 }
 #endif
 
 void
-ProfileEntry::trace(JSTracer* trc) volatile
+ProfileEntry::trace(JSTracer* trc)
 {
     if (isJs()) {
         JSScript* s = rawScript();
         TraceNullableRoot(trc, &s, "ProfileEntry script");
         spOrScript = s;
     }
 }
 
@@ -435,38 +435,38 @@ GeckoProfilerBaselineOSRMarker::GeckoPro
         profiler = nullptr;
         return;
     }
 
     spBefore_ = sp;
     if (sp == 0)
         return;
 
-    volatile ProfileEntry& entry = profiler->pseudoStack_->entries[sp - 1];
+    ProfileEntry& entry = profiler->pseudoStack_->entries[sp - 1];
     MOZ_ASSERT(entry.kind() == ProfileEntry::Kind::JS_NORMAL);
     entry.setKind(ProfileEntry::Kind::JS_OSR);
 }
 
 GeckoProfilerBaselineOSRMarker::~GeckoProfilerBaselineOSRMarker()
 {
     if (profiler == nullptr)
         return;
 
     uint32_t sp = profiler->stackPointer();
     MOZ_ASSERT(spBefore_ == sp);
     if (sp == 0)
         return;
 
-    volatile ProfileEntry& entry = profiler->stack()[sp - 1];
+    ProfileEntry& entry = profiler->stack()[sp - 1];
     MOZ_ASSERT(entry.kind() == ProfileEntry::Kind::JS_OSR);
     entry.setKind(ProfileEntry::Kind::JS_NORMAL);
 }
 
 JS_PUBLIC_API(JSScript*)
-ProfileEntry::script() const volatile
+ProfileEntry::script() const
 {
     MOZ_ASSERT(isJs());
     auto script = reinterpret_cast<JSScript*>(spOrScript);
     if (!script)
         return nullptr;
 
     // If profiling is supressed then we can't trust the script pointers to be
     // valid as they could be in the process of being moved by a compacting GC
@@ -479,33 +479,33 @@ ProfileEntry::script() const volatile
     if (!cx->isProfilerSamplingEnabled())
         return nullptr;
 
     MOZ_ASSERT(!IsForwarded(script));
     return script;
 }
 
 JS_FRIEND_API(jsbytecode*)
-ProfileEntry::pc() const volatile
+ProfileEntry::pc() const
 {
     MOZ_ASSERT(isJs());
     if (lineOrPcOffset == NullPCOffset)
         return nullptr;
 
     JSScript* script = this->script();
     return script ? script->offsetToPC(lineOrPcOffset) : nullptr;
 }
 
 /* static */ int32_t
 ProfileEntry::pcToOffset(JSScript* aScript, jsbytecode* aPc) {
     return aPc ? aScript->pcToOffset(aPc) : NullPCOffset;
 }
 
 void
-ProfileEntry::setPC(jsbytecode* pc) volatile
+ProfileEntry::setPC(jsbytecode* pc)
 {
     MOZ_ASSERT(isJs());
     JSScript* script = this->script();
     MOZ_ASSERT(script); // This should not be called while profiling is suppressed.
     lineOrPcOffset = pcToOffset(script, pc);
 }
 
 JS_FRIEND_API(void)
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -137,17 +137,17 @@ class GeckoProfiler
     UniqueChars allocProfileString(JSScript* script, JSFunction* function);
 
   public:
     explicit GeckoProfiler(JSRuntime* rt);
 
     bool init();
 
     uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; }
-    volatile ProfileEntry* stack() { return pseudoStack_->entries; }
+    ProfileEntry* stack() { return pseudoStack_->entries; }
 
     /* management of whether instrumentation is on or off */
     bool enabled() { MOZ_ASSERT_IF(enabled_, installed()); return enabled_; }
     bool installed() { return pseudoStack_ != nullptr; }
     MOZ_MUST_USE bool enable(bool enabled);
     void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
     bool slowAssertionsEnabled() { return slowAssertions; }
 
@@ -184,17 +184,17 @@ class GeckoProfiler
     /* meant to be used for testing, not recommended to call in normal code */
     size_t stringsCount();
     void stringsReset();
 
     uint32_t* addressOfEnabled() {
         return &enabled_;
     }
 
-    void trace(JSTracer* trc) volatile;
+    void trace(JSTracer* trc);
     void fixupStringsMapAfterMovingGC();
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkStringsMapAfterMovingGC();
 #endif
 };
 
 inline size_t
 GeckoProfiler::stringsCount()
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1534,38 +1534,36 @@ jit::JitActivation::getRematerializedFra
             ReportOutOfMemory(cx);
             return nullptr;
         }
     }
 
     uint8_t* top = iter.fp();
     RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
     if (!p) {
-        RematerializedFrameVector empty(cx);
-        if (!rematerializedFrames_->add(p, top, Move(empty))) {
-            ReportOutOfMemory(cx);
-            return nullptr;
-        }
+        RematerializedFrameVector frames(cx);
 
         // The unit of rematerialization is an uninlined frame and its inlined
         // frames. Since inlined frames do not exist outside of snapshots, it
         // is impossible to synchronize their rematerialized copies to
         // preserve identity. Therefore, we always rematerialize an uninlined
         // frame and all its inlined frames at once.
         InlineFrameIterator inlineIter(cx, &iter);
         MaybeReadFallback recover(cx, this, &iter);
 
         // Frames are often rematerialized with the cx inside a Debugger's
         // compartment. To recover slots and to create CallObjects, we need to
         // be in the activation's compartment.
         AutoCompartmentUnchecked ac(cx, compartment_);
 
-        if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover,
-                                                            p->value()))
-        {
+        if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover, frames))
+            return nullptr;
+
+        if (!rematerializedFrames_->add(p, top, Move(frames))) {
+            ReportOutOfMemory(cx);
             return nullptr;
         }
 
         // See comment in unsetPrevUpToDateUntil.
         DebugEnvironments::unsetPrevUpToDateUntil(cx, p->value()[inlineDepth]);
     }
 
     return p->value()[inlineDepth];
--- a/mobile/android/chrome/geckoview/geckoview.js
+++ b/mobile/android/chrome/geckoview/geckoview.js
@@ -43,17 +43,20 @@ var ModuleManager = {
       return;
     }
     delete this.modules[aType];
   }
 };
 
 function startup() {
   ModuleManager.init();
+
+  // GeckoViewNavigation needs to go first because nsIDOMBrowserWindow must set up
+  // before the first remote browser. Bug 1365364.
+  ModuleManager.add("resource://gre/modules/GeckoViewNavigation.jsm",
+                    "GeckoViewNavigation");
   ModuleManager.add("resource://gre/modules/GeckoViewSettings.jsm",
                     "GeckoViewSettings");
   ModuleManager.add("resource://gre/modules/GeckoViewContent.jsm",
                     "GeckoViewContent");
-  ModuleManager.add("resource://gre/modules/GeckoViewNavigation.jsm",
-                    "GeckoViewNavigation");
   ModuleManager.add("resource://gre/modules/GeckoViewProgress.jsm",
                     "GeckoViewProgress");
 }
--- a/testing/talos/talos/tests/video/video_playback.html
+++ b/testing/talos/talos/tests/video/video_playback.html
@@ -15,17 +15,18 @@ var start2 = 0;
 var paintedFramesStart2 = 0;
 var testIndex = 0;
 var test = [
   'testsrc.240p.120fps.mp4',
   'testsrc.480p.60fps.webm',
   'testsrc.1080p.60fps.mp4',
 ];
 var viewModeIndex = 0;
-var viewMode = ['fullscreen', 1, 1.1, 2];
+// Remove fullscreen mode since it causes intermittent failures on try server. See bug 1192317.
+var viewMode = [1, 1.1, 2];
 var testResult = {names: [], values: []};
 
 function init() {
   TalosPowersContent.focus(content_focused)
 }
 
 function content_focused() {
   vdo = document.getElementById('vdo');
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -581,26 +581,26 @@ private:
 };
 
 MOZ_THREAD_LOCAL(ThreadInfo*) TLSInfo::sThreadInfo;
 
 // Although you can access a thread's PseudoStack via TLSInfo::sThreadInfo, we
 // also have a second TLS pointer directly to the PseudoStack. Here's why.
 //
 // - We need to be able to push to and pop from the PseudoStack in
-//   profiler_call_{enter,exit}.
+//   ProfilerStackFrameRAII.
 //
 // - Those two functions are hot and must be defined in GeckoProfiler.h so they
 //   can be inlined.
 //
 // - We don't want to expose TLSInfo (and ThreadInfo) in GeckoProfiler.h.
 //
 // This second pointer isn't ideal, but does provide a way to satisfy those
 // constraints. TLSInfo manages it, except for the uses in
-// profiler_call_{enter,exit}.
+// ProfilerStackFrameRAII.
 MOZ_THREAD_LOCAL(PseudoStack*) sPseudoStack;
 
 // The name of the main thread.
 static const char* const kMainThreadName = "GeckoMain";
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN tick/unwinding code
 
@@ -719,18 +719,17 @@ AddDynamicCodeLocationTag(ProfileBuffer*
 
     // Cast to *((void**) to pass the text data to a void*.
     aBuffer->addTag(ProfileBufferEntry::EmbeddedString(*((void**)(&text[0]))));
   }
 }
 
 static void
 AddPseudoEntry(PSLockRef aLock, ProfileBuffer* aBuffer,
-               volatile js::ProfileEntry& entry,
-               NotNull<RacyThreadInfo*> aRacyInfo)
+               js::ProfileEntry& entry, NotNull<RacyThreadInfo*> aRacyInfo)
 {
   MOZ_ASSERT(entry.kind() == js::ProfileEntry::Kind::CPP_NORMAL ||
              entry.kind() == js::ProfileEntry::Kind::JS_NORMAL);
 
   int lineno = -1;
 
   // First entry has kind CodeLocation.
   const char* label = entry.label();
@@ -822,17 +821,17 @@ struct AutoWalkJSStack
   }
 };
 
 static void
 MergeStacksIntoProfile(PSLockRef aLock, ProfileBuffer* aBuffer,
                        const TickSample& aSample, NativeStack& aNativeStack)
 {
   NotNull<RacyThreadInfo*> racyInfo = aSample.mRacyInfo;
-  volatile js::ProfileEntry* pseudoEntries = racyInfo->entries;
+  js::ProfileEntry* pseudoEntries = racyInfo->entries;
   uint32_t pseudoCount = racyInfo->stackSize();
   JSContext* context = aSample.mJSContext;
 
   // Make a copy of the JS stack into a JSFrame array. This is necessary since,
   // like the native stack, the JS stack is iterated youngest-to-oldest and we
   // need to iterate oldest-to-youngest when adding entries to aInfo.
 
   // Synchronous sampling reports an invalid buffer generation to
@@ -897,17 +896,17 @@ MergeStacksIntoProfile(PSLockRef aLock, 
   // Iterate as long as there is at least one frame remaining.
   while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
     // There are 1 to 3 frames available. Find and add the oldest.
     uint8_t* pseudoStackAddr = nullptr;
     uint8_t* jsStackAddr = nullptr;
     uint8_t* nativeStackAddr = nullptr;
 
     if (pseudoIndex != pseudoCount) {
-      volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
+      js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
 
       if (pseudoEntry.isCpp()) {
         lastPseudoCppStackAddr = (uint8_t*) pseudoEntry.stackAddress();
       }
 
       // Skip any JS_OSR frames. Such frames are used when the JS interpreter
       // enters a jit frame on a loop edge (via on-stack-replacement, or OSR).
       // To avoid both the pseudoframe and jit frame being recorded (and
@@ -947,17 +946,17 @@ MergeStacksIntoProfile(PSLockRef aLock, 
     MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
                                jsStackAddr != nativeStackAddr);
     MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
                                    nativeStackAddr != jsStackAddr);
 
     // Check to see if pseudoStack frame is top-most.
     if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
       MOZ_ASSERT(pseudoIndex < pseudoCount);
-      volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
+      js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
 
       // Pseudo-frames with the CPP_MARKER_FOR_JS kind are just annotations and
       // should not be recorded in the profile.
       if (pseudoEntry.kind() != js::ProfileEntry::Kind::CPP_MARKER_FOR_JS) {
         AddPseudoEntry(aLock, aBuffer, pseudoEntry, racyInfo);
       }
       pseudoIndex++;
       continue;
@@ -1095,17 +1094,17 @@ DoNativeBacktrace(PSLockRef aLock, Profi
 
   // The pseudostack contains an "EnterJIT" frame whenever we enter
   // JIT code with profiling enabled; the stack pointer value points
   // the saved registers.  We use this to unwind resume unwinding
   // after encounting JIT code.
   for (uint32_t i = racyInfo->stackSize(); i > 0; --i) {
     // The pseudostack grows towards higher indices, so we iterate
     // backwards (from callee to caller).
-    volatile js::ProfileEntry& entry = racyInfo->entries[i - 1];
+    js::ProfileEntry& entry = racyInfo->entries[i - 1];
     if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
       // Found JIT entry frame.  Unwind up to that point (i.e., force
       // the stack walk to stop before the block of saved registers;
       // note that it yields nondecreasing stack pointers), then restore
       // the saved state.
       uint32_t* vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
 
       nativeStack.count += EHABIStackWalk(*mcontext,
@@ -2852,17 +2851,17 @@ profiler_get_backtrace_noalloc(char *out
 
   PseudoStack* pseudoStack = TLSInfo::Stack();
   if (!pseudoStack) {
     return;
   }
 
   bool includeDynamicString = !ActivePS::FeaturePrivacy(lock);
 
-  volatile js::ProfileEntry* pseudoEntries = pseudoStack->entries;
+  js::ProfileEntry* pseudoEntries = pseudoStack->entries;
   uint32_t pseudoCount = pseudoStack->stackSize();
 
   for (uint32_t i = 0; i < pseudoCount; i++) {
     const char* label = pseudoEntries[i].label();
     const char* dynamicString =
       includeDynamicString ? pseudoEntries[i].dynamicString() : nullptr;
     size_t labelLength = strlen(label);
     if (dynamicString) {
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -93,17 +93,18 @@ using UniqueProfilerBacktrace =
 // profile buffer. So there's one string copy operation, and it happens at
 // sample time.
 // Compare this to the plain PROFILER_LABEL macro, which only accepts literal
 // strings: When the pseudo stack frames generated by PROFILER_LABEL are
 // sampled, no string copy needs to be made because the profile buffer can
 // just store the raw pointers to the literal strings. Consequently,
 // PROFILER_LABEL frames take up considerably less space in the profile buffer
 // than PROFILER_LABEL_DYNAMIC frames.
-#define PROFILER_LABEL_DYNAMIC(name_space, info, category, str) do {} while (0)
+#define PROFILER_LABEL_DYNAMIC(name_space, info, category, dynamicStr) \
+  do {} while (0)
 
 // Insert a marker in the profile timeline. This is useful to delimit something
 // important happening such as the first paint. Unlike profiler_label that are
 // only recorded if a sample is collected while it is active, marker will always
 // be collected.
 #define PROFILER_MARKER(marker_name) do {} while (0)
 #define PROFILER_MARKER_PAYLOAD(marker_name, payload) \
   do { \
@@ -123,30 +124,30 @@ using UniqueProfilerBacktrace =
 #define PROFILER_FUNC_VOID(decl) void decl;
 
 // we want the class and function name but can't easily get that using preprocessor macros
 // __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
 
 #define PROFILER_LABEL(name_space, info, category) \
   PROFILER_PLATFORM_TRACING(name_space "::" info) \
   mozilla::ProfilerStackFrameRAII \
-  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(name_space "::" info, category, \
-                                             __LINE__)
+  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(name_space "::" info, nullptr, \
+                                             __LINE__, category)
 
 #define PROFILER_LABEL_FUNC(category) \
   PROFILER_PLATFORM_TRACING(PROFILER_FUNCTION_NAME) \
   mozilla::ProfilerStackFrameRAII \
-  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(PROFILER_FUNCTION_NAME, category, \
-                                             __LINE__)
+  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(PROFILER_FUNCTION_NAME, nullptr, \
+                                             __LINE__, category)
 
-#define PROFILER_LABEL_DYNAMIC(name_space, info, category, str) \
+#define PROFILER_LABEL_DYNAMIC(name_space, info, category, dynamicStr) \
   PROFILER_PLATFORM_TRACING(name_space "::" info) \
-  mozilla::ProfilerStackFrameDynamicRAII \
-  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(name_space "::" info, category, \
-                                             __LINE__, str)
+  mozilla::ProfilerStackFrameRAII \
+  PROFILER_APPEND_LINE_NUMBER(profiler_raii)(name_space "::" info, dynamicStr, \
+                                             __LINE__, category)
 
 #define PROFILER_MARKER(marker_name) profiler_add_marker(marker_name)
 #define PROFILER_MARKER_PAYLOAD(marker_name, payload) \
   profiler_add_marker(marker_name, payload)
 
 #endif  // defined(MOZ_GECKO_PROFILER)
 
 // Higher-order macro containing all the feature info in one place. Define
@@ -400,61 +401,20 @@ PROFILER_FUNC(void* profiler_get_stack_t
 // Make sure that we can use std::min here without the Windows headers messing with us.
 #ifdef min
 # undef min
 #endif
 
 class nsISupports;
 class ProfilerMarkerPayload;
 
-// This exists purely for profiler_call_{enter,exit}. See the comment on the
+// This exists purely for ProfilerStackFrameRAII. See the comment on the
 // definition in platform.cpp for details.
 extern MOZ_THREAD_LOCAL(PseudoStack*) sPseudoStack;
 
-// Returns a handle to pass on exit. This can check that we are popping the
-// correct callstack. Operates the same whether the profiler is active or not.
-//
-// A short-lived, non-owning PseudoStack reference is created between each
-// profiler_call_enter() / profiler_call_exit() call pair. RAII objects (e.g.
-// ProfilerStackFrameRAII) ensure that these calls are balanced. Furthermore,
-// the RAII objects exist within the thread itself, which means they are
-// necessarily bounded by the lifetime of the thread, which ensures that the
-// references held can't be used after the PseudoStack is destroyed.
-inline void*
-profiler_call_enter(const char* aLabel, js::ProfileEntry::Category aCategory,
-                    void* aFrameAddress, uint32_t aLine,
-                    const char* aDynamicString = nullptr)
-{
-  // This function runs both on and off the main thread.
-
-  PseudoStack* pseudoStack = sPseudoStack.get();
-  if (!pseudoStack) {
-    return pseudoStack;
-  }
-  pseudoStack->pushCppFrame(aLabel, aDynamicString, aFrameAddress, aLine,
-                            js::ProfileEntry::Kind::CPP_NORMAL, aCategory);
-
-  // The handle is meant to support future changes but for now it is simply
-  // used to avoid having to call TLSInfo::RacyInfo() in profiler_call_exit().
-  return pseudoStack;
-}
-
-inline void
-profiler_call_exit(void* aHandle)
-{
-  // This function runs both on and off the main thread.
-
-  if (!aHandle) {
-    return;
-  }
-
-  PseudoStack* pseudoStack = static_cast<PseudoStack*>(aHandle);
-  pseudoStack->pop();
-}
-
 // Adds a marker to the PseudoStack. A no-op if the profiler is inactive or in
 // privacy mode.
 void profiler_add_marker(const char* aMarkerName,
                          ProfilerMarkerPayload* aPayload = nullptr);
 
 #define PROFILER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
 #define PROFILER_APPEND_LINE_NUMBER_EXPAND(id, line) \
   PROFILER_APPEND_LINE_NUMBER_PASTE(id, line)
@@ -502,51 +462,51 @@ void profiler_add_marker(const char* aMa
 // In the case of profiler_get_backtrace we know that we only need enough space
 // for a single backtrace.
 #define PROFILER_GET_BACKTRACE_ENTRIES 1000
 
 #define PROFILER_DEFAULT_INTERVAL 1
 
 namespace mozilla {
 
+// This class creates a non-owning PseudoStack reference. Objects of this class
+// are stack-allocated, and so exist within a thread, and are thus bounded by
+// the lifetime of the thread, which ensures that the references held can't be
+// used after the PseudoStack is destroyed.
 class MOZ_RAII ProfilerStackFrameRAII {
 public:
-  // We only copy the strings at save time, so to take multiple parameters we'd
-  // need to copy them then.
-  ProfilerStackFrameRAII(const char* aLabel,
-    js::ProfileEntry::Category aCategory, uint32_t aLine
-    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+  ProfilerStackFrameRAII(const char* aLabel, const char* aDynamicString,
+                         uint32_t aLine, js::ProfileEntry::Category aCategory
+                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    mHandle = profiler_call_enter(aLabel, aCategory, this, aLine);
-  }
-  ~ProfilerStackFrameRAII() {
-    profiler_call_exit(mHandle);
-  }
-private:
-  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-  void* mHandle;
-};
+
+    // This function runs both on and off the main thread.
 
-class MOZ_RAII ProfilerStackFrameDynamicRAII {
-public:
-  ProfilerStackFrameDynamicRAII(const char* aLabel,
-    js::ProfileEntry::Category aCategory, uint32_t aLine,
-    const char* aDynamicString)
-  {
-    mHandle = profiler_call_enter(aLabel, aCategory, this, aLine,
-                                  aDynamicString);
+    mPseudoStack = sPseudoStack.get();
+    if (mPseudoStack) {
+      mPseudoStack->pushCppFrame(aLabel, aDynamicString, this, aLine,
+                                js::ProfileEntry::Kind::CPP_NORMAL, aCategory);
+    }
   }
 
-  ~ProfilerStackFrameDynamicRAII() {
-    profiler_call_exit(mHandle);
+  ~ProfilerStackFrameRAII()
+  {
+    // This function runs both on and off the main thread.
+
+    if (mPseudoStack) {
+      mPseudoStack->pop();
+    }
   }
 
 private:
-  void* mHandle;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  // We save a PseudoStack pointer in the ctor so we don't have to redo the TLS
+  // lookup in the dtor.
+  PseudoStack* mPseudoStack;
 };
 
 } // namespace mozilla
 
 PseudoStack* profiler_get_pseudo_stack();
 
 void profiler_set_js_context(JSContext* aCx);
 void profiler_clear_js_context();
--- a/tools/profiler/tests/gtest/GeckoProfiler.cpp
+++ b/tools/profiler/tests/gtest/GeckoProfiler.cpp
@@ -578,25 +578,21 @@ TEST(GeckoProfiler, PseudoStack)
 
     profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
                    features, filters, MOZ_ARRAY_LENGTH(filters));
 
     ASSERT_TRUE(profiler_get_backtrace());
   }
 
 #if defined(MOZ_GECKO_PROFILER)
-  ProfilerStackFrameRAII raii1("A", js::ProfileEntry::Category::STORAGE, 888);
-  ProfilerStackFrameDynamicRAII raii2("A", js::ProfileEntry::Category::STORAGE,
-                                      888, dynamic.get());
-  void* handle = profiler_call_enter("A", js::ProfileEntry::Category::NETWORK,
-                                     this, 999);
+  ProfilerStackFrameRAII raii1("A", nullptr, 888,
+                               js::ProfileEntry::Category::STORAGE);
+  ProfilerStackFrameRAII raii2("A", dynamic.get(), 888,
+                               js::ProfileEntry::Category::NETWORK);
   ASSERT_TRUE(profiler_get_backtrace());
-  profiler_call_exit(handle);
-
-  profiler_call_exit(nullptr);  // a no-op
 #endif
 
   profiler_stop();
 
   ASSERT_TRUE(!profiler_get_profile());
 }
 
 TEST(GeckoProfiler, Bug1355807)
--- a/xpcom/threads/ThreadStackHelper.cpp
+++ b/xpcom/threads/ThreadStackHelper.cpp
@@ -400,17 +400,17 @@ GetPathAfterComponent(const char* filena
     next = strstr(found - 1, component);
   }
   return found;
 }
 
 } // namespace
 
 const char*
-ThreadStackHelper::AppendJSEntry(const volatile js::ProfileEntry* aEntry,
+ThreadStackHelper::AppendJSEntry(const js::ProfileEntry* aEntry,
                                  intptr_t& aAvailableBufferSize,
                                  const char* aPrevLabel)
 {
   // May be called from another thread or inside a signal handler.
   // We assume querying the script is safe but we must not manupulate it.
   // Also we must not allocate any memory from heap.
   MOZ_ASSERT(aEntry->isJs());
 
@@ -487,18 +487,18 @@ ThreadStackHelper::FillStackBuffer()
   MOZ_ASSERT(mStackToFill->empty());
 
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
   size_t reservedSize = mStackToFill->capacity();
   size_t reservedBufferSize = mStackToFill->AvailableBufferSize();
   intptr_t availableBufferSize = intptr_t(reservedBufferSize);
 
   // Go from front to back
-  const volatile js::ProfileEntry* entry = mPseudoStack->entries;
-  const volatile js::ProfileEntry* end = entry + mPseudoStack->stackSize();
+  const js::ProfileEntry* entry = mPseudoStack->entries;
+  const js::ProfileEntry* end = entry + mPseudoStack->stackSize();
   // Deduplicate identical, consecutive frames
   const char* prevLabel = nullptr;
   for (; reservedSize-- && entry != end; entry++) {
     if (entry->isJs()) {
       prevLabel = AppendJSEntry(entry, availableBufferSize, prevLabel);
       continue;
     }
     const char* const label = entry->label();
--- a/xpcom/threads/ThreadStackHelper.h
+++ b/xpcom/threads/ThreadStackHelper.h
@@ -72,17 +72,17 @@ private:
   const PseudoStack* const mPseudoStack;
   size_t mMaxStackSize;
   size_t mMaxBufferSize;
 #endif
 
   bool PrepareStackBuffer(Stack& aStack);
   void FillStackBuffer();
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
-  const char* AppendJSEntry(const volatile js::ProfileEntry* aEntry,
+  const char* AppendJSEntry(const js::ProfileEntry* aEntry,
                             intptr_t& aAvailableBufferSize,
                             const char* aPrevLabel);
 #endif
 
 public:
   /**
    * Initialize ThreadStackHelper. Must be called from main thread.
    */