Bug 778979 - Part 4: Provide an ip => pc translation mechanism for currently running JIT code. r=bhackett

This commit is contained in:
Alex Crichton 2012-08-06 11:48:33 -07:00
Родитель 0603f5b88e
Коммит b4ee1d6c18
10 изменённых файлов: 369 добавлений и 272 удалений

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

@ -922,6 +922,12 @@ EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled)
rt->spsProfiler.enable(enabled); rt->spsProfiler.enable(enabled);
} }
JS_FRIEND_API(jsbytecode*)
ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip)
{
return rt->spsProfiler.ipToPC(script, size_t(ip));
}
JS_FRIEND_API(void) JS_FRIEND_API(void)
SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks) SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks)
{ {

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

@ -614,6 +614,9 @@ SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
JS_FRIEND_API(void) JS_FRIEND_API(void)
EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled); EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);
JS_FRIEND_API(jsbytecode*)
ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip);
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
JS_FRIEND_API(void *) JS_FRIEND_API(void *)
GetOwnerThread(const JSContext *cx); GetOwnerThread(const JSContext *cx);

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

@ -40,182 +40,48 @@ const char Probes::anonymousName[] = "(anonymous)";
bool Probes::ProfilingActive = true; bool Probes::ProfilingActive = true;
static Vector<Probes::JITWatcher*, 4, SystemAllocPolicy> jitWatchers;
bool
Probes::addJITWatcher(JITWatcher *watcher)
{
return jitWatchers.append(watcher);
}
bool
Probes::removeJITWatcher(JSRuntime *rt, JITWatcher *watcher)
{
JITWatcher **place = Find(jitWatchers, watcher);
if (!place)
return false;
if (rt)
rt->delete_(*place);
else
Foreground::delete_(*place);
jitWatchers.erase(place);
return true;
}
void
Probes::removeAllJITWatchers(JSRuntime *rt)
{
if (rt) {
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
rt->delete_(*p);
} else {
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
Foreground::delete_(*p);
}
jitWatchers.clear();
}
Probes::JITReportGranularity Probes::JITReportGranularity
Probes::JITGranularityRequested() Probes::JITGranularityRequested(JSContext *cx)
{ {
JITReportGranularity want = JITREPORT_GRANULARITY_NONE; if (cx->runtime->spsProfiler.enabled())
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) { return JITREPORT_GRANULARITY_LINE;
JITReportGranularity request = (*p)->granularityRequested(); return JITREPORT_GRANULARITY_NONE;
if (request > want)
want = request;
}
return want;
} }
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
/*
* Flatten the tree of inlined frames into a series of native code regions, one
* for each contiguous section of native code that belongs to a single
* ActiveFrame. (Note that some of these regions may be zero-length, for
* example if two ActiveFrames end at the same place.)
*/
typedef mjit::Compiler::ActiveFrame ActiveFrame;
bool bool
Probes::JITWatcher::CollectNativeRegions(RegionVector &regions,
JSRuntime *rt,
mjit::JITChunk *jit,
mjit::JSActiveFrame *outerFrame,
mjit::JSActiveFrame **inlineFrames)
{
regions.resize(jit->nInlineFrames * 2 + 2);
mjit::JSActiveFrame **stack =
rt->array_new<mjit::JSActiveFrame*>(jit->nInlineFrames+2);
if (!stack)
return false;
uint32_t depth = 0;
uint32_t ip = 0;
stack[depth++] = NULL;
stack[depth++] = outerFrame;
regions[0].frame = outerFrame;
regions[0].script = outerFrame->script;
regions[0].pc = outerFrame->script->code;
regions[0].enter = true;
ip++;
for (uint32_t i = 0; i <= jit->nInlineFrames; i++) {
mjit::JSActiveFrame *frame = (i < jit->nInlineFrames) ? inlineFrames[i] : outerFrame;
// Not a down frame; pop the current frame, then pop until we reach
// this frame's parent, recording subframe ends as we go
while (stack[depth-1] != frame->parent) {
depth--;
JS_ASSERT(depth > 0);
// Pop up from regions[ip-1].frame to top of the stack: start a
// region in the destination frame and close off the source
// (origin) frame at the end of its script
mjit::JSActiveFrame *src = regions[ip-1].frame;
mjit::JSActiveFrame *dst = stack[depth-1];
JS_ASSERT_IF(!dst, i == jit->nInlineFrames);
regions[ip].frame = dst;
regions[ip].script = dst ? dst->script : NULL;
regions[ip].pc = src->parentPC + 1;
regions[ip-1].endpc = src->script->code + src->script->length;
regions[ip].enter = false;
ip++;
}
if (i < jit->nInlineFrames) {
// Push a frame (enter an inlined function). Start a region at the
// beginning of the new frame's script, and end the previous region
// at parentPC.
stack[depth++] = frame;
regions[ip].frame = frame;
regions[ip].script = frame->script;
regions[ip].pc = frame->script->code;
regions[ip-1].endpc = frame->parentPC;
regions[ip].enter = true;
ip++;
}
}
// Final region is always zero-length and not particularly useful
ip--;
regions.popBack();
mjit::JSActiveFrame *prev = NULL;
for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter) {
mjit::JSActiveFrame *frame = iter->frame;
if (iter->enter) {
// Pushing down a frame, so region starts at the beginning of the
// (destination) frame
iter->mainOffset = frame->mainCodeStart;
iter->stubOffset = frame->stubCodeStart;
} else {
// Popping up a level, so region starts at the end of the (source) frame
iter->mainOffset = prev->mainCodeEnd;
iter->stubOffset = prev->stubCodeEnd;
}
prev = frame;
}
JS_ASSERT(ip == 2 * jit->nInlineFrames + 1);
rt->array_delete(stack);
// All of the stub code comes immediately after the main code
for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter)
iter->stubOffset += outerFrame->mainCodeEnd;
return true;
}
void
Probes::registerMJITCode(JSContext *cx, js::mjit::JITChunk *chunk, Probes::registerMJITCode(JSContext *cx, js::mjit::JITChunk *chunk,
js::mjit::JSActiveFrame *outerFrame, js::mjit::JSActiveFrame *outerFrame,
js::mjit::JSActiveFrame **inlineFrames, js::mjit::JSActiveFrame **inlineFrames)
void *mainCodeAddress, size_t mainCodeSize,
void *stubCodeAddress, size_t stubCodeSize)
{ {
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) if (cx->runtime->spsProfiler.enabled() &&
(*p)->registerMJITCode(cx, chunk, outerFrame, !cx->runtime->spsProfiler.registerMJITCode(chunk, outerFrame, inlineFrames))
inlineFrames, {
mainCodeAddress, mainCodeSize, return false;
stubCodeAddress, stubCodeSize); }
return true;
} }
void void
Probes::discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, mjit::JITChunk *chunk, void* address) Probes::discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, mjit::JITChunk *chunk, void* address)
{ {
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) if (fop->runtime()->spsProfiler.enabled())
(*p)->discardMJITCode(fop, jscr, chunk, address); fop->runtime()->spsProfiler.discardMJITCode(jscr, chunk, address);
} }
void bool
Probes::registerICCode(JSContext *cx, Probes::registerICCode(JSContext *cx,
mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc, mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
void *start, size_t size) void *start, size_t size)
{ {
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) if (cx->runtime->spsProfiler.enabled() &&
(*p)->registerICCode(cx, chunk, script, pc, start, size); !cx->runtime->spsProfiler.registerICCode(chunk, script, pc, start, size))
{
return false;
}
return true;
} }
#endif #endif
@ -223,8 +89,10 @@ Probes::registerICCode(JSContext *cx,
void void
Probes::discardExecutableRegion(void *start, size_t size) Probes::discardExecutableRegion(void *start, size_t size)
{ {
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) /*
(*p)->discardExecutableRegion(start, size); * Not needed for SPS because ICs are disposed of when the normal JITChunk
* is disposed of
*/
} }
static JSRuntime *initRuntime; static JSRuntime *initRuntime;
@ -276,8 +144,6 @@ Probes::shutdown()
ok = false; ok = false;
#endif #endif
Probes::removeAllJITWatchers(NULL);
return ok; return ok;
} }

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

@ -197,88 +197,20 @@ enum JITReportGranularity {
JITREPORT_GRANULARITY_OP = 3 JITREPORT_GRANULARITY_OP = 3
}; };
/*
* Observer class for JIT code allocation/deallocation. Currently, this only
* handles the method JIT, and does not get notifications when JIT code is
* changed (patched) with no new allocation.
*/
class JITWatcher {
public:
struct NativeRegion {
mjit::JSActiveFrame *frame;
JSScript *script;
size_t inlinedOffset;
jsbytecode *pc;
jsbytecode *endpc;
uintptr_t mainOffset;
uintptr_t stubOffset;
bool enter;
};
typedef Vector<NativeRegion, 0, RuntimeAllocPolicy> RegionVector;
virtual JITReportGranularity granularityRequested() = 0;
#ifdef JS_METHODJIT
static bool CollectNativeRegions(RegionVector &regions,
JSRuntime *rt,
mjit::JITChunk *jit,
mjit::JSActiveFrame *outerFrame,
mjit::JSActiveFrame **inlineFrames);
virtual void registerMJITCode(JSContext *cx, js::mjit::JITChunk *chunk,
mjit::JSActiveFrame *outerFrame,
mjit::JSActiveFrame **inlineFrames,
void *mainCodeAddress, size_t mainCodeSize,
void *stubCodeAddress, size_t stubCodeSize) = 0;
virtual void discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, mjit::JITChunk *chunk,
void* address) = 0;
virtual void registerICCode(JSContext *cx,
js::mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
void *start, size_t size) = 0;
#endif
virtual void discardExecutableRegion(void *start, size_t size) = 0;
};
/*
* Register a JITWatcher subclass to be informed of JIT code
* allocation/deallocation.
*/
bool
addJITWatcher(JITWatcher *watcher);
/*
* Remove (and destroy) a registered JITWatcher. rt may be NULL. Returns false
* if the watcher is not found.
*/
bool
removeJITWatcher(JSRuntime *rt, JITWatcher *watcher);
/*
* Remove (and destroy) all registered JITWatchers. rt may be NULL.
*/
void
removeAllJITWatchers(JSRuntime *rt);
/* /*
* Finest granularity of JIT information desired by all watchers. * Finest granularity of JIT information desired by all watchers.
*/ */
JITReportGranularity JITReportGranularity
JITGranularityRequested(); JITGranularityRequested(JSContext *cx);
#ifdef JS_METHODJIT #ifdef JS_METHODJIT
/* /*
* New method JIT code has been created * New method JIT code has been created
*/ */
void bool
registerMJITCode(JSContext *cx, js::mjit::JITChunk *chunk, registerMJITCode(JSContext *cx, js::mjit::JITChunk *chunk,
mjit::JSActiveFrame *outerFrame, mjit::JSActiveFrame *outerFrame,
mjit::JSActiveFrame **inlineFrames, mjit::JSActiveFrame **inlineFrames);
void *mainCodeAddress, size_t mainCodeSize,
void *stubCodeAddress, size_t stubCodeSize);
/* /*
* Method JIT code is about to be discarded * Method JIT code is about to be discarded
@ -289,7 +221,7 @@ discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, mjit::JITChunk *chunk, void*
/* /*
* IC code has been allocated within the given JITChunk * IC code has been allocated within the given JITChunk
*/ */
void bool
registerICCode(JSContext *cx, registerICCode(JSContext *cx,
mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc, mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
void *start, size_t size); void *start, size_t size);
@ -376,7 +308,7 @@ inline bool
Probes::wantNativeAddressInfo(JSContext *cx) Probes::wantNativeAddressInfo(JSContext *cx)
{ {
return (cx->reportGranularity >= JITREPORT_GRANULARITY_FUNCTION && return (cx->reportGranularity >= JITREPORT_GRANULARITY_FUNCTION &&
JITGranularityRequested() >= JITREPORT_GRANULARITY_FUNCTION); JITGranularityRequested(cx) >= JITREPORT_GRANULARITY_FUNCTION);
} }
inline bool inline bool

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

@ -178,6 +178,8 @@ class SPSInstrumentation {
* reenter() will be a no-op. * reenter() will be a no-op.
*/ */
void skipNextReenter() { void skipNextReenter() {
if (!enabled())
return;
JS_ASSERT(!frame->skipNext && frame->left == 0); JS_ASSERT(!frame->skipNext && frame->left == 0);
frame->skipNext = true; frame->skipNext = true;
} }
@ -188,6 +190,8 @@ class SPSInstrumentation {
* that further instrumentation should actually be emitted. * that further instrumentation should actually be emitted.
*/ */
void setPushed() { void setPushed() {
if (!enabled())
return;
JS_ASSERT(!frame->pushed); JS_ASSERT(!frame->pushed);
frame->pushed = true; frame->pushed = true;
} }

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

@ -1777,11 +1777,17 @@ mjit::Compiler::finishThisUp()
JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size());
JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size());
Probes::registerMJITCode(cx, chunk, a->mainCodeStart = size_t(result);
a, a->mainCodeEnd = size_t(result + masm.size());
(JSActiveFrame**) inlineFrames.begin(), a->stubCodeStart = a->mainCodeEnd;
result, masm.size(), a->stubCodeEnd = a->mainCodeEnd + stubcc.size();
result + masm.size(), stubcc.size()); if (!Probes::registerMJITCode(cx, chunk,
a, (JSActiveFrame**) inlineFrames.begin())) {
execPool->release();
cx->free_(chunk);
js_ReportOutOfMemory(cx);
return Compile_Error;
}
outerChunkRef().chunk = chunk; outerChunkRef().chunk = chunk;
@ -2050,7 +2056,7 @@ mjit::Compiler::generateMethod()
/* Track this sync code for the previous op. */ /* Track this sync code for the previous op. */
size_t length = masm.size() - masm.distanceOf(start); size_t length = masm.size() - masm.distanceOf(start);
uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code; uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code;
pcLengths[offset].codeLength += length; pcLengths[offset].codeLengthAugment += length;
} }
JS_ASSERT(frame.consistentRegisters(PC)); JS_ASSERT(frame.consistentRegisters(PC));
} }
@ -2136,7 +2142,8 @@ mjit::Compiler::generateMethod()
continue; continue;
} }
Label codeStart = masm.label(); Label inlineStart = masm.label();
Label stubStart = stubcc.masm.label();
bool countsUpdated = false; bool countsUpdated = false;
bool arithUpdated = false; bool arithUpdated = false;
@ -2167,7 +2174,7 @@ mjit::Compiler::generateMethod()
* as we want to skip updating for ops we didn't generate any code for. * as we want to skip updating for ops we didn't generate any code for.
*/ */
if (script->hasScriptCounts && JOF_OPTYPE(op) == JOF_JUMP) if (script->hasScriptCounts && JOF_OPTYPE(op) == JOF_JUMP)
updatePCCounts(PC, &codeStart, &countsUpdated); updatePCCounts(PC, &countsUpdated);
/********************** /**********************
* BEGIN COMPILER OPS * * BEGIN COMPILER OPS *
@ -2203,7 +2210,7 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_RETURN) BEGIN_CASE(JSOP_RETURN)
if (script->hasScriptCounts) if (script->hasScriptCounts)
updatePCCounts(PC, &codeStart, &countsUpdated); updatePCCounts(PC, &countsUpdated);
emitReturn(frame.peek(-1)); emitReturn(frame.peek(-1));
fallthrough = false; fallthrough = false;
END_CASE(JSOP_RETURN) END_CASE(JSOP_RETURN)
@ -2357,7 +2364,7 @@ mjit::Compiler::generateMethod()
jsbytecode *target = NULL; jsbytecode *target = NULL;
if (fused != JSOP_NOP) { if (fused != JSOP_NOP) {
if (script->hasScriptCounts) if (script->hasScriptCounts)
updatePCCounts(PC, &codeStart, &countsUpdated); updatePCCounts(PC, &countsUpdated);
target = next + GET_JUMP_OFFSET(next); target = next + GET_JUMP_OFFSET(next);
fixDoubleTypes(target); fixDoubleTypes(target);
} }
@ -2631,7 +2638,7 @@ mjit::Compiler::generateMethod()
return status; return status;
if (script->hasScriptCounts) { if (script->hasScriptCounts) {
/* Code generated while inlining has been accounted for. */ /* Code generated while inlining has been accounted for. */
updatePCCounts(PC, &codeStart, &countsUpdated); countsUpdated = true;
} }
} }
@ -2728,7 +2735,7 @@ mjit::Compiler::generateMethod()
* switch statements (could be fixed). * switch statements (could be fixed).
*/ */
if (script->hasScriptCounts) if (script->hasScriptCounts)
updatePCCounts(PC, &codeStart, &countsUpdated); updatePCCounts(PC, &countsUpdated);
#if defined JS_CPU_ARM /* Need to implement jump(BaseIndex) for ARM */ #if defined JS_CPU_ARM /* Need to implement jump(BaseIndex) for ARM */
frame.syncAndKillEverything(); frame.syncAndKillEverything();
masm.move(ImmPtr(PC), Registers::ArgReg1); masm.move(ImmPtr(PC), Registers::ArgReg1);
@ -2748,7 +2755,7 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_LOOKUPSWITCH) BEGIN_CASE(JSOP_LOOKUPSWITCH)
if (script->hasScriptCounts) if (script->hasScriptCounts)
updatePCCounts(PC, &codeStart, &countsUpdated); updatePCCounts(PC, &countsUpdated);
frame.syncAndForgetEverything(); frame.syncAndForgetEverything();
masm.move(ImmPtr(PC), Registers::ArgReg1); masm.move(ImmPtr(PC), Registers::ArgReg1);
@ -2792,7 +2799,7 @@ mjit::Compiler::generateMethod()
{ {
/* At the byte level, this is always fused with IFNE or IFNEX. */ /* At the byte level, this is always fused with IFNE or IFNEX. */
if (script->hasScriptCounts) if (script->hasScriptCounts)
updatePCCounts(PC, &codeStart, &countsUpdated); updatePCCounts(PC, &countsUpdated);
jsbytecode *target = &PC[JSOP_MOREITER_LENGTH]; jsbytecode *target = &PC[JSOP_MOREITER_LENGTH];
JSOp next = JSOp(*target); JSOp next = JSOp(*target);
JS_ASSERT(next == JSOP_IFNE); JS_ASSERT(next == JSOP_IFNE);
@ -3153,7 +3160,7 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_STOP) BEGIN_CASE(JSOP_STOP)
if (script->hasScriptCounts) if (script->hasScriptCounts)
updatePCCounts(PC, &codeStart, &countsUpdated); updatePCCounts(PC, &countsUpdated);
emitReturn(NULL); emitReturn(NULL);
goto done; goto done;
END_CASE(JSOP_STOP) END_CASE(JSOP_STOP)
@ -3231,7 +3238,7 @@ mjit::Compiler::generateMethod()
} }
if (script->hasScriptCounts) { if (script->hasScriptCounts) {
size_t length = masm.size() - masm.distanceOf(codeStart); size_t length = masm.size() - masm.distanceOf(inlineStart);
bool typesUpdated = false; bool typesUpdated = false;
/* Update information about the type of value pushed by arithmetic ops. */ /* Update information about the type of value pushed by arithmetic ops. */
@ -3253,21 +3260,16 @@ mjit::Compiler::generateMethod()
typesUpdated = true; typesUpdated = true;
} }
if (countsUpdated || typesUpdated || length != 0) { if (!countsUpdated && (typesUpdated || length != 0))
if (!countsUpdated) updatePCCounts(lastPC, &countsUpdated);
updatePCCounts(lastPC, &codeStart, &countsUpdated); }
/* Update how much code we generated for this opcode */
if (pcLengths) { if (pcLengths) {
/* Fill in the amount of inline code generated for the op. */ size_t length = masm.size() - masm.distanceOf(inlineStart);
uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code; size_t stubLength = stubcc.size() - stubcc.masm.distanceOf(stubStart);
pcLengths[offset].codeLength += length; uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code;
} pcLengths[offset].inlineLength += length;
} pcLengths[offset].stubLength += stubLength;
} else if (pcLengths) {
/* Fill in the amount of inline code generated for the op. */
size_t length = masm.size() - masm.distanceOf(codeStart);
uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code;
pcLengths[offset].codeLength += length;
} }
frame.assertValidRegisterState(); frame.assertValidRegisterState();
@ -3281,10 +3283,12 @@ mjit::Compiler::generateMethod()
#undef BEGIN_CASE #undef BEGIN_CASE
void void
mjit::Compiler::updatePCCounts(jsbytecode *pc, Label *start, bool *updated) mjit::Compiler::updatePCCounts(jsbytecode *pc, bool *updated)
{ {
JS_ASSERT(script->hasScriptCounts); JS_ASSERT(script->hasScriptCounts);
Label start = masm.label();
/* /*
* Bump the METHODJIT count for the opcode, read the METHODJIT_CODE_LENGTH * Bump the METHODJIT count for the opcode, read the METHODJIT_CODE_LENGTH
* and METHODJIT_PICS_LENGTH counts, indicating the amounts of inline path * and METHODJIT_PICS_LENGTH counts, indicating the amounts of inline path
@ -3304,9 +3308,15 @@ mjit::Compiler::updatePCCounts(jsbytecode *pc, Label *start, bool *updated)
PCCounts counts = script->getPCCounts(pc); PCCounts counts = script->getPCCounts(pc);
/*
* The inlineLength represents the actual length of the opcode generated,
* but this includes the instrumentation as well as other possibly not
* useful bytes. This extra cruft is accumulated in codeLengthAugment and
* will be taken out accordingly.
*/
double *code = &counts.get(PCCounts::BASE_METHODJIT_CODE); double *code = &counts.get(PCCounts::BASE_METHODJIT_CODE);
double *codeLength = &pcLengths[offset].codeLength; masm.addCount(&pcLengths[offset].inlineLength, code, reg);
masm.addCount(codeLength, code, reg); masm.addCount(&pcLengths[offset].codeLengthAugment, code, reg);
double *pics = &counts.get(PCCounts::BASE_METHODJIT_PICS); double *pics = &counts.get(PCCounts::BASE_METHODJIT_PICS);
double *picsLength = &pcLengths[offset].picsLength; double *picsLength = &pcLengths[offset].picsLength;
@ -3318,8 +3328,9 @@ mjit::Compiler::updatePCCounts(jsbytecode *pc, Label *start, bool *updated)
/* Reload the base register's original value. */ /* Reload the base register's original value. */
masm.loadPtr(frame.addressOfTop(), reg); masm.loadPtr(frame.addressOfTop(), reg);
/* The start label should reflect the code for the op, not instrumentation. */ /* The count of code executed should not reflect instrumentation as well */
*start = masm.label(); pcLengths[offset].codeLengthAugment -= masm.size() - masm.distanceOf(start);
*updated = true; *updated = true;
} }
@ -7089,7 +7100,7 @@ mjit::Compiler::jumpAndRun(Jump j, jsbytecode *target, Jump *slow, bool *trampol
*/ */
uint32_t offset = ssa.frameLength(a->inlineIndex) + PC - script->code; uint32_t offset = ssa.frameLength(a->inlineIndex) + PC - script->code;
size_t length = stubcc.masm.size() - stubcc.masm.distanceOf(start); size_t length = stubcc.masm.size() - stubcc.masm.distanceOf(start);
pcLengths[offset].codeLength += length; pcLengths[offset].codeLengthAugment += length;
} }
} }

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

@ -528,7 +528,7 @@ private:
CompileStatus finishThisUp(); CompileStatus finishThisUp();
CompileStatus pushActiveFrame(JSScript *script, uint32_t argc); CompileStatus pushActiveFrame(JSScript *script, uint32_t argc);
void popActiveFrame(); void popActiveFrame();
void updatePCCounts(jsbytecode *pc, Label *start, bool *updated); void updatePCCounts(jsbytecode *pc, bool *updated);
void updatePCTypes(jsbytecode *pc, FrameEntry *fe); void updatePCTypes(jsbytecode *pc, FrameEntry *fe);
void updateArithCounts(jsbytecode *pc, FrameEntry *fe, void updateArithCounts(jsbytecode *pc, FrameEntry *fe,
JSValueType firstUseType, JSValueType secondUseType); JSValueType firstUseType, JSValueType secondUseType);

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

@ -595,8 +595,13 @@ struct NativeMapEntry {
/* Per-op counts of performance metrics. */ /* Per-op counts of performance metrics. */
struct PCLengthEntry { struct PCLengthEntry {
double codeLength; /* amount of inline code generated */ double inlineLength; /* amount of inline code generated */
double picsLength; /* amount of PIC stub code generated */ double picsLength; /* amount of PIC stub code generated */
double stubLength; /* amount of stubcc code generated */
double codeLengthAugment; /* augment to inlineLength to be added
at runtime, represents instrumentation
taken out or common stubcc accounted
for (instead of just adding inlineLength) */
}; };
/* /*

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

@ -8,6 +8,9 @@
#include "jsnum.h" #include "jsnum.h"
#include "jsscript.h" #include "jsscript.h"
#include "methodjit/MethodJIT.h"
#include "methodjit/Compiler.h"
#include "vm/SPSProfiler.h" #include "vm/SPSProfiler.h"
#include "vm/StringBuffer.h" #include "vm/StringBuffer.h"
@ -30,6 +33,12 @@ SPSProfiler::~SPSProfiler()
for (ProfileStringMap::Enum e(strings); !e.empty(); e.popFront()) for (ProfileStringMap::Enum e(strings); !e.empty(); e.popFront())
rt->array_delete(e.front().value); rt->array_delete(e.front().value);
} }
#ifdef JS_METHODJIT
if (jminfo.initialized()) {
for (JITInfoMap::Enum e(jminfo); !e.empty(); e.popFront())
rt->delete_(e.front().value);
}
#endif
} }
void void
@ -196,6 +205,186 @@ SPSProfiler::allocProfileString(JSContext *cx, JSScript *script, JSFunction *may
return cstr; return cstr;
} }
#ifdef JS_METHODJIT
typedef SPSProfiler::JMChunkInfo JMChunkInfo;
JMChunkInfo::JMChunkInfo(mjit::JSActiveFrame *frame,
mjit::PCLengthEntry *pcLengths,
mjit::JITChunk *chunk)
: mainStart(frame->mainCodeStart),
mainEnd(frame->mainCodeEnd),
stubStart(frame->stubCodeStart),
stubEnd(frame->stubCodeEnd),
pcLengths(pcLengths),
chunk(chunk)
{}
jsbytecode*
SPSProfiler::ipToPC(JSScript *script, size_t ip)
{
JS_ASSERT(jminfo.initialized());
JITInfoMap::Ptr ptr = jminfo.lookup(script);
if (!ptr)
return NULL;
JMScriptInfo *info = ptr->value;
/* First check if this ip is in any of the ICs compiled for the script */
for (int i = 0; i < info->ics.length(); i++) {
ICInfo &ic = info->ics[i];
if (ic.base <= ip && ip < ic.base + ic.size)
return ic.pc;
}
/* Otherwise if it's not in any of the chunks, then we can't find it */
for (int i = 0; i < info->chunks.length(); i++) {
jsbytecode *pc = info->chunks[i].convert(script, ip);
if (pc != NULL)
return pc;
}
return NULL;
}
jsbytecode*
JMChunkInfo::convert(JSScript *script, size_t ip)
{
if (mainStart <= ip && ip < mainEnd) {
size_t offset = 0;
uint32_t i;
for (i = 0; i < script->length - 1; i++) {
offset += (uint32_t) pcLengths[i].inlineLength;
if (mainStart + offset > ip)
break;
}
return &script->code[i];
} else if (stubStart <= ip && ip < stubEnd) {
size_t offset = 0;
uint32_t i;
for (i = 0; i < script->length - 1; i++) {
offset += (uint32_t) pcLengths[i].stubLength;
if (stubStart + offset > ip)
break;
}
return &script->code[i];
}
return NULL;
}
bool
SPSProfiler::registerMJITCode(mjit::JITChunk *chunk,
mjit::JSActiveFrame *outerFrame,
mjit::JSActiveFrame **inlineFrames)
{
if (!jminfo.initialized() && !jminfo.init(100))
return false;
JS_ASSERT(chunk->pcLengths != NULL);
JMChunkInfo *info = registerScript(outerFrame, chunk->pcLengths, chunk);
if (!info)
return false;
/*
* The pcLengths array has entries for both the outerFrame's script and also
* all of the inlineFrames' scripts. The layout is something like:
*
* [ outerFrame info ] [ inline frame 1 ] [ inline frame 2 ] ...
*
* This local pcLengths pointer tracks the position of each inline frame's
* pcLengths array. Each section of the array has length script->length for
* the corresponding script for that frame.
*/
mjit::PCLengthEntry *pcLengths = chunk->pcLengths + outerFrame->script->length;
for (int i = 0; i < chunk->nInlineFrames; i++) {
JMChunkInfo *child = registerScript(inlineFrames[i], pcLengths, chunk);
if (!child)
return false;
/*
* When JM tells us about new code, each inline ActiveFrame only has the
* start/end listed relative to the start of the main instruction
* streams. This is corrected here so the addresses listed on the
* JMChunkInfo structure are absolute and can be tested directly.
*/
child->mainStart += info->mainStart;
child->mainEnd += info->mainStart;
child->stubStart += info->stubStart;
child->stubEnd += info->stubStart;
pcLengths += inlineFrames[i]->script->length;
}
return true;
}
JMChunkInfo*
SPSProfiler::registerScript(mjit::JSActiveFrame *frame,
mjit::PCLengthEntry *entries,
mjit::JITChunk *chunk)
{
/*
* An inlined script could possibly be compiled elsewhere as not having been
* inlined, so each JSScript* must be associated with a list of chunks
* instead of just one. Also, our script may already be in the map.
*/
JITInfoMap::AddPtr ptr = jminfo.lookupForAdd(frame->script);
JMScriptInfo *info;
if (ptr) {
info = ptr->value;
JS_ASSERT(info->chunks.length() > 0);
} else {
info = rt->new_<JMScriptInfo>();
if (info == NULL || !jminfo.add(ptr, frame->script, info))
return NULL;
}
if (!info->chunks.append(JMChunkInfo(frame, entries, chunk)))
return NULL;
return info->chunks.end() - 1;
}
bool
SPSProfiler::registerICCode(mjit::JITChunk *chunk,
JSScript *script, jsbytecode *pc,
void *base, size_t size)
{
JS_ASSERT(jminfo.initialized());
JITInfoMap::Ptr ptr = jminfo.lookup(script);
JS_ASSERT(ptr);
return ptr->value->ics.append(ICInfo(base, size, pc));
}
void
SPSProfiler::discardMJITCode(mjit::JITScript *jscr,
mjit::JITChunk *chunk, void* address)
{
if (!jminfo.initialized())
return;
unregisterScript(jscr->script, chunk);
for (int i = 0; i < chunk->nInlineFrames; i++)
unregisterScript(chunk->inlineFrames()[i].fun->script(), chunk);
}
void
SPSProfiler::unregisterScript(JSScript *script, mjit::JITChunk *chunk)
{
JITInfoMap::Ptr ptr = jminfo.lookup(script);
if (!ptr)
return;
JMScriptInfo *info = ptr->value;
for (int i = 0; i < info->chunks.length(); i++) {
if (info->chunks[i].chunk == chunk) {
info->chunks.erase(&info->chunks[i]);
break;
}
}
if (info->chunks.length() == 0) {
jminfo.remove(ptr);
rt->delete_(info);
}
}
#endif
SPSEntryMarker::SPSEntryMarker(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT) SPSEntryMarker::SPSEntryMarker(JSRuntime *rt JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT)
: profiler(&rt->spsProfiler) : profiler(&rt->spsProfiler)
{ {

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

@ -107,6 +107,15 @@ namespace js {
class ProfileEntry; class ProfileEntry;
#ifdef JS_METHODJIT
namespace mjit {
struct JITChunk;
struct JITScript;
struct JSActiveFrame;
struct PCLengthEntry;
}
#endif
typedef HashMap<JSScript*, const char*, DefaultHasher<JSScript*>, SystemAllocPolicy> typedef HashMap<JSScript*, const char*, DefaultHasher<JSScript*>, SystemAllocPolicy>
ProfileStringMap; ProfileStringMap;
@ -162,6 +171,78 @@ class SPSProfiler
} }
} }
#ifdef JS_METHODJIT
struct ICInfo
{
size_t base;
size_t size;
jsbytecode *pc;
ICInfo(void *base, size_t size, jsbytecode *pc)
: base(size_t(base)), size(size), pc(pc)
{}
};
struct JMChunkInfo
{
size_t mainStart; // bounds for the inline code
size_t mainEnd;
size_t stubStart; // bounds of the ool code
size_t stubEnd;
mjit::PCLengthEntry *pcLengths; // pcLengths for this chunk
mjit::JITChunk *chunk; // stored to test when removing
JMChunkInfo(mjit::JSActiveFrame *frame,
mjit::PCLengthEntry *pcLengths,
mjit::JITChunk *chunk);
jsbytecode *convert(JSScript *script, size_t ip);
};
struct JMScriptInfo
{
Vector<ICInfo, 0, SystemAllocPolicy> ics;
Vector<JMChunkInfo, 1, SystemAllocPolicy> chunks;
};
typedef HashMap<JSScript*, JMScriptInfo*, DefaultHasher<JSScript*>,
SystemAllocPolicy> JITInfoMap;
/*
* This is the mapping which facilitates translation from an ip to a
* jsbytecode*. The mapping is from a JSScript* to a set of chunks and ics
* which are associated with the script. This way lookup/translation doesn't
* have to do something like iterate the entire map.
*
* Each IC is easy to test because they all have only one pc associated with
* them, and the range is easy to check. The main chunks of code are a bit
* harder because there are both the inline and out of line streams which
* need to be tested. Each of these streams is described by the pcLengths
* array stored within each chunk. This array describes the width of each
* opcode of the corresponding JSScript, and has the same number of entries
* as script->length.
*/
JITInfoMap jminfo;
bool registerMJITCode(mjit::JITChunk *chunk,
mjit::JSActiveFrame *outerFrame,
mjit::JSActiveFrame **inlineFrames);
void discardMJITCode(mjit::JITScript *jscr,
mjit::JITChunk *chunk, void* address);
bool registerICCode(mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
void *start, size_t size);
jsbytecode *ipToPC(JSScript *script, size_t ip);
private:
JMChunkInfo *registerScript(mjit::JSActiveFrame *frame,
mjit::PCLengthEntry *lenths,
mjit::JITChunk *chunk);
void unregisterScript(JSScript *script, mjit::JITChunk *chunk);
public:
#else
jsbytecode *ipToPC(JSScript *script, size_t ip) { return NULL; }
#endif
void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max); void setProfilingStack(ProfileEntry *stack, uint32_t *size, uint32_t max);
const char *profileString(JSContext *cx, JSScript *script, JSFunction *maybeFun); const char *profileString(JSContext *cx, JSScript *script, JSFunction *maybeFun);
void onScriptFinalized(JSScript *script); void onScriptFinalized(JSScript *script);