Bug 610793 - Add a per-script enableSingleStepInterrupts() to JSD [r=dmandelin]

--HG--
extra : rebase_source : 248eb8bf3d3a94cce626614da2be1449c8b27a8f
This commit is contained in:
Steve Fink 2010-11-16 15:18:35 -08:00
Родитель 0c849003c0
Коммит 0676e09544
13 изменённых файлов: 165 добавлений и 19 удалений

Просмотреть файл

@ -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)
{

Просмотреть файл

@ -289,6 +289,8 @@ class jsdService : public jsdIDebuggerService
static jsdService *GetService ();
PRBool CheckInterruptHook() { return !!mInterruptHook; }
private:
PRBool mOn;
PRUint32 mPauseLevel;

Просмотреть файл

@ -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);