Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 26 May 2017 12:40:44 +0200
changeset 408917 bdd2b6377f1d2d5fabbc2ed095b5e1c5950d8d4a
parent 408916 c735b95bef3689566552807ef300a08fa563d6f6 (current diff)
parent 408887 44e41de60c48a94a004494dd48f57dd1d8c157d8 (diff)
child 408918 e7efc643bc078cc791c2c165db609359c4587ed0
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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
tools/profiler/public/PseudoStack.h
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -1567,17 +1567,22 @@ TimeoutManager::StartThrottlingTrackingT
   MOZ_DIAGNOSTIC_ASSERT(!mThrottleTrackingTimeouts);
   mThrottleTrackingTimeouts = true;
   mThrottleTrackingTimeoutsTimer = nullptr;
 }
 
 void
 TimeoutManager::OnDocumentLoaded()
 {
-  MaybeStartThrottleTrackingTimout();
+  // The load event may be firing again if we're coming back to the page by
+  // navigating through the session history, so we need to ensure to only call
+  // this when mThrottleTrackingTimeouts hasn't been set yet.
+  if (!mThrottleTrackingTimeouts) {
+    MaybeStartThrottleTrackingTimout();
+  }
 }
 
 void
 TimeoutManager::MaybeStartThrottleTrackingTimout()
 {
   if (gTrackingTimeoutThrottlingDelay <= 0 ||
       mWindow.AsInner()->InnerObjectsFreed() || mWindow.IsSuspended()) {
     return;
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -2,25 +2,29 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef js_ProfilingStack_h
 #define js_ProfilingStack_h
 
+#include <algorithm>
+#include <stdint.h>
+
 #include "jsbytecode.h"
 #include "jstypes.h"
 #include "js/TypeDecls.h"
-
 #include "js/Utility.h"
 
 struct JSRuntime;
 class JSTracer;
 
+class PseudoStack;
+
 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
@@ -49,16 +53,18 @@ class ProfileEntry
     void * volatile spOrScript;
 
     // Line number for non-JS entries, the bytecode offset otherwise.
     int32_t volatile lineOrPcOffset;
 
     // General purpose storage describing this frame.
     uint32_t volatile flags_;
 
+    static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
+
   public:
     // These traits are bit masks. Make sure they're powers of 2.
     enum Flags : uint32_t {
         // Indicate whether a profile entry represents a CPP frame. If not set,
         // a JS frame is assumed by default. You're not allowed to publicly
         // change the frame type. Instead, initialize the ProfileEntry as either
         // a JS or CPP frame with `initJsFrame` or `initCppFrame` respectively.
         IS_CPP_ENTRY = 1 << 0,
@@ -103,28 +109,37 @@ class ProfileEntry
     // available unless they were marked as volatile as well.
 
     bool isCpp() const volatile { return hasFlag(IS_CPP_ENTRY); }
     bool isJs() const volatile { return !isCpp(); }
 
     void setLabel(const char* aLabel) volatile { label_ = aLabel; }
     const char* label() const volatile { return label_; }
 
-    void setDynamicString(const char* aDynamicString) volatile { dynamicString_ = aDynamicString; }
     const char* dynamicString() const volatile { return dynamicString_; }
 
-    void initJsFrame(JSScript* aScript, jsbytecode* aPc) volatile {
-        flags_ = 0;
-        spOrScript = aScript;
-        setPC(aPc);
+    void initCppFrame(const char* aLabel, const char* aDynamicString, void* sp, uint32_t aLine,
+                      js::ProfileEntry::Flags aFlags, js::ProfileEntry::Category aCategory)
+                      volatile
+    {
+        label_ = aLabel;
+        dynamicString_ = aDynamicString;
+        spOrScript = sp;
+        lineOrPcOffset = static_cast<int32_t>(aLine);
+        flags_ = aFlags | js::ProfileEntry::IS_CPP_ENTRY | uint32_t(aCategory);
     }
-    void initCppFrame(void* aSp, uint32_t aLine) volatile {
-        flags_ = IS_CPP_ENTRY;
-        spOrScript = aSp;
-        lineOrPcOffset = static_cast<int32_t>(aLine);
+
+    void initJsFrame(const char* aLabel, const char* aDynamicString, JSScript* aScript,
+                     jsbytecode* aPc) volatile
+    {
+        label_ = aLabel;
+        dynamicString_ = aDynamicString;
+        spOrScript = aScript;
+        lineOrPcOffset = pcToOffset(aScript, aPc);
+        flags_ = uint32_t(js::ProfileEntry::Category::JS);  // No flags, just the JS category.
     }
 
     void setFlag(uint32_t flag) volatile {
         MOZ_ASSERT(flag != IS_CPP_ENTRY);
         flags_ |= flag;
     }
     void unsetFlag(uint32_t flag) volatile {
         MOZ_ASSERT(flag != IS_CPP_ENTRY);
@@ -140,17 +155,17 @@ class ProfileEntry
 
     uint32_t category() const volatile {
         return flags_ & CATEGORY_MASK;
     }
     void setCategory(Category c) volatile {
         MOZ_ASSERT(c >= Category::FIRST);
         MOZ_ASSERT(c <= Category::LAST);
         flags_ &= ~CATEGORY_MASK;
-        setFlag(static_cast<uint32_t>(c));
+        setFlag(uint32_t(c));
     }
 
     void setOSR() volatile {
         MOZ_ASSERT(isJs());
         setFlag(OSR);
     }
     void unsetOSR() volatile {
         MOZ_ASSERT(isJs());
@@ -175,34 +190,98 @@ class ProfileEntry
         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;
     JS_FRIEND_API(void) setPC(jsbytecode* pc) volatile;
 
-    void trace(JSTracer* trc);
+    void trace(JSTracer* trc) volatile;
 
     // 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;
 
     static size_t offsetOfLabel() { return offsetof(ProfileEntry, label_); }
     static size_t offsetOfSpOrScript() { return offsetof(ProfileEntry, spOrScript); }
     static size_t offsetOfLineOrPcOffset() { return offsetof(ProfileEntry, lineOrPcOffset); }
     static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags_); }
 };
 
 JS_FRIEND_API(void)
-SetContextProfilingStack(JSContext* cx, ProfileEntry* stack, mozilla::Atomic<uint32_t>* size,
-                         uint32_t max);
+SetContextProfilingStack(JSContext* cx, PseudoStack* pseudoStack);
 
 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.
+class PseudoStack
+{
+  public:
+    PseudoStack()
+      : stackPointer(0)
+    {}
+
+    ~PseudoStack() {
+        // The label macros keep a reference to the PseudoStack to avoid a TLS
+        // access. If these are somehow not all cleared we will get a
+        // use-after-free so better to crash now.
+        MOZ_RELEASE_ASSERT(stackPointer == 0);
+    }
+
+    void pushCppFrame(const char* label, const char* dynamicString, void* sp, uint32_t line,
+                      js::ProfileEntry::Category category,
+                      js::ProfileEntry::Flags flags = js::ProfileEntry::Flags(0)) {
+        if (stackPointer < MaxEntries) {
+            entries[stackPointer].initCppFrame(label, dynamicString, sp, line, flags, category);
+        }
+
+        // This must happen at the end! The compiler will not reorder this
+        // update because stackPointer is Atomic.
+        stackPointer++;
+    }
+
+    void pushJsFrame(const char* label, const char* dynamicString, JSScript* script,
+                     jsbytecode* pc) {
+        if (stackPointer < MaxEntries) {
+            entries[stackPointer].initJsFrame(label, dynamicString, script, pc);
+        }
+
+        // This must happen at the end! The compiler will not reorder this
+        // update because stackPointer is Atomic.
+        stackPointer++;
+    }
+
+    void pop() {
+        MOZ_ASSERT(stackPointer > 0);
+        stackPointer--;
+    }
+
+    uint32_t stackSize() const { return std::min(uint32_t(stackPointer), uint32_t(MaxEntries)); }
+
+  private:
+    // 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];
+
+    // 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;
+};
+
 #endif  /* js_ProfilingStack_h */
--- a/js/src/jsapi-tests/testProfileStrings.cpp
+++ b/js/src/jsapi-tests/testProfileStrings.cpp
@@ -8,38 +8,36 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Atomics.h"
 
 #include "jscntxt.h"
 
 #include "jsapi-tests/tests.h"
 
-static js::ProfileEntry pstack[10];
-static mozilla::Atomic<uint32_t> psize;
-static uint32_t max_stack = 0;
+static PseudoStack pseudoStack;
+static uint32_t peakStackPointer = 0;
 
 static void
 reset(JSContext* cx)
 {
-    psize = max_stack = 0;
-    memset(pstack, 0, sizeof(pstack));
+    pseudoStack.stackPointer = 0;
     cx->runtime()->geckoProfiler().stringsReset();
     cx->runtime()->geckoProfiler().enableSlowAssertions(true);
     js::EnableContextProfilingStack(cx, true);
 }
 
 static const JSClass ptestClass = {
     "Prof", 0
 };
 
 static bool
 test_fn(JSContext* cx, unsigned argc, JS::Value* vp)
 {
-    max_stack = psize;
+    peakStackPointer = pseudoStack.stackPointer;
     return true;
 }
 
 static bool
 test_fn2(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::RootedValue r(cx);
     JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
@@ -77,17 +75,17 @@ static const JSFunctionSpec ptestFunctio
     JS_FS("enable", enable, 0, 0),
     JS_FS("disable", disable, 0, 0),
     JS_FS_END
 };
 
 static JSObject*
 initialize(JSContext* cx)
 {
-    js::SetContextProfilingStack(cx, pstack, &psize, 10);
+    js::SetContextProfilingStack(cx, &pseudoStack);
     JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
     return JS_InitClass(cx, global, nullptr, &ptestClass, Prof, 0,
                         nullptr, ptestFunctions, nullptr, nullptr);
 }
 
 BEGIN_TEST(testProfileStrings_isCalledWithInterpreter)
 {
     CHECK(initialize(cx));
@@ -103,47 +101,35 @@ BEGIN_TEST(testProfileStrings_isCalledWi
     EXEC("function check2() { var p = new Prof(); p.test_fn2(); }");
 
     reset(cx);
     {
         JS::RootedValue rval(cx);
         /* Make sure the stack resets and we have an entry for each stack */
         CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
                                   &rval));
-        CHECK(psize == 0);
-        CHECK(max_stack >= 8);
+        CHECK(pseudoStack.stackPointer == 0);
+        CHECK(peakStackPointer >= 8);
         CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8);
         /* Make sure the stack resets and we added no new entries */
-        max_stack = 0;
+        peakStackPointer = 0;
         CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
                                   &rval));
-        CHECK(psize == 0);
-        CHECK(max_stack >= 8);
+        CHECK(pseudoStack.stackPointer == 0);
+        CHECK(peakStackPointer >= 8);
         CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8);
     }
     reset(cx);
     {
         JS::RootedValue rval(cx);
         CHECK(JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(),
                                   &rval));
         CHECK(cx->runtime()->geckoProfiler().stringsCount() == 5);
-        CHECK(max_stack >= 6);
-        CHECK(psize == 0);
-    }
-    js::EnableContextProfilingStack(cx, false);
-    js::SetContextProfilingStack(cx, pstack, &psize, 3);
-    reset(cx);
-    {
-        JS::RootedValue rval(cx);
-        pstack[3].setLabel((char*) 1234);
-        CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
-                                  &rval));
-        CHECK((size_t) pstack[3].label() == 1234);
-        CHECK(max_stack >= 8);
-        CHECK(psize == 0);
+        CHECK(peakStackPointer >= 6);
+        CHECK(pseudoStack.stackPointer == 0);
     }
     return true;
 }
 END_TEST(testProfileStrings_isCalledWithInterpreter)
 
 BEGIN_TEST(testProfileStrings_isCalledWithJIT)
 {
     CHECK(initialize(cx));
@@ -161,42 +147,29 @@ BEGIN_TEST(testProfileStrings_isCalledWi
     EXEC("function check2() { var p = new Prof(); p.test_fn2(); }");
 
     reset(cx);
     {
         JS::RootedValue rval(cx);
         /* Make sure the stack resets and we have an entry for each stack */
         CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
                                   &rval));
-        CHECK(psize == 0);
-        CHECK(max_stack >= 8);
+        CHECK(pseudoStack.stackPointer == 0);
+        CHECK(peakStackPointer >= 8);
 
         /* Make sure the stack resets and we added no new entries */
         uint32_t cnt = cx->runtime()->geckoProfiler().stringsCount();
-        max_stack = 0;
+        peakStackPointer = 0;
         CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
                                   &rval));
-        CHECK(psize == 0);
+        CHECK(pseudoStack.stackPointer == 0);
         CHECK(cx->runtime()->geckoProfiler().stringsCount() == cnt);
-        CHECK(max_stack >= 8);
+        CHECK(peakStackPointer >= 8);
     }
 
-    js::EnableContextProfilingStack(cx, false);
-    js::SetContextProfilingStack(cx, pstack, &psize, 3);
-    reset(cx);
-    {
-        /* Limit the size of the stack and make sure we don't overflow */
-        JS::RootedValue rval(cx);
-        pstack[3].setLabel((char*) 1234);
-        CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
-                                  &rval));
-        CHECK(psize == 0);
-        CHECK(max_stack >= 8);
-        CHECK((size_t) pstack[3].label() == 1234);
-    }
     return true;
 }
 END_TEST(testProfileStrings_isCalledWithJIT)
 
 BEGIN_TEST(testProfileStrings_isCalledWhenError)
 {
     CHECK(initialize(cx));
     JS::ContextOptionsRef(cx).setBaseline(true)
@@ -206,21 +179,22 @@ BEGIN_TEST(testProfileStrings_isCalledWh
 
     reset(cx);
     {
         JS::RootedValue rval(cx);
         /* Make sure the stack resets and we have an entry for each stack */
         bool ok = JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(),
                                       &rval);
         CHECK(!ok);
-        CHECK(psize == 0);
+        CHECK(pseudoStack.stackPointer == 0);
         CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1);
 
         JS_ClearPendingException(cx);
     }
+
     return true;
 }
 END_TEST(testProfileStrings_isCalledWhenError)
 
 BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
 {
     CHECK(initialize(cx));
     JS::ContextOptionsRef(cx).setBaseline(true)
@@ -229,48 +203,48 @@ BEGIN_TEST(testProfileStrings_worksWhenE
     EXEC("function b(p) { p.test_fn(); }");
     EXEC("function a() { var p = new Prof(); p.enable(); b(p); }");
     reset(cx);
     js::EnableContextProfilingStack(cx, false);
     {
         /* enable it in the middle of JS and make sure things check out */
         JS::RootedValue rval(cx);
         JS_CallFunctionName(cx, global, "a", JS::HandleValueArray::empty(), &rval);
-        CHECK(psize == 0);
-        CHECK(max_stack >= 1);
+        CHECK(pseudoStack.stackPointer == 0);
+        CHECK(peakStackPointer >= 1);
         CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1);
     }
 
     EXEC("function d(p) { p.disable(); }");
     EXEC("function c() { var p = new Prof(); d(p); }");
     reset(cx);
     {
         /* now disable in the middle of js */
         JS::RootedValue rval(cx);
         JS_CallFunctionName(cx, global, "c", JS::HandleValueArray::empty(), &rval);
-        CHECK(psize == 0);
+        CHECK(pseudoStack.stackPointer == 0);
     }
 
     EXEC("function e() { var p = new Prof(); d(p); p.enable(); b(p); }");
     reset(cx);
     {
         /* now disable in the middle of js, but re-enable before final exit */
         JS::RootedValue rval(cx);
         JS_CallFunctionName(cx, global, "e", JS::HandleValueArray::empty(), &rval);
-        CHECK(psize == 0);
-        CHECK(max_stack >= 3);
+        CHECK(pseudoStack.stackPointer == 0);
+        CHECK(peakStackPointer >= 3);
     }
 
     EXEC("function h() { }");
     EXEC("function g(p) { p.disable(); for (var i = 0; i < 100; i++) i++; }");
     EXEC("function f() { g(new Prof()); }");
     reset(cx);
     cx->runtime()->geckoProfiler().enableSlowAssertions(false);
     {
         JS::RootedValue rval(cx);
         /* disable, and make sure that if we try to re-enter the JIT the pop
          * will still happen */
         JS_CallFunctionName(cx, global, "f", JS::HandleValueArray::empty(), &rval);
-        CHECK(psize == 0);
+        CHECK(pseudoStack.stackPointer == 0);
     }
     return true;
 }
 END_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -382,18 +382,17 @@ ShellContext::ShellContext(JSContext* cx
     lastWarningEnabled(false),
     lastWarning(cx, NullValue()),
     promiseRejectionTrackerCallback(cx, NullValue()),
     watchdogLock(mutexid::ShellContextWatchdog),
     exitCode(0),
     quitting(false),
     readLineBufPos(0),
     errFilePtr(nullptr),
-    outFilePtr(nullptr),
-    geckoProfilingStackSize(0)
+    outFilePtr(nullptr)
 {}
 
 ShellContext*
 js::shell::GetShellContext(JSContext* cx)
 {
     ShellContext* sc = static_cast<ShellContext*>(JS_GetContextPrivate(cx));
     MOZ_ASSERT(sc);
     return sc;
@@ -5212,18 +5211,17 @@ EnableGeckoProfiling(JSContext* cx, unsi
     CallArgs args = CallArgsFromVp(argc, vp);
 
     ShellContext* sc = GetShellContext(cx);
 
     // Disable before re-enabling; see the assertion in |GeckoProfiler::setProfilingStack|.
     if (cx->runtime()->geckoProfiler().installed())
         MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
 
-    SetContextProfilingStack(cx, sc->geckoProfilingStack, &sc->geckoProfilingStackSize,
-                             ShellContext::GeckoProfilingMaxStackSize);
+    SetContextProfilingStack(cx, &sc->geckoProfilingStack);
     cx->runtime()->geckoProfiler().enableSlowAssertions(false);
     if (!cx->runtime()->geckoProfiler().enable(true))
         JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler");
 
     args.rval().setUndefined();
     return true;
 }
 
@@ -5245,18 +5243,17 @@ EnableGeckoProfilingWithSlowAssertions(J
         // with slow assertions on.
         MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
     }
 
     // Disable before re-enabling; see the assertion in |GeckoProfiler::setProfilingStack|.
     if (cx->runtime()->geckoProfiler().installed())
         MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
 
-    SetContextProfilingStack(cx, sc->geckoProfilingStack, &sc->geckoProfilingStackSize,
-                             ShellContext::GeckoProfilingMaxStackSize);
+    SetContextProfilingStack(cx, &sc->geckoProfilingStack);
     cx->runtime()->geckoProfiler().enableSlowAssertions(true);
     if (!cx->runtime()->geckoProfiler().enable(true))
         JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler");
 
     return true;
 }
 
 static bool
--- a/js/src/shell/jsshell.h
+++ b/js/src/shell/jsshell.h
@@ -198,19 +198,17 @@ struct ShellContext
     bool quitting;
 
     JS::UniqueChars readLineBuf;
     size_t readLineBufPos;
 
     js::shell::RCFile** errFilePtr;
     js::shell::RCFile** outFilePtr;
 
-    static const uint32_t GeckoProfilingMaxStackSize = 1000;
-    js::ProfileEntry geckoProfilingStack[GeckoProfilingMaxStackSize];
-    mozilla::Atomic<uint32_t> geckoProfilingStackSize;
+    PseudoStack geckoProfilingStack;
 
     OffThreadState offThreadState;
 
     JS::UniqueChars moduleLoadPath;
     UniquePtr<MarkBitObservers> markObservers;
 };
 
 extern ShellContext*
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -23,19 +23,17 @@
 
 using namespace js;
 
 using mozilla::DebugOnly;
 
 GeckoProfiler::GeckoProfiler(JSRuntime* rt)
   : rt(rt),
     strings(mutexid::GeckoProfilerStrings),
-    stack_(nullptr),
-    size_(nullptr),
-    max_(0),
+    pseudoStack_(nullptr),
     slowAssertions(false),
     enabled_(false),
     eventMarker_(nullptr)
 {
     MOZ_ASSERT(rt != nullptr);
 }
 
 bool
@@ -44,24 +42,22 @@ GeckoProfiler::init()
     auto locked = strings.lock();
     if (!locked->init())
         return false;
 
     return true;
 }
 
 void
-GeckoProfiler::setProfilingStack(ProfileEntry* stack, mozilla::Atomic<uint32_t>* size, uint32_t max)
+GeckoProfiler::setProfilingStack(PseudoStack* pseudoStack)
 {
-    MOZ_ASSERT_IF(size_ && *size_ != 0, !enabled());
+    MOZ_ASSERT_IF(pseudoStack_, !enabled());
     MOZ_ASSERT(strings.lock()->initialized());
 
-    stack_ = stack;
-    size_  = size;
-    max_   = max;
+    pseudoStack_ = pseudoStack;
 }
 
 void
 GeckoProfiler::setEventMarker(void (*fn)(const char*))
 {
     eventMarker_ = fn;
 }
 
@@ -205,116 +201,65 @@ GeckoProfiler::enter(JSContext* cx, JSSc
         ReportOutOfMemory(cx);
         return false;
     }
 
 #ifdef DEBUG
     // In debug builds, assert the JS pseudo frames already on the stack
     // have a non-null pc. Only look at the top frames to avoid quadratic
     // behavior.
-    if (*size_ > 0 && *size_ - 1 < max_) {
-        size_t start = (*size_ > 4) ? *size_ - 4 : 0;
-        for (size_t i = start; i < *size_ - 1; i++)
-            MOZ_ASSERT_IF(stack_[i].isJs(), stack_[i].pc() != nullptr);
+    uint32_t sp = pseudoStack_->stackPointer;
+    if (sp > 0 && sp - 1 < PseudoStack::MaxEntries) {
+        size_t start = (sp > 4) ? sp - 4 : 0;
+        for (size_t i = start; i < sp - 1; i++)
+            MOZ_ASSERT_IF(pseudoStack_->entries[i].isJs(), pseudoStack_->entries[i].pc());
     }
 #endif
 
-    push("", dynamicString, /* sp = */ nullptr, script, script->code());
+    pseudoStack_->pushJsFrame("", dynamicString, script, script->code());
     return true;
 }
 
 void
 GeckoProfiler::exit(JSScript* script, JSFunction* maybeFun)
 {
-    pop();
+    pseudoStack_->pop();
 
 #ifdef DEBUG
     /* Sanity check to make sure push/pop balanced */
-    if (*size_ < max_) {
+    uint32_t sp = pseudoStack_->stackPointer;
+    if (sp < PseudoStack::MaxEntries) {
         const char* dynamicString = profileString(script, maybeFun);
         /* Can't fail lookup because we should already be in the set */
-        MOZ_ASSERT(dynamicString != nullptr);
+        MOZ_ASSERT(dynamicString);
 
         // Bug 822041
-        if (!stack_[*size_].isJs()) {
+        if (!pseudoStack_->entries[sp].isJs()) {
             fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
-            fprintf(stderr, " stack=%p size=%d/%d\n", (void*) stack_, size(), max_);
-            for (int32_t i = *size_; i >= 0; i--) {
-                if (stack_[i].isJs())
-                    fprintf(stderr, "  [%d] JS %s\n", i, stack_[i].dynamicString());
+            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];
+                if (entry.isJs())
+                    fprintf(stderr, "  [%d] JS %s\n", i, entry.dynamicString());
                 else
-                    fprintf(stderr, "  [%d] C line %d %s\n", i, stack_[i].line(), stack_[i].dynamicString());
+                    fprintf(stderr, "  [%d] C line %d %s\n", i, entry.line(), entry.dynamicString());
             }
         }
 
-        MOZ_ASSERT(stack_[*size_].isJs());
-        MOZ_ASSERT(stack_[*size_].script() == script);
-        MOZ_ASSERT(strcmp((const char*) stack_[*size_].dynamicString(), dynamicString) == 0);
-        stack_[*size_].setDynamicString(nullptr);
-        stack_[*size_].setPC(nullptr);
+        volatile ProfileEntry& entry = pseudoStack_->entries[sp];
+        MOZ_ASSERT(entry.isJs());
+        MOZ_ASSERT(entry.script() == script);
+        MOZ_ASSERT(strcmp((const char*) entry.dynamicString(), dynamicString) == 0);
     }
 #endif
 }
 
-void
-GeckoProfiler::beginPseudoJS(const char* label, void* sp)
-{
-    /* these operations cannot be re-ordered, so volatile-ize operations */
-    volatile ProfileEntry* stack = stack_;
-    uint32_t current = *size_;
-
-    MOZ_ASSERT(installed());
-    if (current < max_) {
-        stack[current].setLabel(label);
-        stack[current].initCppFrame(sp, 0);
-        stack[current].setFlag(ProfileEntry::BEGIN_PSEUDO_JS);
-    }
-    *size_ = current + 1;
-}
-
-void
-GeckoProfiler::push(const char* label, const char* dynamicString, void* sp, JSScript* script,
-                    jsbytecode* pc, ProfileEntry::Category category)
-{
-    MOZ_ASSERT(label[0] == '\0' || !dynamicString);
-    MOZ_ASSERT_IF(sp != nullptr, script == nullptr && pc == nullptr);
-    MOZ_ASSERT_IF(sp == nullptr, script != nullptr && pc != nullptr);
-
-    /* these operations cannot be re-ordered, so volatile-ize operations */
-    volatile ProfileEntry* stack = stack_;
-    uint32_t current = *size_;
-
-    MOZ_ASSERT(installed());
-    if (current < max_) {
-        volatile ProfileEntry& entry = stack[current];
-
-        if (sp != nullptr) {
-            entry.initCppFrame(sp, 0);
-            MOZ_ASSERT(entry.flags() == js::ProfileEntry::IS_CPP_ENTRY);
-        }
-        else {
-            entry.initJsFrame(script, pc);
-            MOZ_ASSERT(entry.flags() == 0);
-        }
-
-        entry.setLabel(label);
-        entry.setDynamicString(dynamicString);
-        entry.setCategory(category);
-    }
-    *size_ = current + 1;
-}
-
-void
-GeckoProfiler::pop()
-{
-    MOZ_ASSERT(installed());
-    MOZ_ASSERT(*size_ > 0);
-    (*size_)--;
-}
-
 /*
  * Serializes the script/function pair into a "descriptive string" which is
  * allowed to fail. This function cannot trigger a GC because it could finalize
  * some scripts, resize the hash table of profile strings, and invalidate the
  * AddPtr held while invoking allocProfileString.
  */
 UniqueChars
 GeckoProfiler::allocProfileString(JSScript* script, JSFunction* maybeFun)
@@ -360,22 +305,22 @@ GeckoProfiler::allocProfileString(JSScri
     }
 
     MOZ_ASSERT(ret == len, "Computed length should match actual length!");
 
     return cstr;
 }
 
 void
-GeckoProfiler::trace(JSTracer* trc)
+GeckoProfiler::trace(JSTracer* trc) volatile
 {
-    if (stack_) {
-        size_t limit = Min(uint32_t(*size_), max_);
-        for (size_t i = 0; i < limit; i++)
-            stack_[i].trace(trc);
+    if (pseudoStack_) {
+        size_t size = pseudoStack_->stackSize();
+        for (size_t i = 0; i < size; i++)
+            pseudoStack_->entries[i].trace(trc);
     }
 }
 
 void
 GeckoProfiler::fixupStringsMapAfterMovingGC()
 {
     auto locked = strings.lock();
     if (!locked->initialized())
@@ -403,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)
+ProfileEntry::trace(JSTracer* trc) volatile
 {
     if (isJs()) {
         JSScript* s = rawScript();
         TraceNullableRoot(trc, &s, "ProfileEntry script");
         spOrScript = s;
     }
 }
 
@@ -422,90 +367,103 @@ GeckoProfilerEntryMarker::GeckoProfilerE
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
     : profiler(&rt->geckoProfiler())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (!profiler->installed()) {
         profiler = nullptr;
         return;
     }
-    size_before = *profiler->size_;
+    spBefore_ = profiler->stackPointer();
+
     // We want to push a CPP frame so the profiler can correctly order JS and native stacks.
-    profiler->beginPseudoJS("js::RunScript", this);
-    profiler->push("js::RunScript", /* dynamicString = */ nullptr, /* sp = */ nullptr, script,
-                   script->code());
+    profiler->pseudoStack_->pushCppFrame(
+        "js::RunScript", /* dynamicString = */ nullptr, /* sp = */ this, /* line = */ 0,
+        js::ProfileEntry::Category::OTHER, js::ProfileEntry::BEGIN_PSEUDO_JS);
+
+    profiler->pseudoStack_->pushJsFrame(
+        "js::RunScript", /* dynamicString = */ nullptr, script, script->code());
 }
 
 GeckoProfilerEntryMarker::~GeckoProfilerEntryMarker()
 {
     if (profiler == nullptr)
         return;
 
-    profiler->pop();
-    profiler->endPseudoJS();
-    MOZ_ASSERT(size_before == *profiler->size_);
+    profiler->pseudoStack_->pop();    // the JS frame
+    profiler->pseudoStack_->pop();    // the BEGIN_PSEUDO_JS frame
+    MOZ_ASSERT(spBefore_ == profiler->stackPointer());
 }
 
 AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSRuntime* rt, const char* label,
                                                ProfileEntry::Category category
                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
     : profiler_(&rt->geckoProfiler())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (!profiler_->installed()) {
         profiler_ = nullptr;
         return;
     }
-    sizeBefore_ = *profiler_->size_;
-    profiler_->beginPseudoJS(label, this);
-    profiler_->push(label, /* dynamicString = */ nullptr, /* sp = */ this, /* script = */ nullptr,
-                    /* pc = */ nullptr, category);
+    spBefore_ = profiler_->stackPointer();
+
+    profiler_->pseudoStack_->pushCppFrame(
+        label, /* dynamicString = */ nullptr, /* sp = */ this, /* line = */ 0,
+        js::ProfileEntry::Category::OTHER, js::ProfileEntry::BEGIN_PSEUDO_JS);
+
+    profiler_->pseudoStack_->pushCppFrame(
+        label, /* dynamicString = */ nullptr, /* sp = */ this, /* line = */ 0, category);
 }
 
 AutoGeckoProfilerEntry::~AutoGeckoProfilerEntry()
 {
     if (!profiler_)
         return;
 
-    profiler_->pop();
-    profiler_->endPseudoJS();
-    MOZ_ASSERT(sizeBefore_ == *profiler_->size_);
+    profiler_->pseudoStack_->pop();   // the C++ frame
+    profiler_->pseudoStack_->pop();   // the BEGIN_PSEUDO_JS frame
+    MOZ_ASSERT(spBefore_ == profiler_->stackPointer());
 }
 
 GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame
                                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
     : profiler(&rt->geckoProfiler())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    if (!hasProfilerFrame || !profiler->enabled() ||
-        profiler->size() >= profiler->maxSize())
-    {
+    if (!hasProfilerFrame || !profiler->enabled()) {
         profiler = nullptr;
         return;
     }
 
-    size_before = profiler->size();
-    if (profiler->size() == 0)
+    uint32_t sp = profiler->pseudoStack_->stackPointer;
+    if (sp >= PseudoStack::MaxEntries) {
+        profiler = nullptr;
+        return;
+    }
+
+    spBefore_ = sp;
+    if (sp == 0)
         return;
 
-    ProfileEntry& entry = profiler->stack()[profiler->size() - 1];
+    volatile ProfileEntry& entry = profiler->pseudoStack_->entries[sp - 1];
     MOZ_ASSERT(entry.isJs());
     entry.setOSR();
 }
 
 GeckoProfilerBaselineOSRMarker::~GeckoProfilerBaselineOSRMarker()
 {
     if (profiler == nullptr)
         return;
 
-    MOZ_ASSERT(size_before == *profiler->size_);
-    if (profiler->size() == 0)
+    uint32_t sp = profiler->stackPointer();
+    MOZ_ASSERT(spBefore_ == sp);
+    if (sp == 0)
         return;
 
-    ProfileEntry& entry = profiler->stack()[profiler->size() - 1];
+    volatile ProfileEntry& entry = profiler->stack()[sp - 1];
     MOZ_ASSERT(entry.isJs());
     entry.unsetOSR();
 }
 
 JS_PUBLIC_API(JSScript*)
 ProfileEntry::script() const volatile
 {
     MOZ_ASSERT(isJs());
@@ -534,29 +492,34 @@ ProfileEntry::pc() const volatile
     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;
+}
+
 JS_FRIEND_API(void)
 ProfileEntry::setPC(jsbytecode* pc) volatile
 {
     MOZ_ASSERT(isJs());
     JSScript* script = this->script();
     MOZ_ASSERT(script); // This should not be called while profiling is suppressed.
-    lineOrPcOffset = pc == nullptr ? NullPCOffset : script->pcToOffset(pc);
+    lineOrPcOffset = pcToOffset(script, pc);
 }
 
 JS_FRIEND_API(void)
-js::SetContextProfilingStack(JSContext* cx, ProfileEntry* stack, mozilla::Atomic<uint32_t>* size, uint32_t max)
+js::SetContextProfilingStack(JSContext* cx, PseudoStack* pseudoStack)
 {
-    cx->runtime()->geckoProfiler().setProfilingStack(stack, size, max);
+    cx->runtime()->geckoProfiler().setProfilingStack(pseudoStack);
 }
 
 JS_FRIEND_API(void)
 js::EnableContextProfilingStack(JSContext* cx, bool enabled)
 {
     if (!cx->runtime()->geckoProfiler().enable(enabled))
         MOZ_CRASH("Execution in this runtime should already be single threaded");
 }
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -124,91 +124,77 @@ class GeckoProfilerBaselineOSRMarker;
 class GeckoProfiler
 {
     friend class AutoGeckoProfilerEntry;
     friend class GeckoProfilerEntryMarker;
     friend class GeckoProfilerBaselineOSRMarker;
 
     JSRuntime*           rt;
     ExclusiveData<ProfileStringMap> strings;
-    ProfileEntry*        stack_;
-    mozilla::Atomic<uint32_t>* size_;
-    uint32_t             max_;
+    PseudoStack*         pseudoStack_;
     bool                 slowAssertions;
     uint32_t             enabled_;
     void                (*eventMarker_)(const char*);
 
     UniqueChars allocProfileString(JSScript* script, JSFunction* function);
-    void push(const char* label, const char* dynamicString, void* sp, JSScript* script,
-              jsbytecode* pc, ProfileEntry::Category category = ProfileEntry::Category::JS);
-    void pop();
 
   public:
     explicit GeckoProfiler(JSRuntime* rt);
 
     bool init();
 
-    uint32_t* addressOfMaxSize() {
-        return &max_;
-    }
-
-    ProfileEntry** addressOfStack() {
-        return &stack_;
-    }
-
-    uint32_t maxSize() { return max_; }
-    uint32_t size() { MOZ_ASSERT(installed()); return *size_; }
-    ProfileEntry* stack() { return stack_; }
+    uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; }
+    volatile 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 stack_ != nullptr && size_ != nullptr; }
+    bool installed() { return pseudoStack_ != nullptr; }
     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
      *               is currently executing
      *   - exit: this function has ceased execution, and no further
      *           entries/exits will be made
      */
     bool enter(JSContext* cx, JSScript* script, JSFunction* maybeFun);
     void exit(JSScript* script, JSFunction* maybeFun);
     void updatePC(JSScript* script, jsbytecode* pc) {
-        if (enabled() && *size_ - 1 < max_) {
-            MOZ_ASSERT(*size_ > 0);
-            MOZ_ASSERT(stack_[*size_ - 1].rawScript() == script);
-            stack_[*size_ - 1].setPC(pc);
+        if (!enabled())
+            return;
+
+        uint32_t sp = pseudoStack_->stackPointer;
+        if (sp - 1 < PseudoStack::MaxEntries) {
+            MOZ_ASSERT(sp > 0);
+            MOZ_ASSERT(pseudoStack_->entries[sp - 1].rawScript() == script);
+            pseudoStack_->entries[sp - 1].setPC(pc);
         }
     }
 
-    /* Enter wasm code */
-    void beginPseudoJS(const char* label, void* sp);    // label must be a static string!
-    void endPseudoJS() { pop(); }
-
-    void setProfilingStack(ProfileEntry* stack, mozilla::Atomic<uint32_t>* size, uint32_t max);
+    void setProfilingStack(PseudoStack* pseudoStack);
     void setEventMarker(void (*fn)(const char*));
     const char* profileString(JSScript* script, JSFunction* maybeFun);
     void onScriptFinalized(JSScript* script);
 
     void markEvent(const char* event);
 
     /* 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);
+    void trace(JSTracer* trc) volatile;
     void fixupStringsMapAfterMovingGC();
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkStringsMapAfterMovingGC();
 #endif
 };
 
 inline size_t
 GeckoProfiler::stringsCount()
@@ -232,17 +218,17 @@ class MOZ_RAII GeckoProfilerEntryMarker
   public:
     explicit GeckoProfilerEntryMarker(JSRuntime* rt,
                                       JSScript* script
                                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~GeckoProfilerEntryMarker();
 
   private:
     GeckoProfiler* profiler;
-    mozilla::DebugOnly<uint32_t> size_before;
+    mozilla::DebugOnly<uint32_t> spBefore_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * RAII class to automatically add Gecko Profiler pseudo frame entries.
  *
  * NB: The `label` string must be statically allocated.
  */
@@ -251,17 +237,17 @@ class MOZ_NONHEAP_CLASS AutoGeckoProfile
   public:
     explicit AutoGeckoProfilerEntry(JSRuntime* rt, const char* label,
                                     ProfileEntry::Category category = ProfileEntry::Category::JS
                                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~AutoGeckoProfilerEntry();
 
   private:
     GeckoProfiler* profiler_;
-    mozilla::DebugOnly<uint32_t> sizeBefore_;
+    mozilla::DebugOnly<uint32_t> spBefore_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * This class is used in the interpreter to bound regions where the baseline JIT
  * being entered via OSR.  It marks the current top pseudostack entry as
  * OSR-ed
  */
@@ -269,17 +255,17 @@ class MOZ_RAII GeckoProfilerBaselineOSRM
 {
   public:
     explicit GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame
                                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~GeckoProfilerBaselineOSRMarker();
 
   private:
     GeckoProfiler* profiler;
-    mozilla::DebugOnly<uint32_t> size_before;
+    mozilla::DebugOnly<uint32_t> spBefore_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * This class manages the instrumentation portion of the profiling for JIT
  * code.
  *
  * The instrumentation tracks entry into functions, leaving those functions via
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1678,17 +1678,17 @@ pref("network.http.enforce-framing.soft"
 // the network will exist in header array as empty string. Call SetHeader with
 // an empty value will still delete the header.(Bug 6699259)
 pref("network.http.keep_empty_response_headers_as_empty_string", true);
 
 // Max size, in bytes, for received HTTP response header.
 pref("network.http.max_response_header_size", 393216);
 
 // If we should attempt to race the cache and network
-pref("network.http.rcwn.enabled", true);
+pref("network.http.rcwn.enabled", false);
 pref("network.http.rcwn.cache_queue_normal_threshold", 8);
 pref("network.http.rcwn.cache_queue_priority_threshold", 2);
 // We might attempt to race the cache with the network only if a resource
 // is smaller than this size.
 pref("network.http.rcwn.small_resource_size_kb", 256);
 
 // The ratio of the transaction count for the focused window and the count of
 // all available active connections.
--- a/tools/profiler/core/ThreadInfo.h
+++ b/tools/profiler/core/ThreadInfo.h
@@ -7,17 +7,17 @@
 #ifndef ThreadInfo_h
 #define ThreadInfo_h
 
 #include "mozilla/NotNull.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 #include "platform.h"
 #include "ProfileBuffer.h"
-#include "PseudoStack.h"
+#include "js/ProfilingStack.h"
 
 // Stub eventMarker function for js-engine event generation.
 void ProfilerJSEventMarker(const char* aEvent);
 
 // This class contains the info for a single thread that is accessible without
 // protection from gPSMutex in platform.cpp. Because there is no external
 // protection against data races, it must provide internal protection. Hence
 // the "Racy" prefix.
@@ -225,21 +225,20 @@ public:
   void SetJSContext(JSContext* aContext)
   {
     // This function runs on-thread.
 
     MOZ_ASSERT(aContext && !mContext);
 
     mContext = aContext;
 
-    js::SetContextProfilingStack(
-      aContext,
-      (js::ProfileEntry*) RacyInfo()->mStack,
-      RacyInfo()->AddressOfStackPointer(),
-      (uint32_t) mozilla::ArrayLength(RacyInfo()->mStack));
+    // We give the JS engine a non-owning reference to the RacyInfo (just the
+    // PseudoStack, really). It's important that the JS engine doesn't touch
+    // this once the thread dies.
+    js::SetContextProfilingStack(aContext, RacyInfo());
 
     PollJSSampling();
   }
 
   // Request that this thread start JS sampling. JS sampling won't actually
   // start until a subsequent PollJSSampling() call occurs *and* mContext has
   // been set.
   void StartJSSampling()
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -19,17 +19,16 @@
 #include "GeckoProfiler.h"
 #include "GeckoProfilerReporter.h"
 #include "ProfilerIOInterposeObserver.h"
 #include "mozilla/StackWalk.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/StaticPtr.h"
-#include "PseudoStack.h"
 #include "ThreadInfo.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsIObserverService.h"
 #include "nsIXULAppInfo.h"
 #include "nsIXULRuntime.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsMemoryReporterManager.h"
@@ -714,17 +713,17 @@ AddPseudoEntry(PSLockRef aLock, ProfileB
     // That will happen to the preceding tag.
     AddDynamicCodeLocationTag(aBuffer, locationString);
     if (entry.isJs()) {
       JSScript* script = entry.script();
       if (script) {
         if (!entry.pc()) {
           // The JIT only allows the top-most entry to have a nullptr pc.
           MOZ_ASSERT(&entry ==
-                     &aRacyInfo->mStack[aRacyInfo->stackSize() - 1]);
+                     &aRacyInfo->entries[aRacyInfo->stackSize() - 1]);
         } else {
           lineno = JS_PCToLineNumber(script, entry.pc());
         }
       }
     } else {
       lineno = entry.line();
     }
   } else {
@@ -774,17 +773,17 @@ struct AutoWalkJSStack
   }
 };
 
 static void
 MergeStacksIntoProfile(PSLockRef aLock, ProfileBuffer* aBuffer,
                        const TickSample& aSample, NativeStack& aNativeStack)
 {
   NotNull<RacyThreadInfo*> racyInfo = aSample.mRacyInfo;
-  volatile js::ProfileEntry* pseudoFrames = racyInfo->mStack;
+  volatile 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
@@ -849,29 +848,29 @@ 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& pseudoFrame = pseudoFrames[pseudoIndex];
-
-      if (pseudoFrame.isCpp()) {
-        lastPseudoCppStackAddr = (uint8_t*) pseudoFrame.stackAddress();
+      volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
+
+      if (pseudoEntry.isCpp()) {
+        lastPseudoCppStackAddr = (uint8_t*) pseudoEntry.stackAddress();
       }
 
       // Skip any pseudo-stack JS frames which are marked isOSR. Pseudostack
       // frames are marked isOSR 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 showing up twice), the
       // interpreter marks the interpreter pseudostack entry with the OSR flag
       // to ensure that it doesn't get counted.
-      if (pseudoFrame.isJs() && pseudoFrame.isOSR()) {
+      if (pseudoEntry.isJs() && pseudoEntry.isOSR()) {
           pseudoIndex++;
           continue;
       }
 
       MOZ_ASSERT(lastPseudoCppStackAddr);
       pseudoStackAddr = lastPseudoCppStackAddr;
     }
 
@@ -900,18 +899,18 @@ 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& pseudoFrame = pseudoFrames[pseudoIndex];
-      AddPseudoEntry(aLock, aBuffer, pseudoFrame, racyInfo);
+      volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
+      AddPseudoEntry(aLock, aBuffer, pseudoEntry, racyInfo);
       pseudoIndex++;
       continue;
     }
 
     // Check to see if JS jit stack frame is top-most
     if (jsStackAddr > nativeStackAddr) {
       MOZ_ASSERT(jsIndex >= 0);
       const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
@@ -1043,17 +1042,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->mStack[i - 1];
+    volatile 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,
@@ -2810,23 +2809,23 @@ profiler_get_backtrace_noalloc(char *out
 
   PseudoStack* pseudoStack = TLSInfo::Stack();
   if (!pseudoStack) {
     return;
   }
 
   bool includeDynamicString = !ActivePS::FeaturePrivacy(lock);
 
-  volatile js::ProfileEntry* pseudoFrames = pseudoStack->mStack;
+  volatile js::ProfileEntry* pseudoEntries = pseudoStack->entries;
   uint32_t pseudoCount = pseudoStack->stackSize();
 
   for (uint32_t i = 0; i < pseudoCount; i++) {
-    const char* label = pseudoFrames[i].label();
+    const char* label = pseudoEntries[i].label();
     const char* dynamicString =
-      includeDynamicString ? pseudoFrames[i].dynamicString() : nullptr;
+      includeDynamicString ? pseudoEntries[i].dynamicString() : nullptr;
     size_t labelLength = strlen(label);
     if (dynamicString) {
       // Put the label, maybe a space, and the dynamic string into output.
       size_t spaceLength = label[0] == '\0' ? 0 : 1;
       size_t dynamicStringLength = strlen(dynamicString);
       if (output + labelLength + spaceLength + dynamicStringLength >= bound) {
         break;
       }
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -7,17 +7,16 @@
 if CONFIG['MOZ_GECKO_PROFILER']:
     XPIDL_MODULE = 'profiler'
     XPIDL_SOURCES += [
         'gecko/nsIProfiler.idl',
     ]
     EXPORTS += [
         'public/CrossProcessProfilerController.h',
         'public/ProfilerMarkerPayload.h',
-        'public/PseudoStack.h',
         'public/shared-libraries.h',
     ]
     UNIFIED_SOURCES += [
         'core/platform.cpp',
         'core/ProfileBuffer.cpp',
         'core/ProfileBufferEntry.cpp',
         'core/ProfileJSONWriter.cpp',
         'core/ProfilerBacktrace.cpp',
--- a/tools/profiler/public/GeckoProfiler.h
+++ b/tools/profiler/public/GeckoProfiler.h
@@ -386,17 +386,16 @@ PROFILER_FUNC(void* profiler_get_stack_t
 
 #if defined(MOZ_GECKO_PROFILER)
 
 #include <stdlib.h>
 #include <signal.h>
 #include "js/ProfilingStack.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/ThreadLocal.h"
-#include "PseudoStack.h"
 #include "nscore.h"
 
 // Make sure that we can use std::min here without the Windows headers messing with us.
 #ifdef min
 # undef min
 #endif
 
 class nsISupports;
@@ -411,27 +410,27 @@ extern MOZ_THREAD_LOCAL(PseudoStack*) sP
 //
 // A short-lived, non-owning PseudoStack reference is created between each
 // profiler_call_enter() / profiler_call_exit() call pair. RAII objects (e.g.
 // SamplerStackFrameRAII) 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* aInfo, js::ProfileEntry::Category aCategory,
+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->push(aInfo, aCategory, aFrameAddress, aLine, aDynamicString);
+  pseudoStack->pushCppFrame(aLabel, aDynamicString, aFrameAddress, aLine, 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)
deleted file mode 100644
--- a/tools/profiler/public/PseudoStack.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef PseudoStack_h
-#define PseudoStack_h
-
-#include "mozilla/ArrayUtils.h"
-#include "js/ProfilingStack.h"
-#include "nsISupportsImpl.h"  // for MOZ_COUNT_{CTOR,DTOR}
-
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <algorithm>
-
-// The PseudoStack members are read by signal handlers, so the mutation of them
-// needs to be signal-safe.
-class PseudoStack
-{
-public:
-  PseudoStack()
-    : mStackPointer(0)
-  {
-    MOZ_COUNT_CTOR(PseudoStack);
-  }
-
-  ~PseudoStack()
-  {
-    MOZ_COUNT_DTOR(PseudoStack);
-
-    // The label macros keep a reference to the PseudoStack to avoid a TLS
-    // access. If these are somehow not all cleared we will get a
-    // use-after-free so better to crash now.
-    MOZ_RELEASE_ASSERT(mStackPointer == 0);
-  }
-
-  void push(const char* aName, js::ProfileEntry::Category aCategory,
-            void* aStackAddress, uint32_t line, const char* aDynamicString)
-  {
-    if (size_t(mStackPointer) >= mozilla::ArrayLength(mStack)) {
-      mStackPointer++;
-      return;
-    }
-
-    volatile js::ProfileEntry& entry = mStack[int(mStackPointer)];
-
-    // Make sure we increment the pointer after the name has been written such
-    // that mStack is always consistent.
-    entry.initCppFrame(aStackAddress, line);
-    entry.setLabel(aName);
-    entry.setDynamicString(aDynamicString);
-    MOZ_ASSERT(entry.flags() == js::ProfileEntry::IS_CPP_ENTRY);
-    entry.setCategory(aCategory);
-
-    // This must happen at the end! The compiler will not reorder this update
-    // because mStackPointer is Atomic.
-    mStackPointer++;
-  }
-
-  // Pop the stack.
-  void pop() { mStackPointer--; }
-
-  uint32_t stackSize() const
-  {
-    return std::min(uint32_t(mStackPointer), uint32_t(mozilla::ArrayLength(mStack)));
-  }
-
-  mozilla::Atomic<uint32_t>* AddressOfStackPointer() { return &mStackPointer; }
-
-private:
-  // No copying.
-  PseudoStack(const PseudoStack&) = delete;
-  void operator=(const PseudoStack&) = delete;
-
-public:
-  // The list of active checkpoints.
-  js::ProfileEntry volatile mStack[1024];
-
-protected:
-  // This may exceed the length of mStack, so instead use the stackSize() method
-  // to determine the number of valid samples in mStack.
-  mozilla::Atomic<uint32_t> mStackPointer;
-};
-
-#endif  // PseudoStack_h
--- a/xpcom/threads/ThreadStackHelper.cpp
+++ b/xpcom/threads/ThreadStackHelper.cpp
@@ -8,17 +8,17 @@
 #include "MainThreadUtils.h"
 #include "nsJSPrincipals.h"
 #include "nsScriptSecurityManager.h"
 #include "jsfriendapi.h"
 #ifdef MOZ_THREADSTACKHELPER_NATIVE
 #include "shared-libraries.h"
 #endif
 #ifdef MOZ_THREADSTACKHELPER_PSEUDO
-#include "PseudoStack.h"
+#include "js/ProfilingStack.h"
 #endif
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Move.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/UniquePtr.h"
@@ -487,17 +487,17 @@ 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->mStack;
+  const volatile js::ProfileEntry* entry = mPseudoStack->entries;
   const volatile 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;
     }