--- 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;
}