зеркало из https://github.com/mozilla/gecko-dev.git
Bug 778979 - Part 4: Provide an ip => pc translation mechanism for currently running JIT code. r=bhackett
This commit is contained in:
Родитель
0603f5b88e
Коммит
b4ee1d6c18
|
@ -922,6 +922,12 @@ EnableRuntimeProfilingStack(JSRuntime *rt, bool 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)
|
||||
SetDOMCallbacks(JSRuntime *rt, const DOMCallbacks *callbacks)
|
||||
{
|
||||
|
|
|
@ -614,6 +614,9 @@ SetRuntimeProfilingStack(JSRuntime *rt, ProfileEntry *stack, uint32_t *size,
|
|||
JS_FRIEND_API(void)
|
||||
EnableRuntimeProfilingStack(JSRuntime *rt, bool enabled);
|
||||
|
||||
JS_FRIEND_API(jsbytecode*)
|
||||
ProfilingGetPC(JSRuntime *rt, JSScript *script, void *ip);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_FRIEND_API(void *)
|
||||
GetOwnerThread(const JSContext *cx);
|
||||
|
|
|
@ -40,182 +40,48 @@ const char Probes::anonymousName[] = "(anonymous)";
|
|||
|
||||
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::JITGranularityRequested()
|
||||
Probes::JITGranularityRequested(JSContext *cx)
|
||||
{
|
||||
JITReportGranularity want = JITREPORT_GRANULARITY_NONE;
|
||||
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) {
|
||||
JITReportGranularity request = (*p)->granularityRequested();
|
||||
if (request > want)
|
||||
want = request;
|
||||
}
|
||||
|
||||
return want;
|
||||
if (cx->runtime->spsProfiler.enabled())
|
||||
return JITREPORT_GRANULARITY_LINE;
|
||||
return JITREPORT_GRANULARITY_NONE;
|
||||
}
|
||||
|
||||
#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
|
||||
Probes::JITWatcher::CollectNativeRegions(RegionVector ®ions,
|
||||
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,
|
||||
js::mjit::JSActiveFrame *outerFrame,
|
||||
js::mjit::JSActiveFrame **inlineFrames,
|
||||
void *mainCodeAddress, size_t mainCodeSize,
|
||||
void *stubCodeAddress, size_t stubCodeSize)
|
||||
js::mjit::JSActiveFrame **inlineFrames)
|
||||
{
|
||||
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
|
||||
(*p)->registerMJITCode(cx, chunk, outerFrame,
|
||||
inlineFrames,
|
||||
mainCodeAddress, mainCodeSize,
|
||||
stubCodeAddress, stubCodeSize);
|
||||
if (cx->runtime->spsProfiler.enabled() &&
|
||||
!cx->runtime->spsProfiler.registerMJITCode(chunk, outerFrame, inlineFrames))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Probes::discardMJITCode(FreeOp *fop, mjit::JITScript *jscr, mjit::JITChunk *chunk, void* address)
|
||||
{
|
||||
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
|
||||
(*p)->discardMJITCode(fop, jscr, chunk, address);
|
||||
if (fop->runtime()->spsProfiler.enabled())
|
||||
fop->runtime()->spsProfiler.discardMJITCode(jscr, chunk, address);
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
Probes::registerICCode(JSContext *cx,
|
||||
mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
|
||||
void *start, size_t size)
|
||||
{
|
||||
for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p)
|
||||
(*p)->registerICCode(cx, chunk, script, pc, start, size);
|
||||
if (cx->runtime->spsProfiler.enabled() &&
|
||||
!cx->runtime->spsProfiler.registerICCode(chunk, script, pc, start, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -223,8 +89,10 @@ Probes::registerICCode(JSContext *cx,
|
|||
void
|
||||
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;
|
||||
|
@ -276,8 +144,6 @@ Probes::shutdown()
|
|||
ok = false;
|
||||
#endif
|
||||
|
||||
Probes::removeAllJITWatchers(NULL);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
|
@ -197,88 +197,20 @@ enum JITReportGranularity {
|
|||
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 ®ions,
|
||||
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.
|
||||
*/
|
||||
JITReportGranularity
|
||||
JITGranularityRequested();
|
||||
JITGranularityRequested(JSContext *cx);
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
/*
|
||||
* New method JIT code has been created
|
||||
*/
|
||||
void
|
||||
bool
|
||||
registerMJITCode(JSContext *cx, js::mjit::JITChunk *chunk,
|
||||
mjit::JSActiveFrame *outerFrame,
|
||||
mjit::JSActiveFrame **inlineFrames,
|
||||
void *mainCodeAddress, size_t mainCodeSize,
|
||||
void *stubCodeAddress, size_t stubCodeSize);
|
||||
mjit::JSActiveFrame **inlineFrames);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
void
|
||||
bool
|
||||
registerICCode(JSContext *cx,
|
||||
mjit::JITChunk *chunk, JSScript *script, jsbytecode* pc,
|
||||
void *start, size_t size);
|
||||
|
@ -376,7 +308,7 @@ inline bool
|
|||
Probes::wantNativeAddressInfo(JSContext *cx)
|
||||
{
|
||||
return (cx->reportGranularity >= JITREPORT_GRANULARITY_FUNCTION &&
|
||||
JITGranularityRequested() >= JITREPORT_GRANULARITY_FUNCTION);
|
||||
JITGranularityRequested(cx) >= JITREPORT_GRANULARITY_FUNCTION);
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -178,6 +178,8 @@ class SPSInstrumentation {
|
|||
* reenter() will be a no-op.
|
||||
*/
|
||||
void skipNextReenter() {
|
||||
if (!enabled())
|
||||
return;
|
||||
JS_ASSERT(!frame->skipNext && frame->left == 0);
|
||||
frame->skipNext = true;
|
||||
}
|
||||
|
@ -188,6 +190,8 @@ class SPSInstrumentation {
|
|||
* that further instrumentation should actually be emitted.
|
||||
*/
|
||||
void setPushed() {
|
||||
if (!enabled())
|
||||
return;
|
||||
JS_ASSERT(!frame->pushed);
|
||||
frame->pushed = true;
|
||||
}
|
||||
|
|
|
@ -1777,11 +1777,17 @@ mjit::Compiler::finishThisUp()
|
|||
JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size());
|
||||
JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size());
|
||||
|
||||
Probes::registerMJITCode(cx, chunk,
|
||||
a,
|
||||
(JSActiveFrame**) inlineFrames.begin(),
|
||||
result, masm.size(),
|
||||
result + masm.size(), stubcc.size());
|
||||
a->mainCodeStart = size_t(result);
|
||||
a->mainCodeEnd = size_t(result + masm.size());
|
||||
a->stubCodeStart = a->mainCodeEnd;
|
||||
a->stubCodeEnd = a->mainCodeEnd + 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;
|
||||
|
||||
|
@ -2050,7 +2056,7 @@ mjit::Compiler::generateMethod()
|
|||
/* Track this sync code for the previous op. */
|
||||
size_t length = masm.size() - masm.distanceOf(start);
|
||||
uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code;
|
||||
pcLengths[offset].codeLength += length;
|
||||
pcLengths[offset].codeLengthAugment += length;
|
||||
}
|
||||
JS_ASSERT(frame.consistentRegisters(PC));
|
||||
}
|
||||
|
@ -2136,7 +2142,8 @@ mjit::Compiler::generateMethod()
|
|||
continue;
|
||||
}
|
||||
|
||||
Label codeStart = masm.label();
|
||||
Label inlineStart = masm.label();
|
||||
Label stubStart = stubcc.masm.label();
|
||||
bool countsUpdated = 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.
|
||||
*/
|
||||
if (script->hasScriptCounts && JOF_OPTYPE(op) == JOF_JUMP)
|
||||
updatePCCounts(PC, &codeStart, &countsUpdated);
|
||||
updatePCCounts(PC, &countsUpdated);
|
||||
|
||||
/**********************
|
||||
* BEGIN COMPILER OPS *
|
||||
|
@ -2203,7 +2210,7 @@ mjit::Compiler::generateMethod()
|
|||
|
||||
BEGIN_CASE(JSOP_RETURN)
|
||||
if (script->hasScriptCounts)
|
||||
updatePCCounts(PC, &codeStart, &countsUpdated);
|
||||
updatePCCounts(PC, &countsUpdated);
|
||||
emitReturn(frame.peek(-1));
|
||||
fallthrough = false;
|
||||
END_CASE(JSOP_RETURN)
|
||||
|
@ -2357,7 +2364,7 @@ mjit::Compiler::generateMethod()
|
|||
jsbytecode *target = NULL;
|
||||
if (fused != JSOP_NOP) {
|
||||
if (script->hasScriptCounts)
|
||||
updatePCCounts(PC, &codeStart, &countsUpdated);
|
||||
updatePCCounts(PC, &countsUpdated);
|
||||
target = next + GET_JUMP_OFFSET(next);
|
||||
fixDoubleTypes(target);
|
||||
}
|
||||
|
@ -2631,7 +2638,7 @@ mjit::Compiler::generateMethod()
|
|||
return status;
|
||||
if (script->hasScriptCounts) {
|
||||
/* 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).
|
||||
*/
|
||||
if (script->hasScriptCounts)
|
||||
updatePCCounts(PC, &codeStart, &countsUpdated);
|
||||
updatePCCounts(PC, &countsUpdated);
|
||||
#if defined JS_CPU_ARM /* Need to implement jump(BaseIndex) for ARM */
|
||||
frame.syncAndKillEverything();
|
||||
masm.move(ImmPtr(PC), Registers::ArgReg1);
|
||||
|
@ -2748,7 +2755,7 @@ mjit::Compiler::generateMethod()
|
|||
|
||||
BEGIN_CASE(JSOP_LOOKUPSWITCH)
|
||||
if (script->hasScriptCounts)
|
||||
updatePCCounts(PC, &codeStart, &countsUpdated);
|
||||
updatePCCounts(PC, &countsUpdated);
|
||||
frame.syncAndForgetEverything();
|
||||
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. */
|
||||
if (script->hasScriptCounts)
|
||||
updatePCCounts(PC, &codeStart, &countsUpdated);
|
||||
updatePCCounts(PC, &countsUpdated);
|
||||
jsbytecode *target = &PC[JSOP_MOREITER_LENGTH];
|
||||
JSOp next = JSOp(*target);
|
||||
JS_ASSERT(next == JSOP_IFNE);
|
||||
|
@ -3153,7 +3160,7 @@ mjit::Compiler::generateMethod()
|
|||
|
||||
BEGIN_CASE(JSOP_STOP)
|
||||
if (script->hasScriptCounts)
|
||||
updatePCCounts(PC, &codeStart, &countsUpdated);
|
||||
updatePCCounts(PC, &countsUpdated);
|
||||
emitReturn(NULL);
|
||||
goto done;
|
||||
END_CASE(JSOP_STOP)
|
||||
|
@ -3231,7 +3238,7 @@ mjit::Compiler::generateMethod()
|
|||
}
|
||||
|
||||
if (script->hasScriptCounts) {
|
||||
size_t length = masm.size() - masm.distanceOf(codeStart);
|
||||
size_t length = masm.size() - masm.distanceOf(inlineStart);
|
||||
bool typesUpdated = false;
|
||||
|
||||
/* Update information about the type of value pushed by arithmetic ops. */
|
||||
|
@ -3253,21 +3260,16 @@ mjit::Compiler::generateMethod()
|
|||
typesUpdated = true;
|
||||
}
|
||||
|
||||
if (countsUpdated || typesUpdated || length != 0) {
|
||||
if (!countsUpdated)
|
||||
updatePCCounts(lastPC, &codeStart, &countsUpdated);
|
||||
|
||||
if (pcLengths) {
|
||||
/* Fill in the amount of inline code generated for the op. */
|
||||
uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code;
|
||||
pcLengths[offset].codeLength += length;
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
if (!countsUpdated && (typesUpdated || length != 0))
|
||||
updatePCCounts(lastPC, &countsUpdated);
|
||||
}
|
||||
/* Update how much code we generated for this opcode */
|
||||
if (pcLengths) {
|
||||
size_t length = masm.size() - masm.distanceOf(inlineStart);
|
||||
size_t stubLength = stubcc.size() - stubcc.masm.distanceOf(stubStart);
|
||||
uint32_t offset = ssa.frameLength(a->inlineIndex) + lastPC - script->code;
|
||||
pcLengths[offset].inlineLength += length;
|
||||
pcLengths[offset].stubLength += stubLength;
|
||||
}
|
||||
|
||||
frame.assertValidRegisterState();
|
||||
|
@ -3281,10 +3283,12 @@ mjit::Compiler::generateMethod()
|
|||
#undef BEGIN_CASE
|
||||
|
||||
void
|
||||
mjit::Compiler::updatePCCounts(jsbytecode *pc, Label *start, bool *updated)
|
||||
mjit::Compiler::updatePCCounts(jsbytecode *pc, bool *updated)
|
||||
{
|
||||
JS_ASSERT(script->hasScriptCounts);
|
||||
|
||||
Label start = masm.label();
|
||||
|
||||
/*
|
||||
* Bump the METHODJIT count for the opcode, read the METHODJIT_CODE_LENGTH
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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 *codeLength = &pcLengths[offset].codeLength;
|
||||
masm.addCount(codeLength, code, reg);
|
||||
masm.addCount(&pcLengths[offset].inlineLength, code, reg);
|
||||
masm.addCount(&pcLengths[offset].codeLengthAugment, code, reg);
|
||||
|
||||
double *pics = &counts.get(PCCounts::BASE_METHODJIT_PICS);
|
||||
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. */
|
||||
masm.loadPtr(frame.addressOfTop(), reg);
|
||||
|
||||
/* The start label should reflect the code for the op, not instrumentation. */
|
||||
*start = masm.label();
|
||||
/* The count of code executed should not reflect instrumentation as well */
|
||||
pcLengths[offset].codeLengthAugment -= masm.size() - masm.distanceOf(start);
|
||||
|
||||
*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;
|
||||
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 pushActiveFrame(JSScript *script, uint32_t argc);
|
||||
void popActiveFrame();
|
||||
void updatePCCounts(jsbytecode *pc, Label *start, bool *updated);
|
||||
void updatePCCounts(jsbytecode *pc, bool *updated);
|
||||
void updatePCTypes(jsbytecode *pc, FrameEntry *fe);
|
||||
void updateArithCounts(jsbytecode *pc, FrameEntry *fe,
|
||||
JSValueType firstUseType, JSValueType secondUseType);
|
||||
|
|
|
@ -595,8 +595,13 @@ struct NativeMapEntry {
|
|||
|
||||
/* Per-op counts of performance metrics. */
|
||||
struct PCLengthEntry {
|
||||
double codeLength; /* amount of inline code generated */
|
||||
double picsLength; /* amount of PIC stub code generated */
|
||||
double inlineLength; /* amount of inline 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 "jsscript.h"
|
||||
|
||||
#include "methodjit/MethodJIT.h"
|
||||
#include "methodjit/Compiler.h"
|
||||
|
||||
#include "vm/SPSProfiler.h"
|
||||
#include "vm/StringBuffer.h"
|
||||
|
||||
|
@ -30,6 +33,12 @@ SPSProfiler::~SPSProfiler()
|
|||
for (ProfileStringMap::Enum e(strings); !e.empty(); e.popFront())
|
||||
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
|
||||
|
@ -196,6 +205,186 @@ SPSProfiler::allocProfileString(JSContext *cx, JSScript *script, JSFunction *may
|
|||
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)
|
||||
: profiler(&rt->spsProfiler)
|
||||
{
|
||||
|
|
|
@ -107,6 +107,15 @@ namespace js {
|
|||
|
||||
class ProfileEntry;
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
namespace mjit {
|
||||
struct JITChunk;
|
||||
struct JITScript;
|
||||
struct JSActiveFrame;
|
||||
struct PCLengthEntry;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef HashMap<JSScript*, const char*, DefaultHasher<JSScript*>, SystemAllocPolicy>
|
||||
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);
|
||||
const char *profileString(JSContext *cx, JSScript *script, JSFunction *maybeFun);
|
||||
void onScriptFinalized(JSScript *script);
|
||||
|
|
Загрузка…
Ссылка в новой задаче