Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 26 May 2017 12:40:44 +0200
changeset 585275 bdd2b6377f1d2d5fabbc2ed095b5e1c5950d8d4a
parent 585274 c735b95bef3689566552807ef300a08fa563d6f6 (current diff)
parent 585005 44e41de60c48a94a004494dd48f57dd1d8c157d8 (diff)
child 585276 e7efc643bc078cc791c2c165db609359c4587ed0
push id61093
push userdgottwald@mozilla.com
push dateFri, 26 May 2017 20:16:26 +0000
milestone55.0a1
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;
     }