From a96f565dbe54db7f4de4363969b7f54bf808f74d Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Wed, 20 Dec 2006 07:47:22 +0000 Subject: [PATCH] Bytecode succession metering (363529, r=crowder). --- js/src/jsapi.c | 6 +++ js/src/jsinterp.c | 110 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/js/src/jsapi.c b/js/src/jsapi.c index 9e10dd65c23..14c03b20a19 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -786,6 +786,12 @@ JS_DestroyRuntime(JSRuntime *rt) JS_PUBLIC_API(void) JS_ShutDown(void) { +#ifdef JS_OPMETER + extern void js_DumpOpPairMeter(); + + js_DumpOpPairMeter(); +#endif + js_FinishDtoa(); #ifdef JS_THREADSAFE js_CleanupLocks(); diff --git a/js/src/jsinterp.c b/js/src/jsinterp.c index c6989fc22f8..c02124c6a24 100644 --- a/js/src/jsinterp.c +++ b/js/src/jsinterp.c @@ -2046,6 +2046,111 @@ InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) # undef JS_THREADED_INTERP #endif +/* + * Define JS_OPMETER to instrument bytecode succession, generating a .dot file + * on shutdown that shows the graph of significant predecessor/successor pairs + * executed, where the edge labels give the succession counts. The .dot file + * is named by the JS_OPMETER_FILE envariable, and defaults to /tmp/ops.dot. + */ +#ifndef JS_OPMETER +# define METER_OP_INIT(op) /* nothing */ +# define METER_OP_PAIR(op1, op2) /* nothing */ +#else + +# include + +/* + * The second dimension is hardcoded at 256 because we know that many bits fit + * in a byte, and mainly to optimize away multiplying by JSOP_LIMIT to address + * any particular row. + */ +static uint32 succeeds[JSOP_LIMIT][256]; + +# define METER_OP_INIT(op) ((op) = JSOP_STOP) +# define METER_OP_PAIR(op1,op2) ((op1) != JSOP_STOP && ++succeeds[op1][op2]) + +typedef struct Edge { + const char *from; + const char *to; + uint32 count; +} Edge; + +static int +compare_edges(const void *a, const void *b) +{ + const Edge *ea = (const Edge *) a; + const Edge *eb = (const Edge *) b; + + return (int32)eb->count - (int32)ea->count; +} + +void +js_DumpOpPairMeter() +{ + const char *name, *from, *style; + FILE *fp; + uint32 total, count; + uint32 i, j, nedges; + Edge *graph; + + name = getenv("JS_OPMETER_FILE"); + if (!name) + name = "/tmp/ops.dot"; + fp = fopen(name, "w"); + if (!fp) { + perror(name); + return; + } + + total = nedges = 0; + for (i = 0; i < JSOP_LIMIT; i++) { + for (j = 0; j < JSOP_LIMIT; j++) { + count = succeeds[i][j]; + if (count != 0) { + total += count; + ++nedges; + } + } + } + +# define SIGNIFICANT(count,total) (200. * (count) >= (total)) + + graph = (Edge *) calloc(nedges, sizeof graph[0]); + for (i = nedges = 0; i < JSOP_LIMIT; i++) { + from = js_CodeSpec[i].name; + for (j = 0; j < JSOP_LIMIT; j++) { + count = succeeds[i][j]; + if (count != 0 && SIGNIFICANT(count, total)) { + graph[nedges].from = from; + graph[nedges].to = js_CodeSpec[j].name; + graph[nedges].count = count; + ++nedges; + } + } + } + qsort(graph, nedges, sizeof(Edge), compare_edges); + +# undef SIGNIFICANT + + fputs("digraph {\n", fp); + for (i = 0, style = NULL; i < nedges; i++) { + JS_ASSERT(i == 0 || graph[i-1].count >= graph[i].count); + if (!style || graph[i-1].count != graph[i].count) { + style = (i > nedges * .75) ? "dotted" : + (i > nedges * .50) ? "dashed" : + (i > nedges * .25) ? "solid" : "bold"; + } + fprintf(fp, " %s -> %s [label=\"%lu\" style=%s]\n", + graph[i].from, graph[i].to, + (unsigned long)graph[i].count, style); + } + free(graph); + fputs("}\n", fp); + fclose(fp); +} + +#endif /* JS_OPSMETER */ + JSBool js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) { @@ -2117,8 +2222,11 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) register void **jumpTable = normalJumpTable; + METER_OP_INIT(op); /* to nullify first METER_OP_PAIR */ + # define DO_OP() JS_EXTENSION_(goto *jumpTable[op]) -# define DO_NEXT_OP(n) do { op = *(pc += (n)); DO_OP(); } while (0) +# define DO_NEXT_OP(n) do { METER_OP_PAIR(op, pc[n]); op = *(pc += (n)); \ + DO_OP(); } while (0) # define BEGIN_CASE(OP) L_##OP: # define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); # define END_VARLEN_CASE DO_NEXT_OP(len);