mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Bug 610793 - Add a per-script enableSingleStepInterrupts() to JSD [r=dmandelin]
--HG-- extra : rebase_source : 248eb8bf3d3a94cce626614da2be1449c8b27a8f
This commit is contained in:
parent
0c849003c0
commit
0676e09544
@ -78,7 +78,7 @@ interface jsdIActivationCallback;
|
||||
* Debugger service. It's not a good idea to have more than one active client of
|
||||
* the debugger service.
|
||||
*/
|
||||
[scriptable, uuid(01769775-c77c-47f9-8848-0abbab404215)]
|
||||
[scriptable, uuid(1ad86ef3-5eca-4ed7-81c5-a757d1957dff)]
|
||||
interface jsdIDebuggerService : nsISupports
|
||||
{
|
||||
/** Internal use only. */
|
||||
@ -512,7 +512,7 @@ interface jsdIFilterEnumerator : nsISupports
|
||||
/**
|
||||
* Pass an instance of one of these to jsdIDebuggerService::enumerateScripts.
|
||||
*/
|
||||
[scriptable, uuid(5ba76b99-acb1-4ed8-a4e4-a716a7d9097e)]
|
||||
[scriptable, uuid(4eef60c2-9bbc-48fa-b196-646a832c6c81)]
|
||||
interface jsdIScriptEnumerator : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -525,7 +525,7 @@ interface jsdIScriptEnumerator : nsISupports
|
||||
/**
|
||||
* Pass an instance of one of these to jsdIDebuggerService::enumerateContexts.
|
||||
*/
|
||||
[scriptable, uuid(d96af02e-3379-4db5-885d-fee28d178701)]
|
||||
[scriptable, uuid(57d18286-550c-4ca9-ac33-56f12ebba91e)]
|
||||
interface jsdIContextEnumerator : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -538,7 +538,7 @@ interface jsdIContextEnumerator : nsISupports
|
||||
/**
|
||||
* Set jsdIDebuggerService::scriptHook to an instance of one of these.
|
||||
*/
|
||||
[scriptable, uuid(cf7ecc3f-361b-44af-84a7-4b0d6cdca204)]
|
||||
[scriptable, uuid(bb722893-0f63-45c5-b547-7a0947c7b6b6)]
|
||||
interface jsdIScriptHook : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -556,7 +556,7 @@ interface jsdIScriptHook : nsISupports
|
||||
* Hook instances of this interface up to the
|
||||
* jsdIDebuggerService::functionHook and toplevelHook properties.
|
||||
*/
|
||||
[scriptable, uuid(191d2738-22e8-4756-b366-6c878c87d73b)]
|
||||
[scriptable, uuid(3eff1314-7ae3-4cf8-833b-c33c24a55633)]
|
||||
interface jsdICallHook : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -588,7 +588,7 @@ interface jsdICallHook : nsISupports
|
||||
void onCall (in jsdIStackFrame frame, in unsigned long type);
|
||||
};
|
||||
|
||||
[scriptable, uuid(cea9ab1a-4b5d-416f-a197-9ffa7046f2ce)]
|
||||
[scriptable, uuid(e6b45eee-d974-4d85-9d9e-f5a67218deb4)]
|
||||
interface jsdIErrorHook : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -880,7 +880,7 @@ interface jsdIStackFrame : jsdIEphemeral
|
||||
* Script object. In JavaScript engine terms, there's a single script for each
|
||||
* function, and one for the top level script.
|
||||
*/
|
||||
[scriptable, uuid(7e6fb9ed-4382-421d-9a14-c80a486e983b)]
|
||||
[scriptable, uuid(e7935220-7def-4c8e-832f-fbc948a97490)]
|
||||
interface jsdIScript : jsdIEphemeral
|
||||
{
|
||||
/** Internal use only. */
|
||||
@ -1038,6 +1038,10 @@ interface jsdIScript : jsdIEphemeral
|
||||
* Clear all breakpoints set in this script.
|
||||
*/
|
||||
void clearAllBreakpoints ();
|
||||
/**
|
||||
* Call interrupt hook at least once per source line
|
||||
*/
|
||||
void enableSingleStepInterrupts (in PRBool mode);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1046,7 +1050,7 @@ interface jsdIScript : jsdIEphemeral
|
||||
* jsdIValue adds a root for the underlying JavaScript value, so don't keep it
|
||||
* if you don't need to.
|
||||
*/
|
||||
[scriptable, uuid(9cab158f-dc78-41dd-9d11-79e05cb3f2bd)]
|
||||
[scriptable, uuid(fd1311f7-096c-44a3-847b-9d478c8176c3)]
|
||||
interface jsdIValue : jsdIEphemeral
|
||||
{
|
||||
/** Internal use only. */
|
||||
@ -1192,7 +1196,7 @@ interface jsdIValue : jsdIEphemeral
|
||||
* functions from jsdIValue should move to this interface. We could inherit from
|
||||
* jsdIValue or use interface flattening or something.
|
||||
*/
|
||||
[scriptable, uuid(a735a94c-9d41-4997-8fcb-cfa8b649a5b7)]
|
||||
[scriptable, uuid(87d86308-7a27-4255-b23c-ce2394f02473)]
|
||||
interface jsdIObject : nsISupports
|
||||
{
|
||||
/** Internal use only. */
|
||||
@ -1228,7 +1232,7 @@ interface jsdIObject : nsISupports
|
||||
* Representation of a property of an object. When an instance is invalid, all
|
||||
* method and property access will result in a NS_UNAVAILABLE error.
|
||||
*/
|
||||
[scriptable, uuid(4491ecd4-fb6b-43fb-bd6f-5d1473f1df24)]
|
||||
[scriptable, uuid(09332485-1419-42bc-ba1f-070815ed4b82)]
|
||||
interface jsdIProperty : jsdIEphemeral
|
||||
{
|
||||
/** Internal use only. */
|
||||
|
@ -587,6 +587,17 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool
|
||||
jsd_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
|
||||
{
|
||||
JSBool rv;
|
||||
JSD_LOCK();
|
||||
rv = JS_SetSingleStepMode(jsdc->dumbContext, jsdscript->script, enable);
|
||||
JSD_UNLOCK();
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
void
|
||||
@ -751,7 +762,7 @@ jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
|
||||
}
|
||||
|
||||
JSD_ASSERT_VALID_EXEC_HOOK(jsdhook);
|
||||
JS_ASSERT(jsdhook->pc == (jsuword)pc);
|
||||
JS_ASSERT(!jsdhook->pc || jsdhook->pc == (jsuword)pc);
|
||||
JS_ASSERT(jsdhook->jsdscript->script == script);
|
||||
JS_ASSERT(jsdhook->jsdscript->jsdc == jsdc);
|
||||
|
||||
|
@ -1480,6 +1480,20 @@ jsdScript::LineToPc(PRUint32 aLine, PRUint32 aPcmap, PRUint32 *_rval)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdScript::EnableSingleStepInterrupts(PRBool enable)
|
||||
{
|
||||
ASSERT_VALID_EPHEMERAL;
|
||||
|
||||
/* Must have set interrupt hook before enabling */
|
||||
if (enable && !jsdService::GetService()->CheckInterruptHook())
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
|
||||
JSD_EnableSingleStepInterrupts(mCx, mScript, enable);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval)
|
||||
{
|
||||
|
@ -288,6 +288,8 @@ class jsdService : public jsdIDebuggerService
|
||||
virtual ~jsdService();
|
||||
|
||||
static jsdService *GetService ();
|
||||
|
||||
PRBool CheckInterruptHook() { return !!mInterruptHook; }
|
||||
|
||||
private:
|
||||
PRBool mOn;
|
||||
|
@ -576,6 +576,14 @@ JSD_SetInterruptHook(JSDContext* jsdc,
|
||||
return jsd_SetInterruptHook(jsdc, hook, callerdata);
|
||||
}
|
||||
|
||||
JSD_PUBLIC_API(JSBool)
|
||||
JSD_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript* jsdscript, JSBool enable)
|
||||
{
|
||||
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
||||
JSD_ASSERT_VALID_SCRIPT(jsdscript);
|
||||
return jsd_EnableSingleStepInterrupts(jsdc, jsdscript, enable);
|
||||
}
|
||||
|
||||
JSD_PUBLIC_API(JSBool)
|
||||
JSD_ClearInterruptHook(JSDContext* jsdc)
|
||||
{
|
||||
|
@ -803,6 +803,12 @@ JSD_SetInterruptHook(JSDContext* jsdc,
|
||||
JSD_ExecutionHookProc hook,
|
||||
void* callerdata);
|
||||
|
||||
/*
|
||||
* Call the interrupt hook at least once per source line
|
||||
*/
|
||||
extern JSD_PUBLIC_API(JSBool)
|
||||
JSD_EnableSingleStepInterrupts(JSDContext* jsdc, JSDScript *jsdscript, JSBool enable);
|
||||
|
||||
/*
|
||||
* Clear the current interrupt hook.
|
||||
*/
|
||||
|
@ -145,7 +145,7 @@ js_SetDebugMode(JSContext *cx, JSBool debug)
|
||||
for (JSScript *script = (JSScript *)cx->compartment->scripts.next;
|
||||
&script->links != &cx->compartment->scripts;
|
||||
script = (JSScript *)script->links.next) {
|
||||
if (script->debugMode != (bool) debug &&
|
||||
if (script->debugMode != !!debug &&
|
||||
script->hasJITCode() &&
|
||||
!IsScriptLive(cx, script)) {
|
||||
/*
|
||||
@ -182,6 +182,30 @@ JS_SetDebugMode(JSContext *cx, JSBool debug)
|
||||
return js_SetDebugMode(cx, debug);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
|
||||
{
|
||||
if (!script->singleStepMode == !singleStep)
|
||||
return JS_TRUE;
|
||||
|
||||
JS_ASSERT_IF(singleStep, cx->compartment->debugMode);
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
/* request the next recompile to inject single step interrupts */
|
||||
script->singleStepMode = !!singleStep;
|
||||
|
||||
js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor;
|
||||
if (jit && script->singleStepMode != jit->singleStepMode) {
|
||||
js::mjit::Recompiler recompiler(cx, script);
|
||||
if (!recompiler.recompile()) {
|
||||
script->singleStepMode = !singleStep;
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
CheckDebugMode(JSContext *cx)
|
||||
{
|
||||
@ -198,6 +222,15 @@ CheckDebugMode(JSContext *cx)
|
||||
return debugMode;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
|
||||
{
|
||||
if (!CheckDebugMode(cx))
|
||||
return JS_FALSE;
|
||||
|
||||
return js_SetSingleStepMode(cx, script, singleStep);
|
||||
}
|
||||
|
||||
/*
|
||||
* NB: FindTrap must be called with rt->debuggerLock acquired.
|
||||
*/
|
||||
|
@ -78,6 +78,14 @@ js_SetDebugMode(JSContext *cx, JSBool debug);
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_SetDebugMode(JSContext *cx, JSBool debug);
|
||||
|
||||
/* Turn on single step mode. Requires debug mode. */
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep);
|
||||
|
||||
/* Turn on single step mode. */
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep);
|
||||
|
||||
/*
|
||||
* Unexported library-private helper used to unpatch all traps in a script.
|
||||
* Returns script->code if script has no traps, else a JS_malloc'ed copy of
|
||||
|
@ -242,6 +242,7 @@ struct JSScript {
|
||||
this script */
|
||||
#ifdef JS_METHODJIT
|
||||
bool debugMode:1; /* script was compiled in debug mode */
|
||||
bool singleStepMode:1; /* compile script in single-step mode */
|
||||
#endif
|
||||
|
||||
jsbytecode *main; /* main entry point, after predef'ing prolog */
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "MethodJIT.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsbool.h"
|
||||
#include "jsemit.h"
|
||||
#include "jsiter.h"
|
||||
#include "Compiler.h"
|
||||
#include "StubCalls.h"
|
||||
@ -440,6 +441,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
||||
jit->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size());
|
||||
jit->nCallSites = callSites.length();
|
||||
jit->invokeEntry = result;
|
||||
jit->singleStepMode = script->singleStepMode;
|
||||
|
||||
/* Build the pc -> ncode mapping. */
|
||||
NativeMapEntry *nmap = (NativeMapEntry *)cursor;
|
||||
@ -791,6 +793,32 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
|
||||
return Compile_Okay;
|
||||
}
|
||||
|
||||
class SrcNoteLineScanner {
|
||||
ptrdiff_t offset;
|
||||
jssrcnote *sn;
|
||||
|
||||
public:
|
||||
SrcNoteLineScanner(jssrcnote *sn) : offset(0), sn(sn) {}
|
||||
|
||||
bool firstOpInLine(ptrdiff_t relpc) {
|
||||
while ((offset < relpc) && !SN_IS_TERMINATOR(sn)) {
|
||||
offset += SN_DELTA(sn);
|
||||
sn = SN_NEXT(sn);
|
||||
}
|
||||
|
||||
while ((offset == relpc) && !SN_IS_TERMINATOR(sn)) {
|
||||
JSSrcNoteType type = (JSSrcNoteType) SN_TYPE(sn);
|
||||
if (type == SRC_SETLINE || type == SRC_NEWLINE)
|
||||
return true;
|
||||
|
||||
offset += SN_DELTA(sn);
|
||||
sn = SN_NEXT(sn);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
#define SPEW_OPCODE() \
|
||||
JS_BEGIN_MACRO \
|
||||
@ -815,16 +843,19 @@ CompileStatus
|
||||
mjit::Compiler::generateMethod()
|
||||
{
|
||||
mjit::AutoScriptRetrapper trapper(cx, script);
|
||||
SrcNoteLineScanner scanner(script->notes());
|
||||
|
||||
for (;;) {
|
||||
JSOp op = JSOp(*PC);
|
||||
bool trap = (op == JSOP_TRAP);
|
||||
|
||||
if (trap) {
|
||||
int trap = stubs::JSTRAP_NONE;
|
||||
if (op == JSOP_TRAP) {
|
||||
if (!trapper.untrap(PC))
|
||||
return Compile_Error;
|
||||
op = JSOp(*PC);
|
||||
trap |= stubs::JSTRAP_TRAP;
|
||||
}
|
||||
if (script->singleStepMode && scanner.firstOpInLine(PC - script->code))
|
||||
trap |= stubs::JSTRAP_SINGLESTEP;
|
||||
|
||||
analyze::Bytecode *opinfo = analysis->maybeCode(PC);
|
||||
|
||||
@ -850,7 +881,7 @@ mjit::Compiler::generateMethod()
|
||||
|
||||
if (trap) {
|
||||
prepareStubCall(Uses(0));
|
||||
masm.move(ImmPtr(PC), Registers::ArgReg1);
|
||||
masm.move(Imm32(trap), Registers::ArgReg1);
|
||||
Call cl = emitStubCall(JS_FUNC_TO_DATA_PTR(void *, stubs::Trap));
|
||||
InternalCallSite site(masm.callReturnOffset(cl), PC,
|
||||
CallSite::MAGIC_TRAP_ID, true, false);
|
||||
|
@ -331,6 +331,7 @@ struct JITScript {
|
||||
void *invokeEntry; /* invoke address */
|
||||
void *fastEntry; /* cached entry, fastest */
|
||||
void *arityCheckEntry; /* arity check address */
|
||||
bool singleStepMode; /* compiled in "single step mode" */
|
||||
|
||||
~JITScript();
|
||||
|
||||
|
@ -1316,11 +1316,32 @@ stubs::Interrupt(VMFrame &f, jsbytecode *pc)
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::Trap(VMFrame &f, jsbytecode *pc)
|
||||
stubs::Trap(VMFrame &f, uint32 trapTypes)
|
||||
{
|
||||
Value rval;
|
||||
jsbytecode *pc = f.cx->regs->pc;
|
||||
|
||||
switch (JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval))) {
|
||||
/*
|
||||
* Trap may be called for a single-step interrupt trap and/or a
|
||||
* regular trap. Try the single-step first, and if it lets control
|
||||
* flow through or does not exist, do the regular trap.
|
||||
*/
|
||||
JSTrapStatus result = JSTRAP_CONTINUE;
|
||||
if (trapTypes & JSTRAP_SINGLESTEP) {
|
||||
/*
|
||||
* single step mode may be paused without recompiling by
|
||||
* setting the interruptHook to NULL.
|
||||
*/
|
||||
JSInterruptHook hook = f.cx->debugHooks->interruptHook;
|
||||
if (hook)
|
||||
result = hook(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval),
|
||||
f.cx->debugHooks->interruptHookData);
|
||||
}
|
||||
|
||||
if (result == JSTRAP_CONTINUE && (trapTypes & JSTRAP_TRAP))
|
||||
result = JS_HandleTrap(f.cx, f.cx->fp()->script(), pc, Jsvalify(&rval));
|
||||
|
||||
switch (result) {
|
||||
case JSTRAP_THROW:
|
||||
f.cx->throwing = JS_TRUE;
|
||||
f.cx->exception = rval;
|
||||
|
@ -47,10 +47,16 @@ namespace js {
|
||||
namespace mjit {
|
||||
namespace stubs {
|
||||
|
||||
typedef enum JSTrapType {
|
||||
JSTRAP_NONE = 0,
|
||||
JSTRAP_TRAP = 1,
|
||||
JSTRAP_SINGLESTEP = 2
|
||||
} JSTrapType;
|
||||
|
||||
void JS_FASTCALL This(VMFrame &f);
|
||||
JSObject * JS_FASTCALL NewInitArray(VMFrame &f, uint32 count);
|
||||
JSObject * JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base);
|
||||
void JS_FASTCALL Trap(VMFrame &f, jsbytecode *pc);
|
||||
void JS_FASTCALL Trap(VMFrame &f, uint32 trapTypes);
|
||||
void JS_FASTCALL Debugger(VMFrame &f, jsbytecode *pc);
|
||||
void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc);
|
||||
void JS_FASTCALL InitElem(VMFrame &f, uint32 last);
|
||||
|
Loading…
Reference in New Issue
Block a user