Bug 637393 - Add an option to count number of executions of each JSOp, broken down by "run mode" (method JIT/trace JIT/interpreter)

--HG--
extra : rebase_source : ba9a916197f8c2e449b3096f1c31fe2b154feef4
This commit is contained in:
Steve Fink 2011-05-10 11:26:39 -07:00
parent dbe059be22
commit 9aeaace01a
11 changed files with 136 additions and 129 deletions

View File

@ -88,6 +88,15 @@ extern JS_PUBLIC_DATA(jsval) JSVAL_VOID;
#endif
/*
* Different "run modes" used for execution accounting (JSOPTION_PCCOUNT)
*/
#define JSRUNMODE_INTERP 0
#define JSRUNMODE_TRACEJIT 1
#define JSRUNMODE_METHODJIT 2
#define JSRUNMODE_COUNT 3
/************************************************************************/
static JS_ALWAYS_INLINE JSBool
@ -981,11 +990,12 @@ JS_StringToVersion(const char *string);
#define JSOPTION_METHODJIT_ALWAYS \
JS_BIT(16) /* Always whole-method JIT,
don't tune at run-time. */
#define JSOPTION_PCCOUNT JS_BIT(17) /* Collect per-op execution counts */
/* Options which reflect compile-time properties of scripts. */
#define JSCOMPILEOPTION_MASK (JSOPTION_XML | JSOPTION_ANONFUNFIX)
#define JSRUNOPTION_MASK (JS_BITMASK(17) & ~JSCOMPILEOPTION_MASK)
#define JSRUNOPTION_MASK (JS_BITMASK(18) & ~JSCOMPILEOPTION_MASK)
#define JSALLOPTION_MASK (JSCOMPILEOPTION_MASK | JSRUNOPTION_MASK)
extern JS_PUBLIC_API(uint32)

View File

@ -2127,7 +2127,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
#endif
JSAutoResolveFlags rf(cx, RESOLVE_INFER);
# ifdef DEBUG
#ifdef DEBUG
/*
* We call this macro from BEGIN_CASE in threaded interpreters,
* and before entering the switch in non-threaded interpreters.
@ -2142,14 +2142,19 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
* expect from looking at the code. (We do omit POPs after SETs;
* unfortunate, but not worth fixing.)
*/
# define LOG_OPCODE(OP) JS_BEGIN_MACRO \
# define LOG_OPCODE(OP) JS_BEGIN_MACRO \
if (JS_UNLIKELY(cx->logfp != NULL) && \
(OP) == *regs.pc) \
js_LogOpcode(cx); \
JS_END_MACRO
# else
# define LOG_OPCODE(OP) ((void) 0)
# endif
#else
# define LOG_OPCODE(OP) ((void) 0)
#endif
#define COUNT_OP() JS_BEGIN_MACRO \
if (pcCounts && !regs.fp()->hasImacropc()) \
++pcCounts[regs.pc - script->code]; \
JS_END_MACRO
/*
* Macros for threaded interpreter loop
@ -2184,6 +2189,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
# define DO_OP() JS_BEGIN_MACRO \
CHECK_RECORDER(); \
COUNT_OP(); \
JS_EXTENSION_(goto *jumpTable[op]); \
JS_END_MACRO
# define DO_NEXT_OP(n) JS_BEGIN_MACRO \
@ -2443,6 +2449,7 @@ Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
/* Copy in hot values that change infrequently. */
JSRuntime *const rt = cx->runtime;
JSScript *script = regs.fp()->script();
int *pcCounts = script->pcCounters.get(JSRUNMODE_INTERP);
Value *argv = regs.fp()->maybeFormalArgs();
CHECK_INTERRUPT_HANDLER();
@ -2827,6 +2834,7 @@ BEGIN_CASE(JSOP_STOP)
/* Sync interpreter locals. */
script = regs.fp()->script();
pcCounts = script->pcCounters.get(JSRUNMODE_INTERP);
argv = regs.fp()->maybeFormalArgs();
atoms = FrameAtomBase(cx, regs.fp());
@ -4593,6 +4601,7 @@ BEGIN_CASE(JSOP_FUNCALL)
/* Refresh interpreter locals. */
script = newscript;
pcCounts = script->pcCounters.get(JSRUNMODE_INTERP);
argv = regs.fp()->formalArgsEnd() - newfun->nargs;
atoms = script->atomMap.vector;

View File

@ -130,6 +130,8 @@ const char *js_CodeName[] = {
/************************************************************************/
#define COUNTS_LEN 16
static ptrdiff_t
GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
{
@ -275,20 +277,23 @@ public:
* If counts != NULL, include a counter of the number of times each op was executed.
*/
JS_FRIEND_API(JSBool)
js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc, int* counts, Sprinter *sp)
js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc, Sprinter *sp)
{
jsbytecode *next, *end;
uintN len;
if (counts)
SprintCString(sp, "count x ");
SprintCString(sp, "off ");
SprintCString(sp, "loc ");
if (script->pcCounters)
Sprint(sp, "counts%*s x ", COUNTS_LEN - strlen("counts"), "");
if (lines)
SprintCString(sp, "line");
SprintCString(sp, " op\n");
if (counts)
SprintCString(sp, "-------- ");
SprintCString(sp, "----- ");
if (script->pcCounters) {
for (int i = 0; i < COUNTS_LEN; ++i)
SprintCString(sp, "-");
SprintCString(sp, " ");
}
if (lines)
SprintCString(sp, "----");
SprintCString(sp, " --\n");
@ -306,7 +311,7 @@ js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc
}
len = js_Disassemble1(cx, script, next,
next - script->code,
lines, sp, counts);
lines, sp);
if (!len)
return JS_FALSE;
next += len;
@ -315,18 +320,18 @@ js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc
}
JS_FRIEND_API(JSBool)
js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp, int* counts)
js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp)
{
return js_DisassembleAtPC(cx, script, lines, NULL, counts, sp);
return js_DisassembleAtPC(cx, script, lines, NULL, sp);
}
JS_FRIEND_API(JSBool)
js_DumpPC(JSContext *cx, int* counts = NULL)
js_DumpPC(JSContext *cx)
{
void *mark = JS_ARENA_MARK(&cx->tempPool);
Sprinter sprinter;
INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, counts, &sprinter);
JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter);
fprintf(stdout, "%s", sprinter.base);
JS_ARENA_RELEASE(&cx->tempPool, mark);
return ok;
@ -399,7 +404,7 @@ ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
JS_FRIEND_API(uintN)
js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
uintN loc, JSBool lines, Sprinter *sp, int* counts)
uintN loc, JSBool lines, Sprinter *sp)
{
JSOp op;
const JSCodeSpec *cs;
@ -410,6 +415,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
JSObject *obj;
jsval v;
jsint i;
JSPCCounters& counts(script->pcCounters);
AutoScriptUntrapper untrapper(cx, script, &pc);
@ -425,8 +431,15 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
cs = &js_CodeSpec[op];
len = (ptrdiff_t) cs->length;
Sprint(sp, "%05u:", loc);
if (counts)
Sprint(sp, "% 8d x ", counts[loc]);
if (counts) {
ptrdiff_t start = Sprint(sp, "%d", counts.get(0, loc));
for (size_t i = 1; i < counts.numRunmodes(); ++i)
Sprint(sp, "/%d", counts.get(i, loc));
int l = Sprint(sp, "") - start;
if (l < COUNTS_LEN)
Sprint(sp, "%*s", COUNTS_LEN - l, "");
Sprint(sp, " x ");
}
if (lines)
Sprint(sp, "%4u", JS_PCToLineNumber(cx, script, pc));
Sprint(sp, " %s", js_CodeName[op]);
@ -438,7 +451,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
case JOF_JUMP:
case JOF_JUMPX:
off = GetJumpOffset(pc, pc);
Sprint(sp, " %u (%d)", loc + intN(off), intN(off));
Sprint(sp, " %u (%+d)", loc + (intN) off, (intN) off);
break;
case JOF_ATOM:

View File

@ -522,20 +522,17 @@ CallResultEscapes(jsbytecode *pc);
}
#endif
#ifdef DEBUG
#if defined(DEBUG) && defined(__cplusplus)
/*
* Disassemblers, for debugging only.
*/
#include <stdio.h>
#ifdef __cplusplus
extern JS_FRIEND_API(JSBool)
js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, js::Sprinter *sp, int *counts = NULL);
js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, js::Sprinter *sp);
extern JS_FRIEND_API(uintN)
js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
JSBool lines, js::Sprinter *sp, int *counts = NULL);
JSBool lines, js::Sprinter *sp);
#endif
#endif /* DEBUG */
/*
* Given bytecode address pc in script's main program code, return the operand

View File

@ -745,6 +745,18 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
#endif /* JS_HAS_XDR */
bool
JSPCCounters::init(JSContext *cx, size_t numBytecodes)
{
this->numBytecodes = numBytecodes;
size_t nbytes = sizeof(*counts) * numBytecodes * JSRUNMODE_COUNT;
counts = (int*) cx->malloc_(nbytes);
if (!counts)
return false;
memset(counts, 0, nbytes);
return true;
}
static void
script_finalize(JSContext *cx, JSObject *obj)
{
@ -1093,6 +1105,9 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
script->version = version;
new (&script->bindings) Bindings(cx, emptyCallShape);
if (cx->hasRunOption(JSOPTION_PCCOUNT))
(void) script->pcCounters.init(cx, length);
uint8 *scriptEnd = reinterpret_cast<uint8 *>(script + 1);
cursor = scriptEnd;
@ -1479,7 +1494,7 @@ DestroyScript(JSContext *cx, JSScript *script)
PurgeScriptFragments(&script->compartment->traceMonitor, script);
#endif
#if defined(JS_METHODJIT)
#ifdef JS_METHODJIT
mjit::ReleaseScriptCode(cx, script);
#endif
JS_REMOVE_LINK(&script->links);

View File

@ -395,6 +395,41 @@ struct JITScript;
}
#endif
class JSPCCounters {
size_t numBytecodes;
int *counts;
public:
JSPCCounters() : numBytecodes(0), counts(NULL) {
}
~JSPCCounters() {
js::UnwantedForeground::free_(counts);
}
bool init(JSContext *cx, size_t numBytecodes);
// Boolean conversion, for 'if (counters) ...'
operator void*() const {
return counts;
}
int *get(int runmode) {
JS_ASSERT(runmode >= 0 && runmode < JSRUNMODE_COUNT);
return counts ? &counts[numBytecodes * runmode] : NULL;
}
int& get(int runmode, size_t offset) {
JS_ASSERT(offset < numBytecodes);
JS_ASSERT(counts);
return get(runmode)[offset];
}
size_t numRunmodes() const {
return JSRUNMODE_COUNT;
}
};
struct JSScript {
/*
* Two successively less primitive ways to make a new JSScript. The first
@ -499,6 +534,9 @@ struct JSScript {
uint32 *closedSlots; /* vector of closed slots; args first, then vars. */
/* array of execution counters for every JSOp in the script, by runmode */
JSPCCounters pcCounters;
public:
#ifdef JS_METHODJIT
// Fast-cached pointers to make calls faster. These are also used to

View File

@ -7303,9 +7303,19 @@ TraceRecorder::monitorRecording(JSOp op)
*/
AbortableRecordingStatus status;
#ifdef DEBUG
bool wasInImacro = (cx->fp()->hasImacropc());
#endif
if (!wasInImacro && cx->hasRunOption(JSOPTION_PCCOUNT)) {
JSScript *script = cx->fp()->script();
if (script->pcCounters) {
int offset = cx->regs().pc - script->code;
LIns *pcCounter_addr_ins = w.nameImmpNonGC(&script->pcCounters.get(JSRUNMODE_TRACEJIT, offset));
AnyAddress pcCounter_addr(pcCounter_addr_ins);
LIns *ins = w.ldi(pcCounter_addr);
ins = w.addi(ins, w.name(w.immi(1), "pctick"));
w.st(ins, pcCounter_addr);
}
}
switch (op) {
default:
AbortRecording(cx, "unsupported opcode");

View File

@ -120,9 +120,6 @@ mjit::Compiler::Compiler(JSContext *cx, StackFrame *fp)
#endif
oomInVector(false),
applyTricks(NoApplyTricks)
#if defined DEBUG
,pcProfile(NULL)
#endif
{
}
@ -200,15 +197,6 @@ mjit::Compiler::performCompilation(JITScript **jitp)
jumpMap[i] = Label();
#endif
#if defined(JS_METHODJIT_SPEW) && defined(DEBUG)
if (IsJaegerSpewChannelActive(JSpew_PCProf)) {
pcProfile = (int *)cx->malloc_(sizeof(int) * script->length);
if (!pcProfile)
return Compile_Error;
memset(pcProfile, 0, script->length * sizeof(int));
}
#endif
#ifdef JS_METHODJIT_SPEW
Profiler prof;
prof.start();
@ -231,12 +219,6 @@ mjit::Compiler::performCompilation(JITScript **jitp)
CHECK_STATUS(generateEpilogue());
CHECK_STATUS(finishThisUp(jitp));
#if defined(JS_METHODJIT_SPEW) && defined(DEBUG)
/* Transfer ownership to JITScript */
(*jitp)->pcProfile = pcProfile;
pcProfile = NULL;
#endif
#ifdef JS_METHODJIT_SPEW
prof.stop();
JaegerSpew(JSpew_Prof, "compilation took %d us\n", prof.time_us());
@ -252,10 +234,6 @@ mjit::Compiler::performCompilation(JITScript **jitp)
mjit::Compiler::~Compiler()
{
#ifdef DEBUG
if (pcProfile)
cx->free_(pcProfile);
#endif
cx->free_(jumpMap);
cx->free_(savedTraps);
}
@ -902,24 +880,6 @@ mjit::Compiler::generateMethod()
JSOp op = JSOp(*PC);
int trap = stubs::JSTRAP_NONE;
if (op == JSOP_TRAP) {
#if defined(JS_METHODJIT_SPEW) && defined(DEBUG)
if (IsJaegerSpewChannelActive(JSpew_PCProf)) {
RegisterID r1 = frame.allocReg();
RegisterID r2 = frame.allocReg();
if (IsJaegerSpewChannelActive(JSpew_PCProf)) {
masm.move(ImmPtr(pcProfile), r1);
Address pcCounter(r1, sizeof(int) * (PC - script->code));
masm.load32(pcCounter, r2);
masm.add32(Imm32(1), r2);
masm.store32(r2, pcCounter);
}
frame.freeReg(r1);
frame.freeReg(r2);
}
#endif
if (!trapper.untrap(PC))
return Compile_Error;
op = JSOp(*PC);
@ -928,6 +888,20 @@ mjit::Compiler::generateMethod()
if (script->singleStepMode && scanner.firstOpInLine(PC - script->code))
trap |= stubs::JSTRAP_SINGLESTEP;
if (cx->hasRunOption(JSOPTION_PCCOUNT) && script->pcCounters) {
RegisterID r1 = frame.allocReg();
RegisterID r2 = frame.allocReg();
masm.move(ImmPtr(&script->pcCounters.get(JSRUNMODE_METHODJIT, PC - script->code)), r1);
Address pcCounter(r1);
masm.load32(pcCounter, r2);
masm.add32(Imm32(1), r2);
masm.store32(r2, pcCounter);
frame.freeReg(r1);
frame.freeReg(r2);
}
analyze::Bytecode *opinfo = analysis->maybeCode(PC);
if (!opinfo) {

View File

@ -906,12 +906,6 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
if ((jscr = script->jitNormal)) {
cx->runtime->mjitDataSize -= jscr->scriptDataSize();
#ifdef DEBUG
if (jscr->pcProfile) {
cx->free_(jscr->pcProfile);
jscr->pcProfile = NULL;
}
#endif
jscr->~JITScript();
cx->free_(jscr);
@ -921,12 +915,6 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
if ((jscr = script->jitCtor)) {
cx->runtime->mjitDataSize -= jscr->scriptDataSize();
#ifdef DEBUG
if (jscr->pcProfile) {
cx->free_(jscr->pcProfile);
jscr->pcProfile = NULL;
}
#endif
jscr->~JITScript();
cx->free_(jscr);
@ -1023,42 +1011,3 @@ JITScript::nativeToPC(void *returnAddress) const
JS_ASSERT((uint8*)ic.funGuard.executableAddress() + ic.joinPointOffset == returnAddress);
return ic.pc;
}
#ifdef JS_METHODJIT_SPEW
static void
DumpProfile(JSContext *cx, JSScript *script, JITScript* jit, bool isCtor)
{
JS_ASSERT(!cx->runtime->gcRunning);
#ifdef DEBUG
if (IsJaegerSpewChannelActive(JSpew_PCProf) && jit->pcProfile) {
// Display hit counts for every JS code line
AutoArenaAllocator(&cx->tempPool);
Sprinter sprinter;
INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
js_Disassemble(cx, script, true, &sprinter, jit->pcProfile);
fprintf(stdout, "--- PC PROFILE %s:%d%s ---\n", script->filename, script->lineno,
isCtor ? " (constructor)" : "");
fprintf(stdout, "%s\n", sprinter.base);
fprintf(stdout, "--- END PC PROFILE %s:%d%s ---\n", script->filename, script->lineno,
isCtor ? " (constructor)" : "");
}
#endif
}
#endif
void
mjit::DumpAllProfiles(JSContext *cx)
{
#ifdef JS_METHODJIT_SPEW
for (JSScript *script = (JSScript *) JS_LIST_HEAD(&cx->compartment->scripts);
script != (JSScript *) &cx->compartment->scripts;
script = (JSScript *) JS_NEXT_LINK((JSCList *)script))
{
if (script->jitCtor)
DumpProfile(cx, script, script->jitCtor, true);
if (script->jitNormal)
DumpProfile(cx, script, script->jitNormal, false);
}
#endif
}

View File

@ -418,10 +418,6 @@ struct JITScript {
void purgePICs();
size_t scriptDataSize();
#ifdef DEBUG
/* length script->length array of execution counters for every JSOp in the compiled script */
int *pcProfile;
#endif
jsbytecode *nativeToPC(void *returnAddress) const;
private:

View File

@ -6077,10 +6077,6 @@ Shell(JSContext *cx, int argc, char **argv, char **envp)
}
#endif /* JSDEBUGGER */
#ifdef JS_METHODJIT
mjit::DumpAllProfiles(cx);
#endif
return result;
}