зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland
This commit is contained in:
Коммит
f8d5f13130
|
@ -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;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче