Bug 1004726 - Clean up profiler pseudo-stack representation (relanded), r=djvj,jchen

This commit is contained in:
Victor Porof 2014-05-28 18:44:41 -04:00
Родитель ac61ea89ba
Коммит 07cede5f0b
12 изменённых файлов: 167 добавлений и 123 удалений

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

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 999651 et al. apparently require a clobber, unclear why
Bug 1004726 requires a clobber for B2G Emulator builds

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

@ -8,7 +8,7 @@
#define js_ProfilingStack_h
#include "mozilla/NullPtr.h"
#include "jsbytecode.h"
#include "jstypes.h"
@ -34,67 +34,97 @@ class ProfileEntry
// If the size modification were somehow reordered before the stores, then
// if a sample were taken it would be examining bogus information.
//
// A ProfileEntry represents both a C++ profile entry and a JS one. Both use
// the string as a description, but JS uses the sp as nullptr or (void*)1 to
// indicate that it is a JS entry. The script_ is then only ever examined for
// a JS entry, and the idx is used by both, but with different meanings.
//
const char * volatile string; // Descriptive string of this entry
void * volatile sp; // Relevant stack pointer for the entry,
// less than or equal to SCRIPT_OPT_STACKPOINTER for js
// script entries, greater for non-js entries.
JSScript * volatile script_; // if js(), non-null script which is running - low bit
// indicates if script is optimized or not.
int32_t volatile idx; // if js(), idx of pc, otherwise line number
// A ProfileEntry represents both a C++ profile entry and a JS one.
// Descriptive string of this entry.
const char * volatile string;
// Stack pointer for non-JS entries, the script pointer otherwise.
void * volatile spOrScript;
// Line number for non-JS entries, the bytecode offset otherwise.
int32_t volatile lineOrPc;
// General purpose storage describing this frame.
uint32_t volatile flags;
public:
static const uintptr_t SCRIPT_OPT_STACKPOINTER = 0x1;
ProfileEntry(void) : flags(0) {}
// These traits are bit masks. Make sure they're powers of 2.
enum Flags {
// Indicate whether a profile entry represents a CPP frame. If not set,
// a JS frame is assumed by default. You're not allowed to publicly
// change the frame type. Instead, call `setJsFrame` or `setCppFrame`.
IS_CPP_ENTRY = 0x01,
// Indicate that copying the frame label is not necessary when taking a
// sample of the pseudostack.
FRAME_LABEL_COPY = 0x02
};
// All of these methods are marked with the 'volatile' keyword because SPS's
// representation of the stack is stored such that all ProfileEntry
// instances are volatile. These methods would not be available unless they
// were marked as volatile as well.
bool js() const volatile {
MOZ_ASSERT_IF(uintptr_t(sp) <= SCRIPT_OPT_STACKPOINTER, script_ != nullptr);
return uintptr_t(sp) <= SCRIPT_OPT_STACKPOINTER;
}
bool isCpp() const volatile { return hasFlag(IS_CPP_ENTRY); }
bool isJs() const volatile { return !isCpp(); }
uint32_t line() const volatile { MOZ_ASSERT(!js()); return idx; }
JSScript *script() const volatile { MOZ_ASSERT(js()); return script_; }
bool scriptIsOptimized() const volatile {
MOZ_ASSERT(js());
return uintptr_t(sp) <= SCRIPT_OPT_STACKPOINTER;
}
void *stackAddress() const volatile {
if (js())
return nullptr;
return sp;
}
bool isCopyLabel() const volatile { return hasFlag(FRAME_LABEL_COPY); };
void setLabel(const char *aString) volatile { string = aString; }
const char *label() const volatile { return string; }
void setLine(uint32_t aLine) volatile { MOZ_ASSERT(!js()); idx = aLine; }
void setLabel(const char *aString) volatile { string = aString; }
void setStackAddress(void *aSp) volatile { sp = aSp; }
void setScript(JSScript *aScript) volatile { script_ = aScript; }
void setJsFrame(JSScript *aScript, jsbytecode *aPc) volatile {
flags &= ~IS_CPP_ENTRY;
spOrScript = aScript;
setPC(aPc);
}
void setCppFrame(void *aSp, uint32_t aLine) volatile {
flags |= IS_CPP_ENTRY;
spOrScript = aSp;
lineOrPc = aLine;
}
void setFlag(Flags flag) volatile {
MOZ_ASSERT(flag != IS_CPP_ENTRY);
flags |= flag;
}
void unsetFlag(Flags flag) volatile {
MOZ_ASSERT(flag != IS_CPP_ENTRY);
flags &= ~flag;
}
bool hasFlag(Flags flag) const volatile {
return bool(flags & uint32_t(flag));
}
void *stackAddress() const volatile {
MOZ_ASSERT(!isJs());
return spOrScript;
}
JSScript *script() const volatile {
MOZ_ASSERT(isJs());
return (JSScript *)spOrScript;
}
uint32_t line() const volatile {
MOZ_ASSERT(!isJs());
return lineOrPc;
}
// We can't know the layout of JSScript, so look in vm/SPSProfiler.cpp.
JS_FRIEND_API(jsbytecode *) pc() const volatile;
JS_FRIEND_API(void) setPC(jsbytecode *pc) volatile;
static size_t offsetOfString() { return offsetof(ProfileEntry, string); }
static size_t offsetOfStackAddress() { return offsetof(ProfileEntry, sp); }
static size_t offsetOfPCIdx() { return offsetof(ProfileEntry, idx); }
static size_t offsetOfScript() { return offsetof(ProfileEntry, script_); }
// The offset of a pc into a script's code can actually be 0, so to
// signify a nullptr pc, use a -1 index. This is checked against in
// pc() and setPC() to set/get the right pc.
static const int32_t NullPCOffset = -1;
// The index used in the entry can either be a line number or the offset of
// a pc into a script's code. To signify a nullptr pc, use a -1 index. This
// is checked against in pc() and setPC() to set/get the right pc.
static const int32_t NullPCIndex = -1;
// This bit is added to the stack address to indicate that copying the
// frame label is not necessary when taking a sample of the pseudostack.
static const uintptr_t NoCopyBit = 1;
static size_t offsetOfLabel() { return offsetof(ProfileEntry, string); }
static size_t offsetOfSpOrScript() { return offsetof(ProfileEntry, spOrScript); }
static size_t offsetOfLineOrPc() { return offsetof(ProfileEntry, lineOrPc); }
static size_t offsetOfFlags() { return offsetof(ProfileEntry, flags); }
};
JS_FRIEND_API(void)

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

@ -1942,10 +1942,10 @@ MacroAssembler::spsMarkJit(SPSProfiler *p, Register framePtr, Register temp)
// and won't be regenerated when SPS state changes.
spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfString()));
storePtr(framePtr, Address(temp, ProfileEntry::offsetOfStackAddress()));
storePtr(ImmWord(uintptr_t(0)), Address(temp, ProfileEntry::offsetOfScript()));
store32(Imm32(ProfileEntry::NullPCIndex), Address(temp, ProfileEntry::offsetOfPCIdx()));
storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfLabel()));
storePtr(framePtr, Address(temp, ProfileEntry::offsetOfSpOrScript()));
store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
store32(Imm32(ProfileEntry::IS_CPP_ENTRY), Address(temp, ProfileEntry::offsetOfFlags()));
/* Always increment the stack size, whether or not we actually pushed. */
bind(&stackFull);

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

@ -1016,9 +1016,14 @@ class MacroAssembler : public MacroAssemblerSpecific
add32(Imm32(offset), temp);
branch32(Assembler::GreaterThanOrEqual, temp, Imm32(p->maxSize()), full);
// 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
JS_STATIC_ASSERT(sizeof(ProfileEntry) == (2 * sizeof(void *)) + 8);
if (sizeof(void *) == 4) {
lshiftPtr(Imm32(4), temp);
} else {
lshiftPtr(Imm32(3), temp);
mulBy3(temp, temp);
}
addPtr(ImmPtr(p->stack()), temp);
}
@ -1042,9 +1047,14 @@ class MacroAssembler : public MacroAssemblerSpecific
// Test against max size.
branch32(Assembler::LessThanOrEqual, AbsoluteAddress(p->addressOfMaxSize()), temp, full);
// 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
JS_STATIC_ASSERT(sizeof(ProfileEntry) == (2 * sizeof(void *)) + 8);
if (sizeof(void *) == 4) {
lshiftPtr(Imm32(4), temp);
} else {
lshiftPtr(Imm32(3), temp);
mulBy3(temp, temp);
}
push(temp);
loadPtr(AbsoluteAddress(p->addressOfStack()), temp);
addPtr(Address(StackPointer, 0), temp);
@ -1059,14 +1069,14 @@ class MacroAssembler : public MacroAssemblerSpecific
void spsUpdatePCIdx(SPSProfiler *p, int32_t idx, Register temp) {
Label stackFull;
spsProfileEntryAddress(p, -1, temp, &stackFull);
store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfPCIdx()));
store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfLineOrPc()));
bind(&stackFull);
}
void spsUpdatePCIdx(SPSProfiler *p, Register idx, Register temp) {
Label stackFull;
spsProfileEntryAddressSafe(p, -1, temp, &stackFull);
store32(idx, Address(temp, ProfileEntry::offsetOfPCIdx()));
store32(idx, Address(temp, ProfileEntry::offsetOfLineOrPc()));
bind(&stackFull);
}
@ -1075,11 +1085,10 @@ class MacroAssembler : public MacroAssemblerSpecific
Label stackFull;
spsProfileEntryAddress(p, 0, temp, &stackFull);
storePtr(ImmPtr(str), Address(temp, ProfileEntry::offsetOfString()));
storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfScript()));
storePtr(ImmPtr((void*) ProfileEntry::SCRIPT_OPT_STACKPOINTER),
Address(temp, ProfileEntry::offsetOfStackAddress()));
store32(Imm32(ProfileEntry::NullPCIndex), Address(temp, ProfileEntry::offsetOfPCIdx()));
storePtr(ImmPtr(str), Address(temp, ProfileEntry::offsetOfLabel()));
storePtr(ImmGCPtr(s), Address(temp, ProfileEntry::offsetOfSpOrScript()));
store32(Imm32(ProfileEntry::NullPCOffset), Address(temp, ProfileEntry::offsetOfLineOrPc()));
store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
/* Always increment the stack size, whether or not we actually pushed. */
bind(&stackFull);
@ -1095,17 +1104,16 @@ class MacroAssembler : public MacroAssemblerSpecific
spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
loadPtr(str, temp2);
storePtr(temp2, Address(temp, ProfileEntry::offsetOfString()));
storePtr(temp2, Address(temp, ProfileEntry::offsetOfLabel()));
loadPtr(script, temp2);
storePtr(temp2, Address(temp, ProfileEntry::offsetOfScript()));
storePtr(ImmPtr(nullptr), Address(temp, ProfileEntry::offsetOfStackAddress()));
storePtr(temp2, Address(temp, ProfileEntry::offsetOfSpOrScript()));
// Store 0 for PCIdx because that's what interpreter does.
// (See probes::EnterScript, which calls spsProfiler.enter, which pushes an entry
// with 0 pcIdx).
store32(Imm32(0), Address(temp, ProfileEntry::offsetOfPCIdx()));
store32(Imm32(0), Address(temp, ProfileEntry::offsetOfLineOrPc()));
store32(Imm32(0), Address(temp, ProfileEntry::offsetOfFlags()));
/* Always increment the stack size, whether or not we actually pushed. */
bind(&stackFull);

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

@ -1427,6 +1427,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void addPtr(ImmPtr imm, const Register dest) {
addPtr(ImmWord(uintptr_t(imm.value)), dest);
}
void mulBy3(const Register &src, const Register &dest) {
as_add(dest, src, lsl(src, 1));
}
void setStackArg(Register reg, uint32_t arg);

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

@ -572,6 +572,9 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
void subPtr(Register src, const Address &dest) {
subq(src, Operand(dest));
}
void mulBy3(const Register &src, const Register &dest) {
lea(Operand(src, src, TimesTwo), dest);
}
void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label *label) {
if (JSC::X86Assembler::isAddressImmediate(lhs.addr)) {

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

@ -581,6 +581,9 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
void subPtr(Register src, const Address &dest) {
subl(src, Operand(dest));
}
void mulBy3(const Register &src, const Register &dest) {
lea(Operand(src, src, TimesTwo), dest);
}
void branch32(Condition cond, AbsoluteAddress lhs, Imm32 rhs, Label *label) {
cmpl(Operand(lhs), rhs);

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

@ -162,11 +162,11 @@ SPSProfiler::enter(JSScript *script, JSFunction *maybeFun)
if (*size_ > 0 && *size_ - 1 < max_) {
size_t start = (*size_ > 4) ? *size_ - 4 : 0;
for (size_t i = start; i < *size_ - 1; i++)
MOZ_ASSERT_IF(stack_[i].js(), stack_[i].pc() != nullptr);
MOZ_ASSERT_IF(stack_[i].isJs(), stack_[i].pc() != nullptr);
}
#endif
push(str, nullptr, script, script->code());
push(str, nullptr, script, script->code(), /* copy = */ true);
return true;
}
@ -183,18 +183,18 @@ SPSProfiler::exit(JSScript *script, JSFunction *maybeFun)
JS_ASSERT(str != nullptr);
// Bug 822041
if (!stack_[*size_].js()) {
if (!stack_[*size_].isJs()) {
fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
fprintf(stderr, " stack=%p size=%d/%d\n", (void*) stack_, *size_, max_);
for (int32_t i = *size_; i >= 0; i--) {
if (stack_[i].js())
if (stack_[i].isJs())
fprintf(stderr, " [%d] JS %s\n", i, stack_[i].label());
else
fprintf(stderr, " [%d] C line %d %s\n", i, stack_[i].line(), stack_[i].label());
}
}
JS_ASSERT(stack_[*size_].js());
JS_ASSERT(stack_[*size_].isJs());
JS_ASSERT(stack_[*size_].script() == script);
JS_ASSERT(strcmp((const char*) stack_[*size_].label(), str) == 0);
stack_[*size_].setLabel(nullptr);
@ -214,16 +214,17 @@ SPSProfiler::enterNative(const char *string, void *sp)
JS_ASSERT(enabled());
if (current < max_) {
stack[current].setLabel(string);
stack[current].setStackAddress(sp);
stack[current].setScript(nullptr);
stack[current].setLine(0);
stack[current].setCppFrame(sp, 0);
}
*size = current + 1;
}
void
SPSProfiler::push(const char *string, void *sp, JSScript *script, jsbytecode *pc)
SPSProfiler::push(const char *string, void *sp, JSScript *script, jsbytecode *pc, bool copy)
{
JS_ASSERT_IF(sp != nullptr, script == nullptr && pc == nullptr);
JS_ASSERT_IF(sp == nullptr, script != nullptr && pc != nullptr);
/* these operations cannot be re-ordered, so volatile-ize operations */
volatile ProfileEntry *stack = stack_;
volatile uint32_t *size = size_;
@ -231,10 +232,19 @@ SPSProfiler::push(const char *string, void *sp, JSScript *script, jsbytecode *pc
JS_ASSERT(installed());
if (current < max_) {
stack[current].setLabel(string);
stack[current].setStackAddress(sp);
stack[current].setScript(script);
stack[current].setPC(pc);
volatile ProfileEntry &entry = stack[current];
entry.setLabel(string);
if (sp != nullptr)
entry.setCppFrame(sp, 0);
else
entry.setJsFrame(script, pc);
// Track if mLabel needs a copy.
if (copy)
entry.setFlag(js::ProfileEntry::FRAME_LABEL_COPY);
else
entry.unsetFlag(js::ProfileEntry::FRAME_LABEL_COPY);
}
*size = current + 1;
}
@ -313,9 +323,8 @@ SPSEntryMarker::SPSEntryMarker(JSRuntime *rt
return;
}
size_before = *profiler->size_;
profiler->pushNoCopy("js::RunScript", this, nullptr, nullptr);
profiler->push("js::RunScript", this, nullptr, nullptr, /* copy = */ false);
}
SPSEntryMarker::~SPSEntryMarker()
{
if (profiler != nullptr) {
@ -327,13 +336,15 @@ SPSEntryMarker::~SPSEntryMarker()
JS_FRIEND_API(jsbytecode*)
ProfileEntry::pc() const volatile
{
return idx == NullPCIndex ? nullptr : script()->offsetToPC(idx);
MOZ_ASSERT(isJs());
return lineOrPc == NullPCOffset ? nullptr : script()->offsetToPC(lineOrPc);
}
JS_FRIEND_API(void)
ProfileEntry::setPC(jsbytecode *pc) volatile
{
idx = pc == nullptr ? NullPCIndex : script()->pcToOffset(pc);
MOZ_ASSERT(isJs());
lineOrPc = pc == nullptr ? NullPCOffset : script()->pcToOffset(pc);
}
JS_FRIEND_API(void)

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

@ -128,13 +128,7 @@ class SPSProfiler
void (*eventMarker_)(const char *);
const char *allocProfileString(JSScript *script, JSFunction *function);
void push(const char *string, void *sp, JSScript *script, jsbytecode *pc);
void pushNoCopy(const char *string, void *sp,
JSScript *script, jsbytecode *pc) {
push(string, reinterpret_cast<void*>(
reinterpret_cast<uintptr_t>(sp) | ProfileEntry::NoCopyBit),
script, pc);
}
void push(const char *string, void *sp, JSScript *script, jsbytecode *pc, bool copy);
void pop();
public:
@ -437,7 +431,7 @@ class SPSInstrumentation
return;
if (!inlinedFunction)
masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCIndex, scratch);
masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCOffset, scratch);
setPushed(script);
}
@ -478,8 +472,8 @@ class SPSInstrumentation
if (frame->skipNext) {
frame->skipNext = false;
} else {
if (!inlinedFunction)
masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCIndex, scratch);
if (!inlinedFunction)
masm.spsUpdatePCIdx(profiler_, ProfileEntry::NullPCOffset, scratch);
}
}

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

@ -97,7 +97,7 @@ void genProfileEntry(/*MODIFIED*/UnwinderThreadBuffer* utb,
// Cast to *((void**) to pass the text data to a void*
utb__addEntry( utb, ProfileEntry('d', *((void**)(&text[0]))) );
}
if (entry.js()) {
if (entry.isJs()) {
if (!entry.pc()) {
// The JIT only allows the top-most entry to have a nullptr pc
MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
@ -227,7 +227,7 @@ void populateBuffer(UnwinderThreadBuffer* utb, TickSample* sample,
MOZ_CRASH();
}
if (recordSample) {
if (recordSample) {
// add a "flush now" hint
utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'F'/*flush*/) );
}

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

@ -85,26 +85,6 @@ static inline uint32_t sMin(uint32_t l, uint32_t r) {
// of the two representations are consistent.
class StackEntry : public js::ProfileEntry
{
public:
bool isCopyLabel() const volatile {
return !((uintptr_t)stackAddress() & 0x1);
}
void setStackAddressCopy(void *sparg, bool copy) volatile {
// Tagged pointer. Less significant bit used to track if mLabel needs a
// copy. Note that we don't need the last bit of the stack address for
// proper ordering. This is optimized for encoding within the JS engine's
// instrumentation, so we do the extra work here of encoding a bit.
// Last bit 1 = Don't copy, Last bit 0 = Copy.
if (copy) {
setStackAddress(reinterpret_cast<void*>(
reinterpret_cast<uintptr_t>(sparg) & ~NoCopyBit));
} else {
setStackAddress(reinterpret_cast<void*>(
reinterpret_cast<uintptr_t>(sparg) | NoCopyBit));
}
}
};
class ProfilerMarkerPayload;
@ -375,11 +355,18 @@ public:
return;
}
volatile StackEntry &entry = mStack[mStackPointer];
// Make sure we increment the pointer after the name has
// been written such that mStack is always consistent.
mStack[mStackPointer].setLabel(aName);
mStack[mStackPointer].setStackAddressCopy(aStackAddress, aCopy);
mStack[mStackPointer].setLine(line);
entry.setLabel(aName);
entry.setCppFrame(aStackAddress, line);
// Track if mLabel needs a copy.
if (aCopy)
entry.setFlag(js::ProfileEntry::FRAME_LABEL_COPY);
else
entry.unsetFlag(js::ProfileEntry::FRAME_LABEL_COPY);
// Prevent the optimizer from re-ordering these instructions
STORE_SEQUENCER();

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

@ -347,7 +347,7 @@ void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
// that will happen to the preceding tag
addDynamicTag(aProfile, 'c', sampleLabel);
if (entry.js()) {
if (entry.isJs()) {
if (!entry.pc()) {
// The JIT only allows the top-most entry to have a nullptr pc
MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
@ -367,7 +367,12 @@ void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
}
} else {
aProfile.addTag(ProfileEntry('c', sampleLabel));
lineno = entry.line();
// XXX: Bug 1010578. Don't assume a CPP entry and try to get the
// line for js entries as well.
if (entry.isCpp()) {
lineno = entry.line();
}
}
if (lineno != -1) {
aProfile.addTag(ProfileEntry('n', lineno));
@ -507,7 +512,7 @@ void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample
// The pseudostack grows towards higher indices, so we iterate
// backwards (from callee to caller).
volatile StackEntry &entry = pseudoStack->mStack[i - 1];
if (!entry.js() && 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
// the stack walk to stop before the block of saved registers;
// note that it yields nondecreasing stack pointers), then restore