зеркало из https://github.com/mozilla/gecko-dev.git
Add profiling of basic block hit counts for IonMonkey and expose to addons, bug 811349. r=pierron
This commit is contained in:
Родитель
e0bfa38e3b
Коммит
02f84ecb22
|
@ -1384,13 +1384,86 @@ CodeGenerator::visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool)
|
|||
return true;
|
||||
}
|
||||
|
||||
IonScriptCounts *
|
||||
CodeGenerator::maybeCreateScriptCounts()
|
||||
{
|
||||
// If scripts are being profiled, create a new IonScriptCounts and attach
|
||||
// it to the script. This must be done on the main thread.
|
||||
JSContext *cx = GetIonContext()->cx;
|
||||
if (!cx)
|
||||
return NULL;
|
||||
|
||||
IonScriptCounts *counts = NULL;
|
||||
|
||||
CompileInfo *outerInfo = &gen->info();
|
||||
RawScript script = outerInfo->script();
|
||||
|
||||
if (cx->runtime->profilingScripts && !script->hasScriptCounts) {
|
||||
if (!script->initScriptCounts(cx))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!script->hasScriptCounts)
|
||||
return NULL;
|
||||
|
||||
counts = js_new<IonScriptCounts>();
|
||||
if (!counts || !counts->init(graph.numBlocks())) {
|
||||
js_delete(counts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
script->addIonCounts(counts);
|
||||
|
||||
for (size_t i = 0; i < graph.numBlocks(); i++) {
|
||||
MBasicBlock *block = graph.getBlock(i)->mir();
|
||||
|
||||
// Find a PC offset in the outermost script to use. If this block is
|
||||
// from an inlined script, find a location in the outer script to
|
||||
// associate information about the inling with.
|
||||
MResumePoint *resume = block->entryResumePoint();
|
||||
while (resume->caller())
|
||||
resume = resume->caller();
|
||||
uint32 offset = resume->pc() - script->code;
|
||||
JS_ASSERT(offset < script->length);
|
||||
|
||||
if (!counts->block(i).init(block->id(), offset, block->numSuccessors()))
|
||||
return NULL;
|
||||
for (size_t j = 0; j < block->numSuccessors(); j++)
|
||||
counts->block(i).setSuccessor(j, block->getSuccessor(j)->id());
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::generateBody()
|
||||
{
|
||||
IonScriptCounts *counts = maybeCreateScriptCounts();
|
||||
|
||||
for (size_t i = 0; i < graph.numBlocks(); i++) {
|
||||
current = graph.getBlock(i);
|
||||
for (LInstructionIterator iter = current->begin(); iter != current->end(); iter++) {
|
||||
|
||||
LInstructionIterator iter = current->begin();
|
||||
|
||||
// Separately visit the label at the start of every block, so that
|
||||
// count instrumentation is inserted after the block label is bound.
|
||||
if (!iter->accept(this))
|
||||
return false;
|
||||
iter++;
|
||||
|
||||
mozilla::Maybe<Sprinter> printer;
|
||||
if (counts) {
|
||||
masm.inc64(AbsoluteAddress(counts->block(i).addressOfHitCount()));
|
||||
printer.construct(GetIonContext()->cx);
|
||||
if (!printer.ref().init())
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; iter != current->end(); iter++) {
|
||||
IonSpew(IonSpew_Codegen, "instruction %s", iter->opName());
|
||||
if (counts)
|
||||
printer.ref().printf("[%s]\n", iter->opName());
|
||||
|
||||
if (iter->safepoint() && pushedArgumentSlots_.length()) {
|
||||
if (!markArgumentSlots(iter->safepoint()))
|
||||
return false;
|
||||
|
@ -1401,6 +1474,9 @@ CodeGenerator::generateBody()
|
|||
}
|
||||
if (masm.oom())
|
||||
return false;
|
||||
|
||||
if (counts)
|
||||
counts->block(i).setCode(printer.ref().string());
|
||||
}
|
||||
|
||||
JS_ASSERT(pushedArgumentSlots_.empty());
|
||||
|
|
|
@ -219,6 +219,8 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
|
||||
ConstantOrRegister getSetPropertyValue(LInstruction *ins);
|
||||
bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
|
||||
|
||||
IonScriptCounts *maybeCreateScriptCounts();
|
||||
};
|
||||
|
||||
} // namespace ion
|
||||
|
|
|
@ -402,6 +402,141 @@ struct IonScript
|
|||
}
|
||||
};
|
||||
|
||||
// Execution information for a basic block which may persist after the
|
||||
// accompanying IonScript is destroyed, for use during profiling.
|
||||
struct IonBlockCounts
|
||||
{
|
||||
private:
|
||||
uint32 id_;
|
||||
|
||||
// Approximate bytecode in the outer (not inlined) script this block
|
||||
// was generated from.
|
||||
uint32 offset_;
|
||||
|
||||
// ids for successors of this block.
|
||||
uint32 numSuccessors_;
|
||||
uint32 *successors_;
|
||||
|
||||
// Hit count for this block.
|
||||
uint64 hitCount_;
|
||||
|
||||
// Information about the code generated for this block.
|
||||
char *code_;
|
||||
|
||||
public:
|
||||
|
||||
bool init(uint32 id, uint32 offset, uint32 numSuccessors) {
|
||||
id_ = id;
|
||||
offset_ = offset;
|
||||
numSuccessors_ = numSuccessors;
|
||||
if (numSuccessors) {
|
||||
successors_ = (uint32 *) js_calloc(numSuccessors * sizeof(uint32));
|
||||
if (!successors_)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
if (successors_)
|
||||
js_free(successors_);
|
||||
if (code_)
|
||||
js_free(code_);
|
||||
}
|
||||
|
||||
uint32 id() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
uint32 offset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
size_t numSuccessors() const {
|
||||
return numSuccessors_;
|
||||
}
|
||||
|
||||
void setSuccessor(size_t i, uint32 id) {
|
||||
JS_ASSERT(i < numSuccessors_);
|
||||
successors_[i] = id;
|
||||
}
|
||||
|
||||
uint32 successor(size_t i) const {
|
||||
JS_ASSERT(i < numSuccessors_);
|
||||
return successors_[i];
|
||||
}
|
||||
|
||||
uint64 *addressOfHitCount() {
|
||||
return &hitCount_;
|
||||
}
|
||||
|
||||
uint64 hitCount() const {
|
||||
return hitCount_;
|
||||
}
|
||||
|
||||
void setCode(const char *code) {
|
||||
char *ncode = (char *) js_malloc(strlen(code) + 1);
|
||||
if (ncode) {
|
||||
strcpy(ncode, code);
|
||||
code_ = ncode;
|
||||
}
|
||||
}
|
||||
|
||||
const char *code() const {
|
||||
return code_;
|
||||
}
|
||||
};
|
||||
|
||||
// Execution information for a compiled script which may persist after the
|
||||
// IonScript is destroyed, for use during profiling.
|
||||
struct IonScriptCounts
|
||||
{
|
||||
private:
|
||||
// Any previous invalidated compilation(s) for the script.
|
||||
IonScriptCounts *previous_;
|
||||
|
||||
// Information about basic blocks in this script.
|
||||
size_t numBlocks_;
|
||||
IonBlockCounts *blocks_;
|
||||
|
||||
public:
|
||||
|
||||
IonScriptCounts() {
|
||||
PodZero(this);
|
||||
}
|
||||
|
||||
~IonScriptCounts() {
|
||||
for (size_t i = 0; i < numBlocks_; i++)
|
||||
blocks_[i].destroy();
|
||||
js_free(blocks_);
|
||||
if (previous_)
|
||||
js_delete(previous_);
|
||||
}
|
||||
|
||||
bool init(size_t numBlocks) {
|
||||
numBlocks_ = numBlocks;
|
||||
blocks_ = (IonBlockCounts *) js_calloc(numBlocks * sizeof(IonBlockCounts));
|
||||
return blocks_ != NULL;
|
||||
}
|
||||
|
||||
size_t numBlocks() const {
|
||||
return numBlocks_;
|
||||
}
|
||||
|
||||
IonBlockCounts &block(size_t i) {
|
||||
JS_ASSERT(i < numBlocks_);
|
||||
return blocks_[i];
|
||||
}
|
||||
|
||||
void setPrevious(IonScriptCounts *previous) {
|
||||
previous_ = previous;
|
||||
}
|
||||
|
||||
IonScriptCounts *previous() const {
|
||||
return previous_;
|
||||
}
|
||||
};
|
||||
|
||||
struct VMFunction;
|
||||
|
||||
class IonCompartment;
|
||||
|
|
|
@ -65,6 +65,12 @@ MacroAssemblerARM::branchTruncateDouble(const FloatRegister &src, const Register
|
|||
ma_b(fail, Assembler::Equal);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARM::inc64(AbsoluteAddress dest)
|
||||
{
|
||||
JS_NOT_REACHED("NYI");
|
||||
}
|
||||
|
||||
bool
|
||||
MacroAssemblerARM::alu_dbl(Register src1, Imm32 imm, Register dest, ALUOp op,
|
||||
SetCond_ sc, Condition c)
|
||||
|
|
|
@ -35,6 +35,8 @@ class MacroAssemblerARM : public Assembler
|
|||
void convertDoubleToFloat(const FloatRegister &src, const FloatRegister &dest);
|
||||
void branchTruncateDouble(const FloatRegister &src, const Register &dest, Label *fail);
|
||||
|
||||
void inc64(AbsoluteAddress dest);
|
||||
|
||||
// somewhat direct wrappers for the low-level assembler funcitons
|
||||
// bitops
|
||||
// attempt to encode a virtual alu instruction using
|
||||
|
|
|
@ -107,6 +107,10 @@ struct AbsoluteAddress {
|
|||
explicit AbsoluteAddress(void *addr)
|
||||
: addr(addr)
|
||||
{ }
|
||||
|
||||
AbsoluteAddress offset(ptrdiff_t delta) {
|
||||
return AbsoluteAddress(((uint8 *) addr) + delta);
|
||||
}
|
||||
};
|
||||
|
||||
// Specifies an address computed in the form of a register base and a constant,
|
||||
|
|
|
@ -826,6 +826,11 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
|||
cvtsq2sd(src, dest);
|
||||
}
|
||||
|
||||
void inc64(AbsoluteAddress dest) {
|
||||
mov(ImmWord(dest.addr), ScratchReg);
|
||||
addPtr(Imm32(1), Address(ScratchReg, 0));
|
||||
}
|
||||
|
||||
// Setup a call to C/C++ code, given the number of general arguments it
|
||||
// takes. Note that this only supports cdecl.
|
||||
//
|
||||
|
|
|
@ -683,6 +683,14 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
|||
addsd(Operand(&NegativeOne), dest);
|
||||
}
|
||||
|
||||
void inc64(AbsoluteAddress dest) {
|
||||
addl(Imm32(1), Operand(dest));
|
||||
Label noOverflow;
|
||||
j(NonZero, &noOverflow);
|
||||
addl(Imm32(1), Operand(dest.offset(4)));
|
||||
bind(&noOverflow);
|
||||
}
|
||||
|
||||
// Setup a call to C/C++ code, given the number of general arguments it
|
||||
// takes. Note that this only supports cdecl.
|
||||
//
|
||||
|
|
|
@ -318,6 +318,21 @@ js_DumpPCCounts(JSContext *cx, HandleScript script, js::Sprinter *sp)
|
|||
|
||||
pc = next;
|
||||
}
|
||||
|
||||
ion::IonScriptCounts *ionCounts = script->getIonCounts();
|
||||
|
||||
while (ionCounts) {
|
||||
Sprint(sp, "IonScript [%lu blocks]:\n", ionCounts->numBlocks());
|
||||
for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
|
||||
const ion::IonBlockCounts &block = ionCounts->block(i);
|
||||
Sprint(sp, "BB #%lu [%05u]", block.id(), block.offset());
|
||||
for (size_t j = 0; j < block.numSuccessors(); j++)
|
||||
Sprint(sp, " -> #%lu", block.successor(j));
|
||||
Sprint(sp, " :: %llu hits\n", block.hitCount());
|
||||
Sprint(sp, "%s\n", block.code());
|
||||
}
|
||||
ionCounts = ionCounts->previous();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -6702,6 +6717,18 @@ GetPCCountScriptSummary(JSContext *cx, size_t index)
|
|||
AppendArrayJSONProperties(cx, buf, arithTotals, countArithNames,
|
||||
JS_ARRAY_LENGTH(arithTotals), comma);
|
||||
|
||||
uint64_t ionActivity = 0;
|
||||
ion::IonScriptCounts *ionCounts = sac.getIonCounts();
|
||||
while (ionCounts) {
|
||||
for (size_t i = 0; i < ionCounts->numBlocks(); i++)
|
||||
ionActivity += ionCounts->block(i).hitCount();
|
||||
ionCounts = ionCounts->previous();
|
||||
}
|
||||
if (ionActivity) {
|
||||
AppendJSONProperty(buf, "ion", comma);
|
||||
NumberValueToStringBuffer(cx, DoubleValue(ionActivity), buf);
|
||||
}
|
||||
|
||||
buf.append('}');
|
||||
buf.append('}');
|
||||
|
||||
|
@ -6838,6 +6865,54 @@ GetPCCountJSON(JSContext *cx, const ScriptAndCounts &sac, StringBuffer &buf)
|
|||
}
|
||||
|
||||
buf.append(']');
|
||||
|
||||
ion::IonScriptCounts *ionCounts = sac.getIonCounts();
|
||||
if (ionCounts) {
|
||||
AppendJSONProperty(buf, "ion");
|
||||
buf.append('[');
|
||||
bool comma = false;
|
||||
while (ionCounts) {
|
||||
if (comma)
|
||||
buf.append(',');
|
||||
comma = true;
|
||||
|
||||
buf.append('[');
|
||||
for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
|
||||
if (i)
|
||||
buf.append(',');
|
||||
const ion::IonBlockCounts &block = ionCounts->block(i);
|
||||
|
||||
buf.append('{');
|
||||
AppendJSONProperty(buf, "id", NO_COMMA);
|
||||
NumberValueToStringBuffer(cx, Int32Value(block.id()), buf);
|
||||
AppendJSONProperty(buf, "offset");
|
||||
NumberValueToStringBuffer(cx, Int32Value(block.offset()), buf);
|
||||
AppendJSONProperty(buf, "successors");
|
||||
buf.append('[');
|
||||
for (size_t j = 0; j < block.numSuccessors(); j++) {
|
||||
if (j)
|
||||
buf.append(',');
|
||||
NumberValueToStringBuffer(cx, Int32Value(block.successor(j)), buf);
|
||||
}
|
||||
buf.append(']');
|
||||
AppendJSONProperty(buf, "hits");
|
||||
NumberValueToStringBuffer(cx, DoubleValue(block.hitCount()), buf);
|
||||
|
||||
AppendJSONProperty(buf, "code");
|
||||
JSString *str = JS_NewStringCopyZ(cx, block.code());
|
||||
if (!str || !(str = JS_ValueToSource(cx, StringValue(str))))
|
||||
return false;
|
||||
buf.append(str);
|
||||
|
||||
buf.append('}');
|
||||
}
|
||||
buf.append(']');
|
||||
|
||||
ionCounts = ionCounts->previous();
|
||||
}
|
||||
buf.append(']');
|
||||
}
|
||||
|
||||
buf.append('}');
|
||||
|
||||
return !cx->isExceptionPending();
|
||||
|
|
|
@ -842,27 +842,44 @@ JSScript::initScriptCounts(JSContext *cx)
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript *script)
|
||||
{
|
||||
JS_ASSERT(script->hasScriptCounts);
|
||||
ScriptCountsMap *map = script->compartment()->scriptCountsMap;
|
||||
ScriptCountsMap::Ptr p = map->lookup(script);
|
||||
JS_ASSERT(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
js::PCCounts
|
||||
JSScript::getPCCounts(jsbytecode *pc) {
|
||||
JS_ASSERT(hasScriptCounts);
|
||||
JS_ASSERT(size_t(pc - code) < length);
|
||||
ScriptCountsMap *map = compartment()->scriptCountsMap;
|
||||
JS_ASSERT(map);
|
||||
ScriptCountsMap::Ptr p = map->lookup(this);
|
||||
JS_ASSERT(p);
|
||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
return p->value.pcCountsVector[pc - code];
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::addIonCounts(ion::IonScriptCounts *ionCounts)
|
||||
{
|
||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
if (p->value.ionCounts)
|
||||
ionCounts->setPrevious(p->value.ionCounts);
|
||||
p->value.ionCounts = ionCounts;
|
||||
}
|
||||
|
||||
ion::IonScriptCounts *
|
||||
JSScript::getIonCounts()
|
||||
{
|
||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
return p->value.ionCounts;
|
||||
}
|
||||
|
||||
ScriptCounts
|
||||
JSScript::releaseScriptCounts()
|
||||
{
|
||||
JS_ASSERT(hasScriptCounts);
|
||||
ScriptCountsMap *map = compartment()->scriptCountsMap;
|
||||
JS_ASSERT(map);
|
||||
ScriptCountsMap::Ptr p = map->lookup(this);
|
||||
JS_ASSERT(p);
|
||||
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
|
||||
ScriptCounts counts = p->value;
|
||||
map->remove(p);
|
||||
compartment()->scriptCountsMap->remove(p);
|
||||
hasScriptCounts = false;
|
||||
return counts;
|
||||
}
|
||||
|
@ -872,7 +889,7 @@ JSScript::destroyScriptCounts(FreeOp *fop)
|
|||
{
|
||||
if (hasScriptCounts) {
|
||||
ScriptCounts scriptCounts = releaseScriptCounts();
|
||||
fop->free_(scriptCounts.pcCountsVector);
|
||||
scriptCounts.destroy(fop);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace js {
|
|||
|
||||
namespace ion {
|
||||
struct IonScript;
|
||||
struct IonScriptCounts;
|
||||
}
|
||||
|
||||
# define ION_DISABLED_SCRIPT ((js::ion::IonScript *)0x1)
|
||||
|
@ -216,6 +217,7 @@ class ScriptCounts
|
|||
{
|
||||
friend struct ::JSScript;
|
||||
friend struct ScriptAndCounts;
|
||||
|
||||
/*
|
||||
* This points to a single block that holds an array of PCCounts followed
|
||||
* by an array of doubles. Each element in the PCCounts array has a
|
||||
|
@ -223,13 +225,17 @@ class ScriptCounts
|
|||
*/
|
||||
PCCounts *pcCountsVector;
|
||||
|
||||
/* Information about any Ion compilations for the script. */
|
||||
ion::IonScriptCounts *ionCounts;
|
||||
|
||||
public:
|
||||
ScriptCounts() : pcCountsVector(NULL) { }
|
||||
ScriptCounts() : pcCountsVector(NULL), ionCounts(NULL) { }
|
||||
|
||||
inline void destroy(FreeOp *fop);
|
||||
|
||||
void set(js::ScriptCounts counts) {
|
||||
pcCountsVector = counts.pcCountsVector;
|
||||
ionCounts = counts.ionCounts;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -734,6 +740,8 @@ struct JSScript : public js::gc::Cell
|
|||
public:
|
||||
bool initScriptCounts(JSContext *cx);
|
||||
js::PCCounts getPCCounts(jsbytecode *pc);
|
||||
void addIonCounts(js::ion::IonScriptCounts *ionCounts);
|
||||
js::ion::IonScriptCounts *getIonCounts();
|
||||
js::ScriptCounts releaseScriptCounts();
|
||||
void destroyScriptCounts(js::FreeOp *fop);
|
||||
|
||||
|
@ -1243,6 +1251,10 @@ struct ScriptAndCounts
|
|||
JS_ASSERT(unsigned(pc - script->code) < script->length);
|
||||
return scriptCounts.pcCountsVector[pc - script->code];
|
||||
}
|
||||
|
||||
ion::IonScriptCounts *getIonCounts() const {
|
||||
return scriptCounts.ionCounts;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
|
|
@ -62,6 +62,7 @@ inline void
|
|||
ScriptCounts::destroy(FreeOp *fop)
|
||||
{
|
||||
fop->free_(pcCountsVector);
|
||||
fop->delete_(ionCounts);
|
||||
}
|
||||
|
||||
inline void
|
||||
|
|
Загрузка…
Ссылка в новой задаче