mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 482743: Fix up bytecode execution tracing. Allow tracing to file. r=igor
js_TraceOpcode: Remember the last bytecode we traced explicitly, instead of subtracting 'len' from regs.pc, which isn't reliable. Decline to trace values in script prologues (between 'code' and 'main'). Decline to walk off the bottom of the stack when the 'last bytecode' is misleading. Flush the stream after each bytecode. Use the TRACE_OPCODE macro in both threaded and non-threaded interpreters. Take care to make threaded and non-threaded interpreters produce the same traces. In the shell's 'tracing' function, use JS_ValueToBoolean to recognize all sorts of booleans, and treat a string as the name of a file to write the trace to.
This commit is contained in:
parent
4df05a423c
commit
02252a653d
@ -1557,7 +1557,7 @@ js_ReportValueErrorFlags(JSContext *cx, uintN flags, const uintN errorNumber,
|
|||||||
|
|
||||||
#if defined DEBUG && defined XP_UNIX
|
#if defined DEBUG && defined XP_UNIX
|
||||||
/* For gdb usage. */
|
/* For gdb usage. */
|
||||||
void js_traceon(JSContext *cx) { cx->tracefp = stderr; }
|
void js_traceon(JSContext *cx) { cx->tracefp = stderr; cx->tracePrevOp = JSOP_LIMIT; }
|
||||||
void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
|
void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -922,6 +922,7 @@ struct JSContext {
|
|||||||
char *lastMessage;
|
char *lastMessage;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
void *tracefp;
|
void *tracefp;
|
||||||
|
JSOp tracePrevOp;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Per-context optional error reporter. */
|
/* Per-context optional error reporter. */
|
||||||
|
@ -2045,12 +2045,11 @@ js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
JS_STATIC_INTERPRET JS_REQUIRES_STACK void
|
JS_STATIC_INTERPRET JS_REQUIRES_STACK void
|
||||||
js_TraceOpcode(JSContext *cx, jsint len)
|
js_TraceOpcode(JSContext *cx)
|
||||||
{
|
{
|
||||||
FILE *tracefp;
|
FILE *tracefp;
|
||||||
JSStackFrame *fp;
|
JSStackFrame *fp;
|
||||||
JSFrameRegs *regs;
|
JSFrameRegs *regs;
|
||||||
JSOp prevop;
|
|
||||||
intN ndefs, n, nuses;
|
intN ndefs, n, nuses;
|
||||||
jsval *siter;
|
jsval *siter;
|
||||||
JSString *str;
|
JSString *str;
|
||||||
@ -2060,10 +2059,22 @@ js_TraceOpcode(JSContext *cx, jsint len)
|
|||||||
JS_ASSERT(tracefp);
|
JS_ASSERT(tracefp);
|
||||||
fp = cx->fp;
|
fp = cx->fp;
|
||||||
regs = fp->regs;
|
regs = fp->regs;
|
||||||
if (len != 0) {
|
|
||||||
prevop = (JSOp) regs->pc[-len];
|
/*
|
||||||
ndefs = js_CodeSpec[prevop].ndefs;
|
* Operations in prologues don't produce interesting values, and
|
||||||
if (ndefs != 0) {
|
* js_DecompileValueGenerator isn't set up to handle them anyway.
|
||||||
|
*/
|
||||||
|
if (cx->tracePrevOp != JSOP_LIMIT && regs->pc >= fp->script->main) {
|
||||||
|
ndefs = js_GetStackDefs(cx, &js_CodeSpec[cx->tracePrevOp],
|
||||||
|
cx->tracePrevOp, fp->script, regs->pc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there aren't that many elements on the stack, then
|
||||||
|
* we have probably entered a new frame, and printing output
|
||||||
|
* would just be misleading.
|
||||||
|
*/
|
||||||
|
if (ndefs != 0 &&
|
||||||
|
ndefs < regs->sp - fp->slots) {
|
||||||
for (n = -ndefs; n < 0; n++) {
|
for (n = -ndefs; n < 0; n++) {
|
||||||
char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
|
char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
|
||||||
NULL);
|
NULL);
|
||||||
@ -2108,6 +2119,10 @@ js_TraceOpcode(JSContext *cx, jsint len)
|
|||||||
}
|
}
|
||||||
fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
|
fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
|
||||||
}
|
}
|
||||||
|
cx->tracePrevOp = op;
|
||||||
|
|
||||||
|
/* It's nice to have complete traces when debugging a crash. */
|
||||||
|
fflush(tracefp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* DEBUG */
|
#endif /* DEBUG */
|
||||||
@ -2534,6 +2549,30 @@ js_Interpret(JSContext *cx)
|
|||||||
# define JS_EXTENSION_(s) s
|
# define JS_EXTENSION_(s) s
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
# ifdef DEBUG
|
||||||
|
/*
|
||||||
|
* We call this macro from BEGIN_CASE in threaded interpreters,
|
||||||
|
* and before entering the switch in non-threaded interpreters.
|
||||||
|
* However, reaching such points doesn't mean we've actually
|
||||||
|
* fetched an OP from the instruction stream: some opcodes use
|
||||||
|
* 'op=x; DO_OP()' to let another opcode's implementation finish
|
||||||
|
* their work, and many opcodes share entry points with a run of
|
||||||
|
* consecutive BEGIN_CASEs.
|
||||||
|
*
|
||||||
|
* Take care to trace OP only when it is the opcode fetched from
|
||||||
|
* the instruction stream, so the trace matches what one would
|
||||||
|
* expect from looking at the code. (We do omit POPs after SETs;
|
||||||
|
* unfortunate, but not worth fixing.)
|
||||||
|
*/
|
||||||
|
# define TRACE_OPCODE(OP) JS_BEGIN_MACRO \
|
||||||
|
if (JS_UNLIKELY(cx->tracefp != NULL) && \
|
||||||
|
(OP) == *regs.pc) \
|
||||||
|
js_TraceOpcode(cx); \
|
||||||
|
JS_END_MACRO
|
||||||
|
# else
|
||||||
|
# define TRACE_OPCODE(OP) ((void) 0)
|
||||||
|
# endif
|
||||||
|
|
||||||
#if JS_THREADED_INTERP
|
#if JS_THREADED_INTERP
|
||||||
static void *const normalJumpTable[] = {
|
static void *const normalJumpTable[] = {
|
||||||
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
|
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
|
||||||
@ -2572,15 +2611,6 @@ js_Interpret(JSContext *cx)
|
|||||||
DO_OP(); \
|
DO_OP(); \
|
||||||
JS_END_MACRO
|
JS_END_MACRO
|
||||||
|
|
||||||
# ifdef DEBUG
|
|
||||||
# define TRACE_OPCODE(OP) JS_BEGIN_MACRO \
|
|
||||||
if (cx->tracefp) \
|
|
||||||
js_TraceOpcode(cx, len); \
|
|
||||||
JS_END_MACRO
|
|
||||||
# else
|
|
||||||
# define TRACE_OPCODE(OP) (void)0
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# define BEGIN_CASE(OP) L_##OP: TRACE_OPCODE(OP); CHECK_RECORDER();
|
# define BEGIN_CASE(OP) L_##OP: TRACE_OPCODE(OP); CHECK_RECORDER();
|
||||||
# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
|
# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH);
|
||||||
# define END_VARLEN_CASE DO_NEXT_OP(len);
|
# define END_VARLEN_CASE DO_NEXT_OP(len);
|
||||||
@ -2846,13 +2876,10 @@ js_Interpret(JSContext *cx)
|
|||||||
advance_pc:
|
advance_pc:
|
||||||
regs.pc += len;
|
regs.pc += len;
|
||||||
op = (JSOp) *regs.pc;
|
op = (JSOp) *regs.pc;
|
||||||
# ifdef DEBUG
|
|
||||||
if (cx->tracefp)
|
|
||||||
js_TraceOpcode(cx, len);
|
|
||||||
# endif
|
|
||||||
|
|
||||||
do_op:
|
do_op:
|
||||||
CHECK_RECORDER();
|
CHECK_RECORDER();
|
||||||
|
TRACE_OPCODE(op);
|
||||||
switchOp = intN(op) | switchMask;
|
switchOp = intN(op) | switchMask;
|
||||||
do_switch:
|
do_switch:
|
||||||
switch (switchOp) {
|
switch (switchOp) {
|
||||||
|
@ -611,7 +611,7 @@ js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2);
|
|||||||
* previous opcode.
|
* previous opcode.
|
||||||
*/
|
*/
|
||||||
extern JS_REQUIRES_STACK void
|
extern JS_REQUIRES_STACK void
|
||||||
js_TraceOpcode(JSContext *cx, jsint len);
|
js_TraceOpcode(JSContext *cx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* JS_OPMETER helper functions.
|
* JS_OPMETER helper functions.
|
||||||
|
@ -1859,13 +1859,8 @@ DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
|||||||
static JSBool
|
static JSBool
|
||||||
Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
{
|
{
|
||||||
JSBool bval;
|
FILE *file;
|
||||||
JSString *str;
|
|
||||||
|
|
||||||
#if JS_THREADED_INTERP
|
|
||||||
JS_ReportError(cx, "tracing not supported in JS_THREADED_INTERP builds");
|
|
||||||
return JS_FALSE;
|
|
||||||
#else
|
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
*rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
|
*rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
@ -1873,24 +1868,39 @@ Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
|||||||
|
|
||||||
switch (JS_TypeOfValue(cx, argv[0])) {
|
switch (JS_TypeOfValue(cx, argv[0])) {
|
||||||
case JSTYPE_NUMBER:
|
case JSTYPE_NUMBER:
|
||||||
bval = JSVAL_IS_INT(argv[0])
|
case JSTYPE_BOOLEAN: {
|
||||||
? JSVAL_TO_INT(argv[0])
|
JSBool bval;
|
||||||
: (jsint) *JSVAL_TO_DOUBLE(argv[0]);
|
if (!JS_ValueToBoolean(cx, argv[0], &bval))
|
||||||
|
goto bad_argument;
|
||||||
|
file = bval ? stderr : NULL;
|
||||||
break;
|
break;
|
||||||
case JSTYPE_BOOLEAN:
|
}
|
||||||
bval = JSVAL_TO_BOOLEAN(argv[0]);
|
case JSTYPE_STRING: {
|
||||||
break;
|
char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
||||||
default:
|
file = fopen(name, "w");
|
||||||
str = JS_ValueToString(cx, argv[0]);
|
if (!file) {
|
||||||
if (!str)
|
JS_ReportError(cx, "tracing: couldn't open output file %s: %s",
|
||||||
|
name, strerror(errno));
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
JS_ReportError(cx, "tracing: illegal argument %s",
|
}
|
||||||
JS_GetStringBytes(str));
|
break;
|
||||||
return JS_FALSE;
|
}
|
||||||
|
default:
|
||||||
|
goto bad_argument;
|
||||||
}
|
}
|
||||||
cx->tracefp = bval ? stderr : NULL;
|
if (cx->tracefp && cx->tracefp != stderr)
|
||||||
|
fclose((FILE *)cx->tracefp);
|
||||||
|
cx->tracefp = file;
|
||||||
|
cx->tracePrevOp = JSOP_LIMIT;
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
#endif
|
|
||||||
|
bad_argument:
|
||||||
|
JSString *str = JS_ValueToString(cx, argv[0]);
|
||||||
|
if (!str)
|
||||||
|
return JS_FALSE;
|
||||||
|
JS_ReportError(cx, "tracing: illegal argument %s",
|
||||||
|
JS_GetStringBytes(str));
|
||||||
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -3676,7 +3686,8 @@ static const char *const shell_help_messages[] = {
|
|||||||
"dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
|
"dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
|
||||||
" Interface to JS_DumpHeap with output sent to file",
|
" Interface to JS_DumpHeap with output sent to file",
|
||||||
"notes([fun]) Show source notes for functions",
|
"notes([fun]) Show source notes for functions",
|
||||||
"tracing([toggle]) Turn tracing on or off",
|
"tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
|
||||||
|
" With filename, send to file.\n",
|
||||||
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
|
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
|
||||||
#endif
|
#endif
|
||||||
#ifdef TEST_CVTARGS
|
#ifdef TEST_CVTARGS
|
||||||
|
Loading…
Reference in New Issue
Block a user