Add profiling of basic block hit counts for IonMonkey and expose to addons, bug 811349. r=pierron

This commit is contained in:
Brian Hackett 2012-11-16 09:14:06 -08:00
Родитель e0bfa38e3b
Коммит 02f84ecb22
12 изменённых файлов: 357 добавлений и 14 удалений

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

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