Merge mozilla-central to autoland

This commit is contained in:
Carsten "Tomcat" Book 2017-05-26 12:40:44 +02:00
Родитель c1a123b391 e84601e7a7
Коммит f8d5f13130
14 изменённых файлов: 245 добавлений и 335 удалений

Просмотреть файл

@ -1572,8 +1572,13 @@ TimeoutManager::StartThrottlingTrackingTimeouts()
void void
TimeoutManager::OnDocumentLoaded() TimeoutManager::OnDocumentLoaded()
{ {
// 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(); MaybeStartThrottleTrackingTimout();
} }
}
void void
TimeoutManager::MaybeStartThrottleTrackingTimout() TimeoutManager::MaybeStartThrottleTrackingTimout()

Просмотреть файл

@ -7,15 +7,19 @@
#ifndef js_ProfilingStack_h #ifndef js_ProfilingStack_h
#define js_ProfilingStack_h #define js_ProfilingStack_h
#include <algorithm>
#include <stdint.h>
#include "jsbytecode.h" #include "jsbytecode.h"
#include "jstypes.h" #include "jstypes.h"
#include "js/TypeDecls.h" #include "js/TypeDecls.h"
#include "js/Utility.h" #include "js/Utility.h"
struct JSRuntime; struct JSRuntime;
class JSTracer; class JSTracer;
class PseudoStack;
namespace js { namespace js {
// A call stack can be specified to the JS engine such that all JS entry/exits // A call stack can be specified to the JS engine such that all JS entry/exits
@ -54,6 +58,8 @@ class ProfileEntry
// General purpose storage describing this frame. // General purpose storage describing this frame.
uint32_t volatile flags_; uint32_t volatile flags_;
static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
public: public:
// These traits are bit masks. Make sure they're powers of 2. // These traits are bit masks. Make sure they're powers of 2.
enum Flags : uint32_t { enum Flags : uint32_t {
@ -108,18 +114,27 @@ class ProfileEntry
void setLabel(const char* aLabel) volatile { label_ = aLabel; } void setLabel(const char* aLabel) volatile { label_ = aLabel; }
const char* label() const volatile { return label_; } const char* label() const volatile { return label_; }
void setDynamicString(const char* aDynamicString) volatile { dynamicString_ = aDynamicString; }
const char* dynamicString() const volatile { return dynamicString_; } const char* dynamicString() const volatile { return dynamicString_; }
void initJsFrame(JSScript* aScript, jsbytecode* aPc) volatile { void initCppFrame(const char* aLabel, const char* aDynamicString, void* sp, uint32_t aLine,
flags_ = 0; js::ProfileEntry::Flags aFlags, js::ProfileEntry::Category aCategory)
spOrScript = aScript; volatile
setPC(aPc); {
} label_ = aLabel;
void initCppFrame(void* aSp, uint32_t aLine) volatile { dynamicString_ = aDynamicString;
flags_ = IS_CPP_ENTRY; spOrScript = sp;
spOrScript = aSp;
lineOrPcOffset = static_cast<int32_t>(aLine); lineOrPcOffset = static_cast<int32_t>(aLine);
flags_ = aFlags | js::ProfileEntry::IS_CPP_ENTRY | uint32_t(aCategory);
}
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 { void setFlag(uint32_t flag) volatile {
@ -145,7 +160,7 @@ class ProfileEntry
MOZ_ASSERT(c >= Category::FIRST); MOZ_ASSERT(c >= Category::FIRST);
MOZ_ASSERT(c <= Category::LAST); MOZ_ASSERT(c <= Category::LAST);
flags_ &= ~CATEGORY_MASK; flags_ &= ~CATEGORY_MASK;
setFlag(static_cast<uint32_t>(c)); setFlag(uint32_t(c));
} }
void setOSR() volatile { void setOSR() volatile {
@ -180,7 +195,7 @@ class ProfileEntry
JS_FRIEND_API(jsbytecode*) pc() const volatile; JS_FRIEND_API(jsbytecode*) pc() const volatile;
JS_FRIEND_API(void) setPC(jsbytecode* pc) 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 // 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 // signify a nullptr pc, use a -1 index. This is checked against in
@ -194,8 +209,7 @@ class ProfileEntry
}; };
JS_FRIEND_API(void) JS_FRIEND_API(void)
SetContextProfilingStack(JSContext* cx, ProfileEntry* stack, mozilla::Atomic<uint32_t>* size, SetContextProfilingStack(JSContext* cx, PseudoStack* pseudoStack);
uint32_t max);
JS_FRIEND_API(void) JS_FRIEND_API(void)
EnableContextProfilingStack(JSContext* cx, bool enabled); EnableContextProfilingStack(JSContext* cx, bool enabled);
@ -205,4 +219,69 @@ RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
} // namespace js } // 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 */ #endif /* js_ProfilingStack_h */

Просмотреть файл

@ -13,15 +13,13 @@
#include "jsapi-tests/tests.h" #include "jsapi-tests/tests.h"
static js::ProfileEntry pstack[10]; static PseudoStack pseudoStack;
static mozilla::Atomic<uint32_t> psize; static uint32_t peakStackPointer = 0;
static uint32_t max_stack = 0;
static void static void
reset(JSContext* cx) reset(JSContext* cx)
{ {
psize = max_stack = 0; pseudoStack.stackPointer = 0;
memset(pstack, 0, sizeof(pstack));
cx->runtime()->geckoProfiler().stringsReset(); cx->runtime()->geckoProfiler().stringsReset();
cx->runtime()->geckoProfiler().enableSlowAssertions(true); cx->runtime()->geckoProfiler().enableSlowAssertions(true);
js::EnableContextProfilingStack(cx, true); js::EnableContextProfilingStack(cx, true);
@ -34,7 +32,7 @@ static const JSClass ptestClass = {
static bool static bool
test_fn(JSContext* cx, unsigned argc, JS::Value* vp) test_fn(JSContext* cx, unsigned argc, JS::Value* vp)
{ {
max_stack = psize; peakStackPointer = pseudoStack.stackPointer;
return true; return true;
} }
@ -82,7 +80,7 @@ static const JSFunctionSpec ptestFunctions[] = {
static JSObject* static JSObject*
initialize(JSContext* cx) initialize(JSContext* cx)
{ {
js::SetContextProfilingStack(cx, pstack, &psize, 10); js::SetContextProfilingStack(cx, &pseudoStack);
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
return JS_InitClass(cx, global, nullptr, &ptestClass, Prof, 0, return JS_InitClass(cx, global, nullptr, &ptestClass, Prof, 0,
nullptr, ptestFunctions, nullptr, nullptr); nullptr, ptestFunctions, nullptr, nullptr);
@ -108,15 +106,15 @@ BEGIN_TEST(testProfileStrings_isCalledWithInterpreter)
/* Make sure the stack resets and we have an entry for each stack */ /* Make sure the stack resets and we have an entry for each stack */
CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
&rval)); &rval));
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 0);
CHECK(max_stack >= 8); CHECK(peakStackPointer >= 8);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8); CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8);
/* Make sure the stack resets and we added no new entries */ /* 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(), CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
&rval)); &rval));
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 0);
CHECK(max_stack >= 8); CHECK(peakStackPointer >= 8);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8); CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8);
} }
reset(cx); reset(cx);
@ -125,20 +123,8 @@ BEGIN_TEST(testProfileStrings_isCalledWithInterpreter)
CHECK(JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(), CHECK(JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(),
&rval)); &rval));
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 5); CHECK(cx->runtime()->geckoProfiler().stringsCount() == 5);
CHECK(max_stack >= 6); CHECK(peakStackPointer >= 6);
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 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);
} }
return true; return true;
} }
@ -166,32 +152,19 @@ BEGIN_TEST(testProfileStrings_isCalledWithJIT)
/* Make sure the stack resets and we have an entry for each stack */ /* Make sure the stack resets and we have an entry for each stack */
CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
&rval)); &rval));
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 0);
CHECK(max_stack >= 8); CHECK(peakStackPointer >= 8);
/* Make sure the stack resets and we added no new entries */ /* Make sure the stack resets and we added no new entries */
uint32_t cnt = cx->runtime()->geckoProfiler().stringsCount(); uint32_t cnt = cx->runtime()->geckoProfiler().stringsCount();
max_stack = 0; peakStackPointer = 0;
CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
&rval)); &rval));
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 0);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == cnt); 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; return true;
} }
END_TEST(testProfileStrings_isCalledWithJIT) END_TEST(testProfileStrings_isCalledWithJIT)
@ -211,11 +184,12 @@ BEGIN_TEST(testProfileStrings_isCalledWhenError)
bool ok = JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(), bool ok = JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(),
&rval); &rval);
CHECK(!ok); CHECK(!ok);
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 0);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1); CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1);
JS_ClearPendingException(cx); JS_ClearPendingException(cx);
} }
return true; return true;
} }
END_TEST(testProfileStrings_isCalledWhenError) END_TEST(testProfileStrings_isCalledWhenError)
@ -234,8 +208,8 @@ BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
/* enable it in the middle of JS and make sure things check out */ /* enable it in the middle of JS and make sure things check out */
JS::RootedValue rval(cx); JS::RootedValue rval(cx);
JS_CallFunctionName(cx, global, "a", JS::HandleValueArray::empty(), &rval); JS_CallFunctionName(cx, global, "a", JS::HandleValueArray::empty(), &rval);
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 0);
CHECK(max_stack >= 1); CHECK(peakStackPointer >= 1);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1); CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1);
} }
@ -246,7 +220,7 @@ BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
/* now disable in the middle of js */ /* now disable in the middle of js */
JS::RootedValue rval(cx); JS::RootedValue rval(cx);
JS_CallFunctionName(cx, global, "c", JS::HandleValueArray::empty(), &rval); 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); }"); EXEC("function e() { var p = new Prof(); d(p); p.enable(); b(p); }");
@ -255,8 +229,8 @@ BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
/* now disable in the middle of js, but re-enable before final exit */ /* now disable in the middle of js, but re-enable before final exit */
JS::RootedValue rval(cx); JS::RootedValue rval(cx);
JS_CallFunctionName(cx, global, "e", JS::HandleValueArray::empty(), &rval); JS_CallFunctionName(cx, global, "e", JS::HandleValueArray::empty(), &rval);
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 0);
CHECK(max_stack >= 3); CHECK(peakStackPointer >= 3);
} }
EXEC("function h() { }"); EXEC("function h() { }");
@ -269,7 +243,7 @@ BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
/* disable, and make sure that if we try to re-enter the JIT the pop /* disable, and make sure that if we try to re-enter the JIT the pop
* will still happen */ * will still happen */
JS_CallFunctionName(cx, global, "f", JS::HandleValueArray::empty(), &rval); JS_CallFunctionName(cx, global, "f", JS::HandleValueArray::empty(), &rval);
CHECK(psize == 0); CHECK(pseudoStack.stackPointer == 0);
} }
return true; return true;
} }

Просмотреть файл

@ -387,8 +387,7 @@ ShellContext::ShellContext(JSContext* cx)
quitting(false), quitting(false),
readLineBufPos(0), readLineBufPos(0),
errFilePtr(nullptr), errFilePtr(nullptr),
outFilePtr(nullptr), outFilePtr(nullptr)
geckoProfilingStackSize(0)
{} {}
ShellContext* ShellContext*
@ -5217,8 +5216,7 @@ EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp)
if (cx->runtime()->geckoProfiler().installed()) if (cx->runtime()->geckoProfiler().installed())
MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false)); MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
SetContextProfilingStack(cx, sc->geckoProfilingStack, &sc->geckoProfilingStackSize, SetContextProfilingStack(cx, &sc->geckoProfilingStack);
ShellContext::GeckoProfilingMaxStackSize);
cx->runtime()->geckoProfiler().enableSlowAssertions(false); cx->runtime()->geckoProfiler().enableSlowAssertions(false);
if (!cx->runtime()->geckoProfiler().enable(true)) if (!cx->runtime()->geckoProfiler().enable(true))
JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler"); JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler");
@ -5250,8 +5248,7 @@ EnableGeckoProfilingWithSlowAssertions(JSContext* cx, unsigned argc, Value* vp)
if (cx->runtime()->geckoProfiler().installed()) if (cx->runtime()->geckoProfiler().installed())
MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false)); MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
SetContextProfilingStack(cx, sc->geckoProfilingStack, &sc->geckoProfilingStackSize, SetContextProfilingStack(cx, &sc->geckoProfilingStack);
ShellContext::GeckoProfilingMaxStackSize);
cx->runtime()->geckoProfiler().enableSlowAssertions(true); cx->runtime()->geckoProfiler().enableSlowAssertions(true);
if (!cx->runtime()->geckoProfiler().enable(true)) if (!cx->runtime()->geckoProfiler().enable(true))
JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler"); JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler");

Просмотреть файл

@ -203,9 +203,7 @@ struct ShellContext
js::shell::RCFile** errFilePtr; js::shell::RCFile** errFilePtr;
js::shell::RCFile** outFilePtr; js::shell::RCFile** outFilePtr;
static const uint32_t GeckoProfilingMaxStackSize = 1000; PseudoStack geckoProfilingStack;
js::ProfileEntry geckoProfilingStack[GeckoProfilingMaxStackSize];
mozilla::Atomic<uint32_t> geckoProfilingStackSize;
OffThreadState offThreadState; OffThreadState offThreadState;

Просмотреть файл

@ -28,9 +28,7 @@ using mozilla::DebugOnly;
GeckoProfiler::GeckoProfiler(JSRuntime* rt) GeckoProfiler::GeckoProfiler(JSRuntime* rt)
: rt(rt), : rt(rt),
strings(mutexid::GeckoProfilerStrings), strings(mutexid::GeckoProfilerStrings),
stack_(nullptr), pseudoStack_(nullptr),
size_(nullptr),
max_(0),
slowAssertions(false), slowAssertions(false),
enabled_(false), enabled_(false),
eventMarker_(nullptr) eventMarker_(nullptr)
@ -49,14 +47,12 @@ GeckoProfiler::init()
} }
void 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()); MOZ_ASSERT(strings.lock()->initialized());
stack_ = stack; pseudoStack_ = pseudoStack;
size_ = size;
max_ = max;
} }
void void
@ -210,106 +206,55 @@ GeckoProfiler::enter(JSContext* cx, JSScript* script, JSFunction* maybeFun)
// In debug builds, assert the JS pseudo frames already on the stack // 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 // have a non-null pc. Only look at the top frames to avoid quadratic
// behavior. // behavior.
if (*size_ > 0 && *size_ - 1 < max_) { uint32_t sp = pseudoStack_->stackPointer;
size_t start = (*size_ > 4) ? *size_ - 4 : 0; if (sp > 0 && sp - 1 < PseudoStack::MaxEntries) {
for (size_t i = start; i < *size_ - 1; i++) size_t start = (sp > 4) ? sp - 4 : 0;
MOZ_ASSERT_IF(stack_[i].isJs(), stack_[i].pc() != nullptr); for (size_t i = start; i < sp - 1; i++)
MOZ_ASSERT_IF(pseudoStack_->entries[i].isJs(), pseudoStack_->entries[i].pc());
} }
#endif #endif
push("", dynamicString, /* sp = */ nullptr, script, script->code()); pseudoStack_->pushJsFrame("", dynamicString, script, script->code());
return true; return true;
} }
void void
GeckoProfiler::exit(JSScript* script, JSFunction* maybeFun) GeckoProfiler::exit(JSScript* script, JSFunction* maybeFun)
{ {
pop(); pseudoStack_->pop();
#ifdef DEBUG #ifdef DEBUG
/* Sanity check to make sure push/pop balanced */ /* 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); const char* dynamicString = profileString(script, maybeFun);
/* Can't fail lookup because we should already be in the set */ /* Can't fail lookup because we should already be in the set */
MOZ_ASSERT(dynamicString != nullptr); MOZ_ASSERT(dynamicString);
// Bug 822041 // Bug 822041
if (!stack_[*size_].isJs()) { if (!pseudoStack_->entries[sp].isJs()) {
fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n"); fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
fprintf(stderr, " stack=%p size=%d/%d\n", (void*) stack_, size(), max_); fprintf(stderr, " entries=%p size=%u/%u\n",
for (int32_t i = *size_; i >= 0; i--) { (void*) pseudoStack_->entries,
if (stack_[i].isJs()) uint32_t(pseudoStack_->stackPointer),
fprintf(stderr, " [%d] JS %s\n", i, stack_[i].dynamicString()); 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 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()); volatile ProfileEntry& entry = pseudoStack_->entries[sp];
MOZ_ASSERT(stack_[*size_].script() == script); MOZ_ASSERT(entry.isJs());
MOZ_ASSERT(strcmp((const char*) stack_[*size_].dynamicString(), dynamicString) == 0); MOZ_ASSERT(entry.script() == script);
stack_[*size_].setDynamicString(nullptr); MOZ_ASSERT(strcmp((const char*) entry.dynamicString(), dynamicString) == 0);
stack_[*size_].setPC(nullptr);
} }
#endif #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 * Serializes the script/function pair into a "descriptive string" which is
* allowed to fail. This function cannot trigger a GC because it could finalize * allowed to fail. This function cannot trigger a GC because it could finalize
@ -365,12 +310,12 @@ GeckoProfiler::allocProfileString(JSScript* script, JSFunction* maybeFun)
} }
void void
GeckoProfiler::trace(JSTracer* trc) GeckoProfiler::trace(JSTracer* trc) volatile
{ {
if (stack_) { if (pseudoStack_) {
size_t limit = Min(uint32_t(*size_), max_); size_t size = pseudoStack_->stackSize();
for (size_t i = 0; i < limit; i++) for (size_t i = 0; i < size; i++)
stack_[i].trace(trc); pseudoStack_->entries[i].trace(trc);
} }
} }
@ -408,7 +353,7 @@ GeckoProfiler::checkStringsMapAfterMovingGC()
#endif #endif
void void
ProfileEntry::trace(JSTracer* trc) ProfileEntry::trace(JSTracer* trc) volatile
{ {
if (isJs()) { if (isJs()) {
JSScript* s = rawScript(); JSScript* s = rawScript();
@ -427,11 +372,15 @@ GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSRuntime* rt,
profiler = nullptr; profiler = nullptr;
return; 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. // We want to push a CPP frame so the profiler can correctly order JS and native stacks.
profiler->beginPseudoJS("js::RunScript", this); profiler->pseudoStack_->pushCppFrame(
profiler->push("js::RunScript", /* dynamicString = */ nullptr, /* sp = */ nullptr, script, "js::RunScript", /* dynamicString = */ nullptr, /* sp = */ this, /* line = */ 0,
script->code()); js::ProfileEntry::Category::OTHER, js::ProfileEntry::BEGIN_PSEUDO_JS);
profiler->pseudoStack_->pushJsFrame(
"js::RunScript", /* dynamicString = */ nullptr, script, script->code());
} }
GeckoProfilerEntryMarker::~GeckoProfilerEntryMarker() GeckoProfilerEntryMarker::~GeckoProfilerEntryMarker()
@ -439,9 +388,9 @@ GeckoProfilerEntryMarker::~GeckoProfilerEntryMarker()
if (profiler == nullptr) if (profiler == nullptr)
return; return;
profiler->pop(); profiler->pseudoStack_->pop(); // the JS frame
profiler->endPseudoJS(); profiler->pseudoStack_->pop(); // the BEGIN_PSEUDO_JS frame
MOZ_ASSERT(size_before == *profiler->size_); MOZ_ASSERT(spBefore_ == profiler->stackPointer());
} }
AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSRuntime* rt, const char* label, AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSRuntime* rt, const char* label,
@ -454,10 +403,14 @@ AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSRuntime* rt, const char* label,
profiler_ = nullptr; profiler_ = nullptr;
return; return;
} }
sizeBefore_ = *profiler_->size_; spBefore_ = profiler_->stackPointer();
profiler_->beginPseudoJS(label, this);
profiler_->push(label, /* dynamicString = */ nullptr, /* sp = */ this, /* script = */ nullptr, profiler_->pseudoStack_->pushCppFrame(
/* pc = */ nullptr, category); 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() AutoGeckoProfilerEntry::~AutoGeckoProfilerEntry()
@ -465,9 +418,9 @@ AutoGeckoProfilerEntry::~AutoGeckoProfilerEntry()
if (!profiler_) if (!profiler_)
return; return;
profiler_->pop(); profiler_->pseudoStack_->pop(); // the C++ frame
profiler_->endPseudoJS(); profiler_->pseudoStack_->pop(); // the BEGIN_PSEUDO_JS frame
MOZ_ASSERT(sizeBefore_ == *profiler_->size_); MOZ_ASSERT(spBefore_ == profiler_->stackPointer());
} }
GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame
@ -475,18 +428,22 @@ GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bo
: profiler(&rt->geckoProfiler()) : profiler(&rt->geckoProfiler())
{ {
MOZ_GUARD_OBJECT_NOTIFIER_INIT; MOZ_GUARD_OBJECT_NOTIFIER_INIT;
if (!hasProfilerFrame || !profiler->enabled() || if (!hasProfilerFrame || !profiler->enabled()) {
profiler->size() >= profiler->maxSize())
{
profiler = nullptr; profiler = nullptr;
return; return;
} }
size_before = profiler->size(); uint32_t sp = profiler->pseudoStack_->stackPointer;
if (profiler->size() == 0) if (sp >= PseudoStack::MaxEntries) {
profiler = nullptr;
return;
}
spBefore_ = sp;
if (sp == 0)
return; return;
ProfileEntry& entry = profiler->stack()[profiler->size() - 1]; volatile ProfileEntry& entry = profiler->pseudoStack_->entries[sp - 1];
MOZ_ASSERT(entry.isJs()); MOZ_ASSERT(entry.isJs());
entry.setOSR(); entry.setOSR();
} }
@ -496,11 +453,12 @@ GeckoProfilerBaselineOSRMarker::~GeckoProfilerBaselineOSRMarker()
if (profiler == nullptr) if (profiler == nullptr)
return; return;
MOZ_ASSERT(size_before == *profiler->size_); uint32_t sp = profiler->stackPointer();
if (profiler->size() == 0) MOZ_ASSERT(spBefore_ == sp);
if (sp == 0)
return; return;
ProfileEntry& entry = profiler->stack()[profiler->size() - 1]; volatile ProfileEntry& entry = profiler->stack()[sp - 1];
MOZ_ASSERT(entry.isJs()); MOZ_ASSERT(entry.isJs());
entry.unsetOSR(); entry.unsetOSR();
} }
@ -539,19 +497,24 @@ ProfileEntry::pc() const volatile
return script ? script->offsetToPC(lineOrPcOffset) : nullptr; 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) JS_FRIEND_API(void)
ProfileEntry::setPC(jsbytecode* pc) volatile ProfileEntry::setPC(jsbytecode* pc) volatile
{ {
MOZ_ASSERT(isJs()); MOZ_ASSERT(isJs());
JSScript* script = this->script(); JSScript* script = this->script();
MOZ_ASSERT(script); // This should not be called while profiling is suppressed. 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_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_FRIEND_API(void)

Просмотреть файл

@ -129,38 +129,24 @@ class GeckoProfiler
JSRuntime* rt; JSRuntime* rt;
ExclusiveData<ProfileStringMap> strings; ExclusiveData<ProfileStringMap> strings;
ProfileEntry* stack_; PseudoStack* pseudoStack_;
mozilla::Atomic<uint32_t>* size_;
uint32_t max_;
bool slowAssertions; bool slowAssertions;
uint32_t enabled_; uint32_t enabled_;
void (*eventMarker_)(const char*); void (*eventMarker_)(const char*);
UniqueChars allocProfileString(JSScript* script, JSFunction* function); 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: public:
explicit GeckoProfiler(JSRuntime* rt); explicit GeckoProfiler(JSRuntime* rt);
bool init(); bool init();
uint32_t* addressOfMaxSize() { uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; }
return &max_; volatile ProfileEntry* stack() { return pseudoStack_->entries; }
}
ProfileEntry** addressOfStack() {
return &stack_;
}
uint32_t maxSize() { return max_; }
uint32_t size() { MOZ_ASSERT(installed()); return *size_; }
ProfileEntry* stack() { return stack_; }
/* management of whether instrumentation is on or off */ /* management of whether instrumentation is on or off */
bool enabled() { MOZ_ASSERT_IF(enabled_, installed()); return enabled_; } 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); MOZ_MUST_USE bool enable(bool enabled);
void enableSlowAssertions(bool enabled) { slowAssertions = enabled; } void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
bool slowAssertionsEnabled() { return slowAssertions; } bool slowAssertionsEnabled() { return slowAssertions; }
@ -177,18 +163,18 @@ class GeckoProfiler
bool enter(JSContext* cx, JSScript* script, JSFunction* maybeFun); bool enter(JSContext* cx, JSScript* script, JSFunction* maybeFun);
void exit(JSScript* script, JSFunction* maybeFun); void exit(JSScript* script, JSFunction* maybeFun);
void updatePC(JSScript* script, jsbytecode* pc) { void updatePC(JSScript* script, jsbytecode* pc) {
if (enabled() && *size_ - 1 < max_) { if (!enabled())
MOZ_ASSERT(*size_ > 0); return;
MOZ_ASSERT(stack_[*size_ - 1].rawScript() == script);
stack_[*size_ - 1].setPC(pc); 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 setProfilingStack(PseudoStack* pseudoStack);
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 setEventMarker(void (*fn)(const char*)); void setEventMarker(void (*fn)(const char*));
const char* profileString(JSScript* script, JSFunction* maybeFun); const char* profileString(JSScript* script, JSFunction* maybeFun);
void onScriptFinalized(JSScript* script); void onScriptFinalized(JSScript* script);
@ -203,7 +189,7 @@ class GeckoProfiler
return &enabled_; return &enabled_;
} }
void trace(JSTracer* trc); void trace(JSTracer* trc) volatile;
void fixupStringsMapAfterMovingGC(); void fixupStringsMapAfterMovingGC();
#ifdef JSGC_HASH_TABLE_CHECKS #ifdef JSGC_HASH_TABLE_CHECKS
void checkStringsMapAfterMovingGC(); void checkStringsMapAfterMovingGC();
@ -237,7 +223,7 @@ class MOZ_RAII GeckoProfilerEntryMarker
private: private:
GeckoProfiler* profiler; GeckoProfiler* profiler;
mozilla::DebugOnly<uint32_t> size_before; mozilla::DebugOnly<uint32_t> spBefore_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
}; };
@ -256,7 +242,7 @@ class MOZ_NONHEAP_CLASS AutoGeckoProfilerEntry
private: private:
GeckoProfiler* profiler_; GeckoProfiler* profiler_;
mozilla::DebugOnly<uint32_t> sizeBefore_; mozilla::DebugOnly<uint32_t> spBefore_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
}; };
@ -274,7 +260,7 @@ class MOZ_RAII GeckoProfilerBaselineOSRMarker
private: private:
GeckoProfiler* profiler; GeckoProfiler* profiler;
mozilla::DebugOnly<uint32_t> size_before; mozilla::DebugOnly<uint32_t> spBefore_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
}; };

Просмотреть файл

@ -1683,7 +1683,7 @@ pref("network.http.keep_empty_response_headers_as_empty_string", true);
pref("network.http.max_response_header_size", 393216); pref("network.http.max_response_header_size", 393216);
// If we should attempt to race the cache and network // 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_normal_threshold", 8);
pref("network.http.rcwn.cache_queue_priority_threshold", 2); pref("network.http.rcwn.cache_queue_priority_threshold", 2);
// We might attempt to race the cache with the network only if a resource // We might attempt to race the cache with the network only if a resource

Просмотреть файл

@ -12,7 +12,7 @@
#include "platform.h" #include "platform.h"
#include "ProfileBuffer.h" #include "ProfileBuffer.h"
#include "PseudoStack.h" #include "js/ProfilingStack.h"
// Stub eventMarker function for js-engine event generation. // Stub eventMarker function for js-engine event generation.
void ProfilerJSEventMarker(const char* aEvent); void ProfilerJSEventMarker(const char* aEvent);
@ -230,11 +230,10 @@ public:
mContext = aContext; mContext = aContext;
js::SetContextProfilingStack( // We give the JS engine a non-owning reference to the RacyInfo (just the
aContext, // PseudoStack, really). It's important that the JS engine doesn't touch
(js::ProfileEntry*) RacyInfo()->mStack, // this once the thread dies.
RacyInfo()->AddressOfStackPointer(), js::SetContextProfilingStack(aContext, RacyInfo());
(uint32_t) mozilla::ArrayLength(RacyInfo()->mStack));
PollJSSampling(); PollJSSampling();
} }

Просмотреть файл

@ -24,7 +24,6 @@
#include "mozilla/ThreadLocal.h" #include "mozilla/ThreadLocal.h"
#include "mozilla/TimeStamp.h" #include "mozilla/TimeStamp.h"
#include "mozilla/StaticPtr.h" #include "mozilla/StaticPtr.h"
#include "PseudoStack.h"
#include "ThreadInfo.h" #include "ThreadInfo.h"
#include "nsIHttpProtocolHandler.h" #include "nsIHttpProtocolHandler.h"
#include "nsIObserverService.h" #include "nsIObserverService.h"
@ -719,7 +718,7 @@ AddPseudoEntry(PSLockRef aLock, ProfileBuffer* aBuffer,
if (!entry.pc()) { if (!entry.pc()) {
// The JIT only allows the top-most entry to have a nullptr pc. // The JIT only allows the top-most entry to have a nullptr pc.
MOZ_ASSERT(&entry == MOZ_ASSERT(&entry ==
&aRacyInfo->mStack[aRacyInfo->stackSize() - 1]); &aRacyInfo->entries[aRacyInfo->stackSize() - 1]);
} else { } else {
lineno = JS_PCToLineNumber(script, entry.pc()); lineno = JS_PCToLineNumber(script, entry.pc());
} }
@ -779,7 +778,7 @@ MergeStacksIntoProfile(PSLockRef aLock, ProfileBuffer* aBuffer,
const TickSample& aSample, NativeStack& aNativeStack) const TickSample& aSample, NativeStack& aNativeStack)
{ {
NotNull<RacyThreadInfo*> racyInfo = aSample.mRacyInfo; NotNull<RacyThreadInfo*> racyInfo = aSample.mRacyInfo;
volatile js::ProfileEntry* pseudoFrames = racyInfo->mStack; volatile js::ProfileEntry* pseudoEntries = racyInfo->entries;
uint32_t pseudoCount = racyInfo->stackSize(); uint32_t pseudoCount = racyInfo->stackSize();
JSContext* context = aSample.mJSContext; JSContext* context = aSample.mJSContext;
@ -854,10 +853,10 @@ MergeStacksIntoProfile(PSLockRef aLock, ProfileBuffer* aBuffer,
uint8_t* nativeStackAddr = nullptr; uint8_t* nativeStackAddr = nullptr;
if (pseudoIndex != pseudoCount) { if (pseudoIndex != pseudoCount) {
volatile js::ProfileEntry& pseudoFrame = pseudoFrames[pseudoIndex]; volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
if (pseudoFrame.isCpp()) { if (pseudoEntry.isCpp()) {
lastPseudoCppStackAddr = (uint8_t*) pseudoFrame.stackAddress(); lastPseudoCppStackAddr = (uint8_t*) pseudoEntry.stackAddress();
} }
// Skip any pseudo-stack JS frames which are marked isOSR. Pseudostack // Skip any pseudo-stack JS frames which are marked isOSR. Pseudostack
@ -866,7 +865,7 @@ MergeStacksIntoProfile(PSLockRef aLock, ProfileBuffer* aBuffer,
// pseudoframe and jit frame being recorded (and showing up twice), the // pseudoframe and jit frame being recorded (and showing up twice), the
// interpreter marks the interpreter pseudostack entry with the OSR flag // interpreter marks the interpreter pseudostack entry with the OSR flag
// to ensure that it doesn't get counted. // to ensure that it doesn't get counted.
if (pseudoFrame.isJs() && pseudoFrame.isOSR()) { if (pseudoEntry.isJs() && pseudoEntry.isOSR()) {
pseudoIndex++; pseudoIndex++;
continue; continue;
} }
@ -905,8 +904,8 @@ MergeStacksIntoProfile(PSLockRef aLock, ProfileBuffer* aBuffer,
// Check to see if pseudoStack frame is top-most. // Check to see if pseudoStack frame is top-most.
if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) { if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
MOZ_ASSERT(pseudoIndex < pseudoCount); MOZ_ASSERT(pseudoIndex < pseudoCount);
volatile js::ProfileEntry& pseudoFrame = pseudoFrames[pseudoIndex]; volatile js::ProfileEntry& pseudoEntry = pseudoEntries[pseudoIndex];
AddPseudoEntry(aLock, aBuffer, pseudoFrame, racyInfo); AddPseudoEntry(aLock, aBuffer, pseudoEntry, racyInfo);
pseudoIndex++; pseudoIndex++;
continue; continue;
} }
@ -1048,7 +1047,7 @@ DoNativeBacktrace(PSLockRef aLock, ProfileBuffer* aBuffer,
for (uint32_t i = racyInfo->stackSize(); i > 0; --i) { for (uint32_t i = racyInfo->stackSize(); i > 0; --i) {
// The pseudostack grows towards higher indices, so we iterate // The pseudostack grows towards higher indices, so we iterate
// backwards (from callee to caller). // 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) { if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
// Found JIT entry frame. Unwind up to that point (i.e., force // Found JIT entry frame. Unwind up to that point (i.e., force
// the stack walk to stop before the block of saved registers; // the stack walk to stop before the block of saved registers;
@ -2815,13 +2814,13 @@ profiler_get_backtrace_noalloc(char *output, size_t outputSize)
bool includeDynamicString = !ActivePS::FeaturePrivacy(lock); bool includeDynamicString = !ActivePS::FeaturePrivacy(lock);
volatile js::ProfileEntry* pseudoFrames = pseudoStack->mStack; volatile js::ProfileEntry* pseudoEntries = pseudoStack->entries;
uint32_t pseudoCount = pseudoStack->stackSize(); uint32_t pseudoCount = pseudoStack->stackSize();
for (uint32_t i = 0; i < pseudoCount; i++) { for (uint32_t i = 0; i < pseudoCount; i++) {
const char* label = pseudoFrames[i].label(); const char* label = pseudoEntries[i].label();
const char* dynamicString = const char* dynamicString =
includeDynamicString ? pseudoFrames[i].dynamicString() : nullptr; includeDynamicString ? pseudoEntries[i].dynamicString() : nullptr;
size_t labelLength = strlen(label); size_t labelLength = strlen(label);
if (dynamicString) { if (dynamicString) {
// Put the label, maybe a space, and the dynamic string into output. // Put the label, maybe a space, and the dynamic string into output.

Просмотреть файл

@ -12,7 +12,6 @@ if CONFIG['MOZ_GECKO_PROFILER']:
EXPORTS += [ EXPORTS += [
'public/CrossProcessProfilerController.h', 'public/CrossProcessProfilerController.h',
'public/ProfilerMarkerPayload.h', 'public/ProfilerMarkerPayload.h',
'public/PseudoStack.h',
'public/shared-libraries.h', 'public/shared-libraries.h',
] ]
UNIFIED_SOURCES += [ UNIFIED_SOURCES += [

Просмотреть файл

@ -391,7 +391,6 @@ PROFILER_FUNC(void* profiler_get_stack_top(), nullptr)
#include "js/ProfilingStack.h" #include "js/ProfilingStack.h"
#include "mozilla/Sprintf.h" #include "mozilla/Sprintf.h"
#include "mozilla/ThreadLocal.h" #include "mozilla/ThreadLocal.h"
#include "PseudoStack.h"
#include "nscore.h" #include "nscore.h"
// Make sure that we can use std::min here without the Windows headers messing with us. // Make sure that we can use std::min here without the Windows headers messing with us.
@ -416,7 +415,7 @@ extern MOZ_THREAD_LOCAL(PseudoStack*) sPseudoStack;
// necessarily bounded by the lifetime of the thread, which ensures that the // necessarily bounded by the lifetime of the thread, which ensures that the
// references held can't be used after the PseudoStack is destroyed. // references held can't be used after the PseudoStack is destroyed.
inline void* 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, void* aFrameAddress, uint32_t aLine,
const char* aDynamicString = nullptr) const char* aDynamicString = nullptr)
{ {
@ -426,7 +425,7 @@ profiler_call_enter(const char* aInfo, js::ProfileEntry::Category aCategory,
if (!pseudoStack) { if (!pseudoStack) {
return 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 // 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(). // used to avoid having to call TLSInfo::RacyInfo() in profiler_call_exit().

Просмотреть файл

@ -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

Просмотреть файл

@ -13,7 +13,7 @@
#include "shared-libraries.h" #include "shared-libraries.h"
#endif #endif
#ifdef MOZ_THREADSTACKHELPER_PSEUDO #ifdef MOZ_THREADSTACKHELPER_PSEUDO
#include "PseudoStack.h" #include "js/ProfilingStack.h"
#endif #endif
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
@ -492,7 +492,7 @@ ThreadStackHelper::FillStackBuffer()
intptr_t availableBufferSize = intptr_t(reservedBufferSize); intptr_t availableBufferSize = intptr_t(reservedBufferSize);
// Go from front to back // 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(); const volatile js::ProfileEntry* end = entry + mPseudoStack->stackSize();
// Deduplicate identical, consecutive frames // Deduplicate identical, consecutive frames
const char* prevLabel = nullptr; const char* prevLabel = nullptr;