Bug 605330 - extend jscalls (bug 507012) tracking to cover JM and fix some mismatched calls, r=dvander, a=NPODB

--HG--
extra : rebase_source : 77f64a8ee2c88cbb00ac5671ba2532f71551f28b
This commit is contained in:
Steve Fink 2010-09-20 12:43:51 -07:00
parent c27fce5107
commit 7cdb5fabc2
10 changed files with 109 additions and 87 deletions

View File

@ -16,9 +16,9 @@ static void
funcTransition(const JSFunction *,
const JSScript *,
const JSContext *cx,
JSBool entering)
int entering)
{
if (entering) {
if (entering > 0) {
++depth;
++enters;
if (! JS_ON_TRACE(cx))
@ -32,7 +32,7 @@ funcTransition(const JSFunction *,
static JSBool called2 = false;
static void
funcTransition2(const JSFunction *, const JSScript*, const JSContext*, JSBool)
funcTransition2(const JSFunction *, const JSScript*, const JSContext*, int)
{
called2 = true;
}
@ -43,7 +43,7 @@ static void
funcTransitionOverlay(const JSFunction *fun,
const JSScript *script,
const JSContext *cx,
JSBool entering)
int entering)
{
(*innerCallback)(fun, script, cx, entering);
overlays++;
@ -66,7 +66,7 @@ BEGIN_TEST(testFuncCallback_bug507012)
// Check whether the basic function tracking works
EXEC("f(1)");
CHECK(enters == 2 && leaves == 2 && depth == 0);
CHECK(enters == 1+1 && leaves == 1+1 && depth == 0);
// Can we switch to a different callback?
enters = 777;
@ -91,7 +91,7 @@ BEGIN_TEST(testFuncCallback_bug507012)
EXEC("function g () { ++x; }");
interpreted = enters = leaves = depth = 0;
EXEC("for (i = 0; i < 50; ++i) { g(); }");
CHECK(enters == 50+1 && leaves == 50+1 && depth == 0);
CHECK(enters == 1+50 && leaves == 1+50 && depth == 0);
// If this fails, it means that the code was interpreted rather
// than trace-JITted, and so is not testing what it's supposed to
@ -114,7 +114,7 @@ BEGIN_TEST(testFuncCallback_bug507012)
CHECK(enters == 1);
CHECK(leaves == 1);
CHECK(depth == 0);
CHECK(overlays == 2); // 1 each for enter and exit
CHECK(overlays == enters + leaves);
interpreted = enters = leaves = depth = overlays = 0;
#endif

View File

@ -3307,8 +3307,17 @@ JS_ClearContextThread(JSContext *cx);
typedef void (*JSFunctionCallback)(const JSFunction *fun,
const JSScript *scr,
const JSContext *cx,
JSBool entering);
int entering);
/*
* The callback is expected to be quick and noninvasive. It should not
* trigger interrupts, turn on debugging, or produce uncaught JS
* exceptions. The state of the stack and registers in the context
* cannot be relied upon, since this callback may be invoked directly
* from either JIT. The 'entering' field means we are entering a
* function if it is positive, leaving a function if it is zero or
* negative.
*/
extern JS_PUBLIC_API(void)
JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb);

View File

@ -2176,7 +2176,7 @@ struct JSContext
void doFunctionCallback(const JSFunction *fun,
const JSScript *scr,
JSBool entering) const
int entering) const
{
if (functionCallback)
functionCallback(fun, scr, this, entering);

View File

@ -1347,10 +1347,10 @@ DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp)
JS_ASSERT(vp[0].toObject().getFunctionPrivate() == evalfun);
JS_ASSERT(IsBuiltinEvalFunction(evalfun));
AutoFunctionCallProbe callProbe(cx, evalfun);
JSStackFrame *caller = cx->fp();
JS_ASSERT(caller->isScriptFrame());
AutoFunctionCallProbe callProbe(cx, evalfun, caller->script());
JSObject *scopeChain =
GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
if (!scopeChain || !EvalKernel(cx, argc, vp, DIRECT_EVAL, caller, scopeChain))
@ -2214,7 +2214,8 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp)
if (JS_UNLIKELY(hook != NULL) && !fp->isExecuteFrame())
fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
Probes::enterJSFun(cx, fp->maybeFun());
if (!fp->isExecuteFrame())
Probes::enterJSFun(cx, fp->maybeFun(), fp->maybeScript());
return true;
}
@ -4751,9 +4752,9 @@ BEGIN_CASE(JSOP_FUNCALL)
DO_OP();
}
Probes::enterJSFun(cx, newfun);
Probes::enterJSFun(cx, newfun, script);
JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp);
Probes::exitJSFun(cx, newfun);
Probes::exitJSFun(cx, newfun, script);
regs.sp = vp + 1;
if (!ok)
goto error;

View File

@ -592,7 +592,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const
JSBool ok;
{
AutoPreserveEnumerators preserve(cx);
Probes::enterJSFun(cx, fp->fun());
Probes::enterJSFun(cx, fp->fun(), script_);
#ifdef JS_METHODJIT
AutoInterpPreparer prepareInterp(cx, script_);
ok = mjit::EnterMethodJIT(cx, fp, code_, stackLimit_);
@ -601,7 +601,7 @@ InvokeSessionGuard::invoke(JSContext *cx) const
cx->regs->pc = script_->code;
ok = Interpret(cx, cx->fp());
#endif
Probes::exitJSFun(cx, fp->fun());
Probes::exitJSFun(cx, fp->fun(), script_);
}
PutActivationObjects(cx, fp);
@ -706,7 +706,9 @@ ValuePropertyBearer(JSContext *cx, const Value &v, int spindex)
static inline bool
ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
{
Probes::exitJSFun(cx, fp->maybeFun());
if (!fp->isExecuteFrame())
Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript());
JSInterpreterHook hook = cx->debugHooks->callHook;
if (hook && fp->hasHookData() && !fp->isExecuteFrame())
hook(cx, fp, JS_FALSE, &ok, fp->hookData());

View File

@ -152,16 +152,16 @@ Probes::FunctionName(JSContext *cx, const JSFunction *fun)
* a number of usually unused lines of code would cause.
*/
void
Probes::enterJSFunImpl(JSContext *cx, const JSFunction *fun)
Probes::enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script)
{
JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(FUN_SCRIPT(fun)), FunctionClassname(fun),
JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun),
FunctionName(cx, fun));
}
void
Probes::handleFunctionReturn(JSContext *cx, JSFunction *fun)
Probes::handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script)
{
JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(FUN_SCRIPT(fun)), FunctionClassname(fun),
JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun),
FunctionName(cx, fun));
}

View File

@ -52,16 +52,14 @@ class Probes {
static int FunctionLineNumber(JSContext *cx, const JSFunction *fun);
static const char *FunctionName(JSContext *cx, const JSFunction *fun);
static void enterJSFunImpl(JSContext *cx, const JSFunction *fun);
static void handleFunctionReturn(JSContext *cx, JSFunction *fun);
static void enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script);
static void handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script);
static void finalizeObjectImpl(JSObject *obj);
public:
/*
* If |lval| is provided to the enter/exit methods, it is tested to see if
* it is a function as a predicate to the dtrace event emission.
*/
static void enterJSFun(JSContext *cx, JSFunction *fun, js::Value *lval = NULL);
static void exitJSFun(JSContext *cx, JSFunction *fun, js::Value *lval = NULL);
static bool callTrackingActive(JSContext *);
static void enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1);
static void exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0);
static void startExecution(JSContext *cx, JSScript *script);
static void stopExecution(JSContext *cx, JSScript *script);
@ -113,31 +111,47 @@ class Probes {
static JSBool CustomMark(int marker);
};
inline void
Probes::enterJSFun(JSContext *cx, JSFunction *fun, js::Value *lval)
inline bool
Probes::callTrackingActive(JSContext *cx)
{
#ifdef INCLUDE_MOZILLA_DTRACE
if (!lval || IsFunctionObject(*lval)) {
if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
enterJSFunImpl(cx, fun);
}
if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED() || JAVASCRIPT_FUNCTION_RETURN_ENABLED())
return true;
#endif
#ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(fun, fun ? FUN_SCRIPT(fun) : NULL, true);
if (cx->functionCallback)
return true;
#endif
#ifdef MOZ_ETW
if (ProfilingActive && MCGEN_ENABLE_CHECK(MozillaSpiderMonkey_Context, EvtFunctionEntry))
return true;
#endif
return false;
}
inline void
Probes::enterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
{
#ifdef INCLUDE_MOZILLA_DTRACE
if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
enterJSFunImpl(cx, fun, script);
#endif
#ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(fun, script, counter);
#endif
}
inline void
Probes::exitJSFun(JSContext *cx, JSFunction *fun, js::Value *lval)
Probes::exitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter)
{
#ifdef INCLUDE_MOZILLA_DTRACE
if (!lval || IsFunctionObject(*lval)) {
if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
handleFunctionReturn(cx, fun);
}
if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
handleFunctionReturn(cx, fun, script);
#endif
#ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(fun, fun ? FUN_SCRIPT(fun) : NULL, false);
if (counter > 0)
counter = -counter;
cx->doFunctionCallback(fun, script, counter);
#endif
}
@ -170,11 +184,11 @@ Probes::startExecution(JSContext *cx, JSScript *script)
{
#ifdef INCLUDE_MOZILLA_DTRACE
if (JAVASCRIPT_EXECUTE_START_ENABLED())
JAVASCRIPT_EXECUTE_START(script->filename ? (char *)script->filename : nullName,
JAVASCRIPT_EXECUTE_START((script->filename ? (char *)script->filename : nullName),
script->lineno);
#endif
#ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(NULL, script, true);
cx->doFunctionCallback(NULL, script, 1);
#endif
}
@ -183,11 +197,11 @@ Probes::stopExecution(JSContext *cx, JSScript *script)
{
#ifdef INCLUDE_MOZILLA_DTRACE
if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
JAVASCRIPT_EXECUTE_DONE(script->filename ? (char *)script->filename : nullName,
JAVASCRIPT_EXECUTE_DONE((script->filename ? (char *)script->filename : nullName),
script->lineno);
#endif
#ifdef MOZ_TRACE_JSCALLS
cx->doFunctionCallback(NULL, script, false);
cx->doFunctionCallback(NULL, script, 0);
#endif
}
@ -219,19 +233,19 @@ inline JSBool Probes::CustomMark(int marker) { return JS_TRUE; }
struct AutoFunctionCallProbe {
JSContext * const cx;
JSFunction *fun;
js::Value *lval;
JSScript *script;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
AutoFunctionCallProbe(JSContext *cx, JSFunction *fun, js::Value *lval = NULL
AutoFunctionCallProbe(JSContext *cx, JSFunction *fun, JSScript *script
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: cx(cx), fun(fun), lval(lval)
: cx(cx), fun(fun), script(script)
{
JS_GUARD_OBJECT_NOTIFIER_INIT;
Probes::enterJSFun(cx, fun, lval);
Probes::enterJSFun(cx, fun, script);
}
~AutoFunctionCallProbe() {
Probes::exitJSFun(cx, fun, lval);
Probes::exitJSFun(cx, fun, script);
}
};

View File

@ -10236,22 +10236,20 @@ TraceRecorder::record_JSOP_LEAVEWITH()
return ARECORD_STOP;
}
#ifdef MOZ_TRACE_JSCALLS
// Usually, cx->doFunctionCallback() is invoked via DTrace::enterJSFun
// and friends, but the DTrace:: probes use fp and therefore would
// need to break out of tracing. So we define a functionProbe()
// callback to be called by generated code when a Javascript function
// is entered or exited.
static JSBool JS_FASTCALL
functionProbe(JSContext *cx, JSFunction *fun, JSBool enter)
functionProbe(JSContext *cx, JSFunction *fun, int enter)
{
cx->doFunctionCallback(fun, FUN_SCRIPT(fun), enter);
#ifdef MOZ_TRACE_JSCALLS
JSScript *script = fun ? FUN_SCRIPT(fun) : NULL;
if (enter > 0)
Probes::enterJSFun(cx, fun, script, enter);
else
Probes::exitJSFun(cx, fun, script, enter);
#endif
return true;
}
JS_DEFINE_CALLINFO_3(static, BOOL, functionProbe, CONTEXT, FUNCTION, BOOL,
0, ACCSET_STORE_ANY)
#endif
JS_DEFINE_CALLINFO_3(static, BOOL, functionProbe, CONTEXT, FUNCTION, INT32, 0, ACCSET_ALL)
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_RETURN()
@ -10264,13 +10262,11 @@ TraceRecorder::record_JSOP_RETURN()
putActivationObjects();
#ifdef MOZ_TRACE_JSCALLS
if (cx->functionCallback) {
if (Probes::callTrackingActive(cx)) {
LIns* args[] = { w.immi(0), w.nameImmpNonGC(cx->fp()->fun()), cx_ins };
LIns* call_ins = w.call(&functionProbe_ci, args);
guard(false, w.eqi0(call_ins), MISMATCH_EXIT);
}
#endif
/* If we inlined this function call, make the return value available to the caller code. */
Value& rval = stackval(-1);
@ -11389,8 +11385,7 @@ TraceRecorder::functionCall(uintN argc, JSOp mode)
*/
JSFunction* fun = GET_FUNCTION_PRIVATE(cx, &fval.toObject());
#ifdef MOZ_TRACE_JSCALLS
if (cx->functionCallback) {
if (Probes::callTrackingActive(cx)) {
JSScript *script = FUN_SCRIPT(fun);
if (! script || ! script->isEmpty()) {
LIns* args[] = { w.immi(1), w.nameImmpNonGC(fun), cx_ins };
@ -11398,7 +11393,6 @@ TraceRecorder::functionCall(uintN argc, JSOp mode)
guard(false, w.eqi0(call_ins), MISMATCH_EXIT);
}
}
#endif
if (FUN_INTERPRETED(fun))
return interpretedFunctionCall(fval, fun, argc, mode == JSOP_NEW);
@ -11420,13 +11414,11 @@ TraceRecorder::functionCall(uintN argc, JSOp mode)
}
RecordingStatus rs = callNative(argc, mode);
#ifdef MOZ_TRACE_JSCALLS
if (cx->functionCallback) {
if (Probes::callTrackingActive(cx)) {
LIns* args[] = { w.immi(0), w.nameImmpNonGC(fun), cx_ins };
LIns* call_ins = w.call(&functionProbe_ci, args);
guard(false, w.eqi0(call_ins), MISMATCH_EXIT);
}
#endif
return rs;
}
@ -15514,13 +15506,11 @@ TraceRecorder::record_JSOP_STOP()
putActivationObjects();
#ifdef MOZ_TRACE_JSCALLS
if (cx->functionCallback) {
if (Probes::callTrackingActive(cx)) {
LIns* args[] = { w.immi(0), w.nameImmpNonGC(cx->fp()->fun()), cx_ins };
LIns* call_ins = w.call(&functionProbe_ci, args);
guard(false, w.eqi0(call_ins), MISMATCH_EXIT);
}
#endif
/*
* We know falling off the end of a constructor returns the new object that

View File

@ -361,7 +361,7 @@ mjit::Compiler::generatePrologue()
if (isConstructing)
constructThis();
if (debugMode)
if (debugMode || Probes::callTrackingActive(cx))
stubCall(stubs::EnterScript);
return Compile_Okay;
@ -2199,7 +2199,7 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
/* Only the top of the stack can be returned. */
JS_ASSERT_IF(fe, fe == frame.peek(-1));
if (debugMode) {
if (debugMode || Probes::callTrackingActive(cx)) {
prepareStubCall(Uses(0));
stubCall(stubs::LeaveScript);
}

View File

@ -608,12 +608,15 @@ stubs::EnterScript(VMFrame &f)
{
JSStackFrame *fp = f.fp();
JSContext *cx = f.cx;
JSInterpreterHook hook = cx->debugHooks->callHook;
if (JS_UNLIKELY(hook != NULL) && !fp->isExecuteFrame()) {
fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
if (fp->script()->debugMode) {
JSInterpreterHook hook = cx->debugHooks->callHook;
if (JS_UNLIKELY(hook != NULL) && !fp->isExecuteFrame()) {
fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
}
}
Probes::enterJSFun(cx, fp->maybeFun());
Probes::enterJSFun(cx, fp->maybeFun(), fp->script());
}
void JS_FASTCALL
@ -621,14 +624,17 @@ stubs::LeaveScript(VMFrame &f)
{
JSStackFrame *fp = f.fp();
JSContext *cx = f.cx;
Probes::exitJSFun(cx, fp->maybeFun());
JSInterpreterHook hook = cx->debugHooks->callHook;
Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript());
if (hook && fp->hasHookData() && !fp->isExecuteFrame()) {
JSBool ok = JS_TRUE;
hook(cx, fp, JS_FALSE, &ok, fp->hookData());
if (!ok)
THROW();
if (fp->script()->debugMode) {
JSInterpreterHook hook = cx->debugHooks->callHook;
if (hook && fp->hasHookData() && !fp->isExecuteFrame()) {
JSBool ok = JS_TRUE;
hook(cx, fp, JS_FALSE, &ok, fp->hookData());
if (!ok)
THROW();
}
}
}