Bug 1190454 part 3 - PCCounts: Collect throw/catch hit counts. r=bhackett

This commit is contained in:
Nicolas B. Pierron 2015-08-29 01:32:36 +02:00
parent 12c40c7036
commit b032b5d165
7 changed files with 160 additions and 27 deletions

View File

@ -808,7 +808,7 @@ BaselineCompiler::emitDebugTrap()
void
BaselineCompiler::emitCoverage(jsbytecode* pc)
{
PCCounts* counts = script->getPCCounts(pc);
PCCounts* counts = script->maybeGetPCCounts(pc);
if (!counts)
return;

View File

@ -693,6 +693,17 @@ HandleExceptionBaseline(JSContext* cx, const JitFrameIterator& frame, ResumeFrom
{
MOZ_ASSERT(frame.isBaselineJS());
bool frameOk = false;
RootedScript script(cx, frame.baselineFrame()->script());
if (script->hasScriptCounts()) {
PCCounts* counts = script->getThrowCounts(pc);
// If we failed to allocate, then skip the increment and continue to
// handle the exception.
if (counts)
counts->numExec()++;
}
// We may be propagating a forced return from the interrupt
// callback, which cannot easily force a return.
if (cx->isPropagatingForcedReturn()) {
@ -701,10 +712,7 @@ HandleExceptionBaseline(JSContext* cx, const JitFrameIterator& frame, ResumeFrom
return;
}
bool frameOk = false;
RootedScript script(cx, frame.baselineFrame()->script());
again:
again:
if (cx->isExceptionPending()) {
if (!cx->isClosingGenerator()) {
switch (Debugger::onExceptionUnwind(cx, frame.baselineFrame())) {
@ -733,8 +741,12 @@ again:
ScopeIter si(cx, frame.baselineFrame(), pc);
if (!ProcessTryNotesBaseline(cx, frame, si, rfe, &pc))
goto again;
if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME) {
// No need to increment the PCCounts number of execution here,
// as the interpreter increments any PCCounts if present.
MOZ_ASSERT_IF(script->hasScriptCounts(), script->maybeGetPCCounts(pc));
return;
}
}
frameOk = HandleClosingGeneratorReturn(cx, frame.baselineFrame(), frameOk);

View File

@ -170,7 +170,7 @@ js::DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp)
return;
Sprint(sp, " {");
PCCounts* counts = script->getPCCounts(pc);
PCCounts* counts = script->maybeGetPCCounts(pc);
double val = counts ? counts->numExec() : 0.0;
if (val)
Sprint(sp, "\"%s\": %.0f", PCCounts::numExecName, val);
@ -1746,7 +1746,7 @@ js::GetPCCountScriptSummary(JSContext* cx, size_t index)
jsbytecode* codeEnd = script->codeEnd();
for (jsbytecode* pc = script->code(); pc < codeEnd; pc = GetNextPc(pc)) {
const PCCounts* counts = sac.getPCCounts(pc);
const PCCounts* counts = sac.maybeGetPCCounts(pc);
if (!counts)
continue;
total += counts->numExec();
@ -1846,7 +1846,7 @@ GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, StringBuffer& buf)
buf.append(str);
}
const PCCounts* counts = sac.getPCCounts(pc);
const PCCounts* counts = sac.maybeGetPCCounts(pc);
AppendJSONProperty(buf, "counts");
buf.append('{');

View File

@ -1297,6 +1297,22 @@ JSScript::initScriptCounts(JSContext* cx)
}
}
// Mark catch/finally blocks as being jump targets.
if (hasTrynotes()) {
JSTryNote* tn = trynotes()->vector;
JSTryNote* tnlimit = tn + trynotes()->length;
for (; tn < tnlimit; tn++) {
jsbytecode* tryStart = mainEntry + tn->start;
jsbytecode* tryPc = tryStart - 1;
if (JSOp(*tryPc) != JSOP_TRY)
continue;
jsbytecode* tryTarget = tryStart + tn->length;
if (!jumpTargets.append(tryTarget))
return false;
}
}
// Sort all pc, and remove duplicates.
std::sort(jumpTargets.begin(), jumpTargets.end());
auto last = std::unique(jumpTargets.begin(), jumpTargets.end());
@ -1352,7 +1368,18 @@ static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript* script)
}
js::PCCounts*
ScriptCounts::getPCCounts(size_t offset) const {
ScriptCounts::maybeGetPCCounts(size_t offset) {
PCCounts searched = PCCounts(offset);
PCCounts* begin = pcCountsVector;
PCCounts* end = begin + pcCountsSize;
PCCounts* elem = std::lower_bound(begin, end, searched);
if (elem == end || elem->pcOffset() != offset)
return nullptr;
return elem;
}
const js::PCCounts*
ScriptCounts::maybeGetPCCounts(size_t offset) const {
PCCounts searched = PCCounts(offset);
PCCounts* begin = pcCountsVector;
PCCounts* end = begin + pcCountsSize;
@ -1363,10 +1390,45 @@ ScriptCounts::getPCCounts(size_t offset) const {
}
js::PCCounts*
JSScript::getPCCounts(jsbytecode* pc) {
ScriptCounts::maybeGetThrowCounts(size_t offset) const {
PCCounts searched = PCCounts(offset);
PCCounts* begin = throwCountsVector;
PCCounts* end = throwCountsVector + throwCountsSize;
PCCounts* elem = std::lower_bound(begin, end, searched);
if (elem == end || elem->pcOffset() != offset)
return nullptr;
return elem;
}
js::PCCounts*
ScriptCounts::getThrowCounts(size_t offset) {
PCCounts searched = PCCounts(offset);
PCCounts* begin = throwCountsVector;
PCCounts* end = throwCountsVector + throwCountsSize;
PCCounts* elem = std::lower_bound(begin, end, searched);
if (elem == end || elem->pcOffset() != offset) {
size_t index = elem - begin;
size_t numBytes = (1 + throwCountsSize) * sizeof(PCCounts);
PCCounts* vec = (PCCounts*) js_realloc(throwCountsVector, numBytes);
if (!vec)
return nullptr;
throwCountsVector = vec;
throwCountsSize += 1;
elem = throwCountsVector + index;
end = throwCountsVector + throwCountsSize;
std::copy_backward(elem, end - 1, end);
*elem = searched;
}
return elem;
}
js::PCCounts*
JSScript::maybeGetPCCounts(jsbytecode* pc) {
MOZ_ASSERT(containsPC(pc));
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
return p->value().getPCCounts(pcToOffset(pc));
return p->value().maybeGetPCCounts(pcToOffset(pc));
}
void
@ -1380,6 +1442,25 @@ JSScript::setIonScript(JSContext* maybecx, js::jit::IonScript* ionScript)
updateBaselineOrIonRaw(maybecx);
}
ScriptCounts&
JSScript::getScriptCounts()
{
ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
return p->value();
}
js::PCCounts*
JSScript::maybeGetThrowCounts(jsbytecode* pc) {
MOZ_ASSERT(containsPC(pc));
return getScriptCounts().maybeGetThrowCounts(pcToOffset(pc));
}
js::PCCounts*
JSScript::getThrowCounts(jsbytecode* pc) {
MOZ_ASSERT(containsPC(pc));
return getScriptCounts().getThrowCounts(pcToOffset(pc));
}
void
JSScript::addIonCounts(jit::IonScriptCounts* ionCounts)
{

View File

@ -453,26 +453,50 @@ class ScriptCounts
friend class ::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
* pointer into the array of doubles.
*/
// This sorted array is used to map an offset to the number of times a
// branch got visited.
PCCounts* pcCountsVector;
size_t pcCountsSize;
/* Information about any Ion compilations for the script. */
// This sorted vector is used to map an offset to the number of times an
// instruction throw.
PCCounts* throwCountsVector;
size_t throwCountsSize;
// Information about any Ion compilations for the script.
jit::IonScriptCounts* ionCounts;
public:
ScriptCounts() : pcCountsVector(nullptr), ionCounts(nullptr) { }
ScriptCounts()
: pcCountsVector(nullptr),
pcCountsSize(0),
throwCountsVector(nullptr),
throwCountsSize(0),
ionCounts(nullptr)
{ }
PCCounts* getPCCounts(size_t offset) const;
// Return the counter used to count the number of visits. Returns null if
// the element is not found.
PCCounts* maybeGetPCCounts(size_t offset);
const PCCounts* maybeGetPCCounts(size_t offset) const;
// Return the counter used to count the number of throws. Returns null if
// the element is not found.
PCCounts* maybeGetThrowCounts(size_t offset) const;
// Return the counter used to count the number of throws. Allocate it if
// none exists yet. Returns null if the allocation failed.
PCCounts* getThrowCounts(size_t offset);
inline void destroy(FreeOp* fop);
void set(js::ScriptCounts counts) {
pcCountsVector = counts.pcCountsVector;
pcCountsSize = counts.pcCountsSize;
throwCountsVector = counts.throwCountsVector;
throwCountsSize = counts.throwCountsSize;
ionCounts = counts.ionCounts;
}
};
@ -1612,9 +1636,12 @@ class JSScript : public js::gc::TenuredCell
public:
bool initScriptCounts(JSContext* cx);
js::PCCounts* getPCCounts(jsbytecode* pc);
js::PCCounts* maybeGetPCCounts(jsbytecode* pc);
js::PCCounts* maybeGetThrowCounts(jsbytecode* pc);
js::PCCounts* getThrowCounts(jsbytecode* pc);
void addIonCounts(js::jit::IonScriptCounts* ionCounts);
js::jit::IonScriptCounts* getIonCounts();
js::ScriptCounts& getScriptCounts();
js::ScriptCounts releaseScriptCounts();
void destroyScriptCounts(js::FreeOp* fop);
@ -2340,8 +2367,8 @@ struct ScriptAndCounts
JSScript* script;
ScriptCounts scriptCounts;
const PCCounts* getPCCounts(jsbytecode* pc) const {
return scriptCounts.getPCCounts(script->pcToOffset(pc));
const PCCounts* maybeGetPCCounts(jsbytecode* pc) const {
return scriptCounts.maybeGetPCCounts(script->pcToOffset(pc));
}
jit::IonScriptCounts* getIonCounts() const {

View File

@ -34,6 +34,7 @@ inline void
ScriptCounts::destroy(FreeOp* fop)
{
fop->free_(pcCountsVector);
fop->free_(throwCountsVector);
fop->delete_(ionCounts);
}

View File

@ -1451,6 +1451,14 @@ HandleError(JSContext* cx, InterpreterRegs& regs)
{
MOZ_ASSERT(regs.fp()->script()->containsPC(regs.pc));
if (regs.fp()->script()->hasScriptCounts()) {
PCCounts* counts = regs.fp()->script()->getThrowCounts(regs.pc);
// If we failed to allocate, then skip the increment and continue to
// handle the exception.
if (counts)
counts->numExec()++;
}
ScopeIter si(cx, regs.fp(), regs.pc);
bool ok = false;
@ -1478,15 +1486,19 @@ HandleError(JSContext* cx, InterpreterRegs& regs)
}
}
switch (ProcessTryNotes(cx, si, regs)) {
HandleErrorContinuation res = ProcessTryNotes(cx, si, regs);
switch (res) {
case SuccessfulReturnContinuation:
break;
case ErrorReturnContinuation:
goto again;
case CatchContinuation:
return CatchContinuation;
case FinallyContinuation:
return FinallyContinuation;
// No need to increment the PCCounts number of execution here, as
// the interpreter increments any PCCounts if present.
MOZ_ASSERT_IF(regs.fp()->script()->hasScriptCounts(),
regs.fp()->script()->maybeGetPCCounts(regs.pc));
return res;
}
ok = HandleClosingGeneratorReturn(cx, regs.fp(), ok);
@ -1968,7 +1980,7 @@ CASE(EnableInterruptsPseudoOpcode)
}
if (script->hasScriptCounts()) {
PCCounts* counts = script->getPCCounts(REGS.pc);
PCCounts* counts = script->maybeGetPCCounts(REGS.pc);
if (counts)
counts->numExec()++;
moreInterrupts = true;