18 #include "precompiled.h"
30 static JSTrapStatus
ThrowHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval,
void* closure)
34 return pThreadDebugger->
ThrowHandler(cx, script, pc, rval);
38 static JSTrapStatus
TrapHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval, jsval closure)
42 jsval val = JSVAL_NULL;
43 return pThreadDebugger->
TrapHandler(cx, script, pc, rval, val);
47 JSTrapStatus
StepHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval,
void* closure)
51 jsval val = JSVAL_VOID;
52 return pThreadDebugger->
StepHandler(cx, script, pc, rval, &val);
56 JSTrapStatus
StepIntoHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval,
void* closure)
64 void NewScriptHook_(JSContext* cx,
const char* filename,
unsigned lineno, JSScript* script, JSFunction* fun,
void* callerdata)
68 return pThreadDebugger->
NewScriptHook(cx, filename, lineno, script, fun, NULL);
80 JSTrapStatus
StepOutHandler_(JSContext* cx, JSScript* script, jsbytecode* pc, jsval* rval,
void* closure)
84 return pThreadDebugger->
StepOutHandler(cx, script, pc, rval, NULL);
90 CScopeLock lock(CheckForBreakRequestHandlerMutex);
96 static void*
CallHook_(JSContext* cx, JSStackFrame* fp, JSBool before, JSBool*
UNUSED(ok),
void* closure)
103 script = JS_GetFrameScript(cx, fp);
104 const char* fileName = JS_GetScriptFilename(cx, script);
105 uint lineno = JS_GetScriptBaseLineNumber(cx, script);
106 JSFunction* fun = JS_GetFrameFunction(cx, fp);
107 pThreadDebugger->
ExecuteHook(cx, fileName, lineno, script, fun, closure);
173 , m_pScriptInterface(NULL)
174 , m_pDebuggingServer(NULL)
175 , m_pLastBreakFrame(new JSStackFrame*)
189 std::list<CActiveBreakPoint*>::iterator itr=
m->m_ActiveBreakPoints.begin();
190 while (itr !=
m->m_ActiveBreakPoints.end())
192 if ((*itr)->m_ToRemove)
197 itr =
m->m_ActiveBreakPoints.erase(itr);
207 JSTrapHandler prevHandler;
209 JS_ClearTrap(
m->m_pScriptInterface->GetContext(), activeBreakPoint->
m_Script, activeBreakPoint->
m_Pc, &prevHandler, &prevClosure);
211 activeBreakPoint->
m_Pc = NULL;
216 std::list<CBreakPoint>* pBreakPoints = NULL;
217 double breakPointsLockID;
218 breakPointsLockID =
m->m_pDebuggingServer->AquireBreakPointAccess(&pBreakPoints);
219 std::list<CBreakPoint>::iterator itr = pBreakPoints->begin();
220 while (itr != pBreakPoints->end())
228 bool trapAlreadySet =
false;
231 std::list<CActiveBreakPoint*>::iterator itr1;
232 for (itr1 =
m->m_ActiveBreakPoints.begin(); itr1 !=
m->m_ActiveBreakPoints.end(); ++itr1)
234 if ((*itr1)->m_ActualLine == (*itr).m_UserLine)
235 trapAlreadySet =
true;
245 m->m_ActiveBreakPoints.push_back(pActiveBreakPoint);
247 itr = pBreakPoints->erase(itr);
253 m->m_pDebuggingServer->ReleaseBreakPointAccess(breakPointsLockID);
258 bool isPresent = (
m->m_LineToPCMap.end() !=
m->m_LineToPCMap.find(filename) &&
m->m_LineToPCMap[filename].end() !=
m->m_LineToPCMap[filename].find(line));
267 jsbytecode* pc =
m->m_LineToPCMap[filename][line].pBytecode;
268 JSScript* script =
m->m_LineToPCMap[filename][line].pScript;
269 activeBreakPoint->
m_Script = script;
270 activeBreakPoint->
m_Pc = pc;
271 ENSURE(script != NULL && pc != NULL);
272 activeBreakPoint->
m_ActualLine = JS_PCToLineNumber(
m->m_pScriptInterface->GetContext(), script, pc);
274 JS_SetTrap(
m->m_pScriptInterface->GetContext(), script, pc,
TrapHandler_, PRIVATE_TO_JSVAL(
this));
292 JS_SetExecuteHook(
m->m_pScriptInterface->GetRuntime(), NULL, NULL);
293 JS_SetCallHook(
m->m_pScriptInterface->GetRuntime(), NULL, NULL);
294 JS_SetNewScriptHook(
m->m_pScriptInterface->GetRuntime(), NULL, NULL);
295 JS_SetDestroyScriptHook(
m->m_pScriptInterface->GetRuntime(), NULL, NULL);
301 std::list<CActiveBreakPoint*>::iterator itr;
302 itr =
m->m_ActiveBreakPoints.begin();
303 while (itr !=
m->m_ActiveBreakPoints.end())
306 if ( ((*itr)->m_Pc == pBytecode || pBytecode == NULL) && !(*itr)->m_ToRemove )
308 std::list<CBreakPoint>* pBreakPoints;
309 double breakPointsLockID =
m->m_pDebuggingServer->AquireBreakPointAccess(&pBreakPoints);
315 pBreakPoints->push_back(breakPoint);
317 itr =
m->m_ActiveBreakPoints.erase(itr);
318 m->m_pDebuggingServer->ReleaseBreakPointAccess(breakPointsLockID);
330 m->m_pScriptInterface = pScriptInterface;
331 m->m_pDebuggingServer = pDebuggingServer;
332 JS_SetExecuteHook(
m->m_pScriptInterface->GetRuntime(),
CallHook_, (
void*)
this);
333 JS_SetCallHook(
m->m_pScriptInterface->GetRuntime(),
CallHook_, (
void*)
this);
334 JS_SetNewScriptHook(
m->m_pScriptInterface->GetRuntime(),
NewScriptHook_, (
void*)
this);
335 JS_SetDestroyScriptHook(
m->m_pScriptInterface->GetRuntime(),
DestroyScriptHook_, (
void*)
this);
336 JS_SetThrowHook(
m->m_pScriptInterface->GetRuntime(),
ThrowHandler_, (
void*)
this);
338 if (
m->m_pDebuggingServer->GetSettingSimultaneousThreadBreak())
352 uint line = JS_PCToLineNumber(cx, script, pc);
353 JSStackFrame* iter = NULL;
354 JSStackFrame* pStackFrame;
355 pStackFrame = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
357 jsval val = JSVAL_VOID;
358 if ((*
m->m_pLastBreakFrame == pStackFrame && lastBreakLine != line) ||
362 return JSTRAP_CONTINUE;
369 uint line = JS_PCToLineNumber(cx, script, pc);
370 JSStackFrame* iter = NULL;
371 JSStackFrame* pStackFrame;
372 pStackFrame = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
375 jsval val = JSVAL_VOID;
376 if ((*
m->m_pLastBreakFrame == pStackFrame && lastBreakLine != line) || *
m->m_pLastBreakFrame != pStackFrame)
379 return JSTRAP_CONTINUE;
386 JSStackFrame* iter = NULL;
387 JSStackFrame* pStackFrame;
388 pStackFrame = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
391 jsval val = JSVAL_VOID;
395 return JSTRAP_CONTINUE;
400 JSStackFrame* iter = NULL;
401 JSStackFrame* fp = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
403 fp = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
406 if (fp == pParentFrame)
408 fp = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
415 jsval val = JSVAL_VOID;
416 if (
m->m_pDebuggingServer->GetBreakRequestedByThread() ||
m->m_pDebuggingServer->GetBreakRequestedByUser())
419 return JSTRAP_CONTINUE;
424 jsval val = JSVAL_NULL;
431 JS_GetPendingException(cx, &jsexception);
432 if (JSVAL_IS_STRING(jsexception))
434 std::string str(JS_EncodeString(cx, JSVAL_TO_STRING(jsexception)));
435 if (str ==
"Breakpoint" ||
m->m_pDebuggingServer->GetSettingBreakOnException())
437 if (str ==
"Breakpoint")
438 JS_ClearPendingException(cx);
439 jsval val = JSVAL_NULL;
443 return JSTRAP_CONTINUE;
448 uint line = JS_PCToLineNumber(cx, script, pc);
449 std::string filename(JS_GetScriptFilename(cx, script));
455 *
m->m_pLastBreakFrame = NULL;
459 JS_ClearInterrupt(
m->m_pScriptInterface->GetRuntime(), NULL, NULL);
460 JS_SetSingleStepMode(cx, script,
false);
463 if (
m->m_pDebuggingServer->GetSettingSimultaneousThreadBreak())
465 m->m_pDebuggingServer->SetBreakRequestedByThread(
true);
473 while (!
m->m_StackInfoRequests.empty())
478 m->m_StackInfoRequests.pop();
490 JSStackFrame* iter = NULL;
491 *
m->m_pLastBreakFrame = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
493 if (!JS_SetSingleStepMode(cx, script,
true))
494 LOGERROR(L
"JS_SetSingleStepMode returned false!");
499 JS_SetInterrupt(
m->m_pScriptInterface->GetRuntime(),
StepHandler_,
this);
509 JS_SetInterrupt(
m->m_pScriptInterface->GetRuntime(),
StepOutHandler_,
this);
516 if (!JS_SetSingleStepMode(cx, script,
true))
517 LOGERROR(L
"JS_SetSingleStepMode returned false!");
526 debug_warn(
"Invalid DBGCMD found in CThreadDebugger::BreakHandler!");
537 m->m_StackFrameData.clear();
540 return JSTRAP_CONTINUE;
545 uint scriptExtent = JS_GetScriptLineExtent (cx, script);
546 std::string stringFileName(filename);
547 if (stringFileName.empty())
550 for (uint line = lineno; line < scriptExtent + lineno; ++line)
557 jsbytecode* oldPC = NULL;
560 firstLine =
m->m_LineToPCMap[stringFileName][line].firstLineInFunction;
561 lastLine =
m->m_LineToPCMap[stringFileName][line].lastLineInFunction;
566 if (lineno < firstLine || scriptExtent + lineno > lastLine)
569 oldPC =
m->m_LineToPCMap[stringFileName][line].pBytecode;
572 jsbytecode* pc = JS_LineNumberToPC (cx, script, line);
573 m->m_LineToPCMap[stringFileName][line].pBytecode = pc;
574 m->m_LineToPCMap[stringFileName][line].pScript = script;
575 m->m_LineToPCMap[stringFileName][line].firstLineInFunction = lineno;
576 m->m_LineToPCMap[stringFileName][line].lastLineInFunction = lineno + scriptExtent;
579 if (lineno == firstLine && scriptExtent + lineno == lastLine)
589 uint scriptExtent = JS_GetScriptLineExtent (cx, script);
590 uint baseLine = JS_GetScriptBaseLineNumber(cx, script);
593 pStr = (
char*)JS_GetScriptFilename(cx, script);
596 std::string fileName(pStr);
598 for (uint line = baseLine; line < scriptExtent + baseLine; ++line)
602 if (
m->m_LineToPCMap[fileName][line].pScript == script)
605 m->m_LineToPCMap[fileName].erase(line);
606 if (
m->m_LineToPCMap[fileName].empty())
607 m->m_LineToPCMap.erase(fileName);
627 std::list<CActiveBreakPoint*>::iterator itr;
628 for (itr =
m->m_ActiveBreakPoints.begin(); itr !=
m->m_ActiveBreakPoints.end(); ++itr)
630 if ((*itr)->m_UserLine == userLine && (*itr)->m_Filename == filename)
632 (*itr)->m_ToRemove = !(*itr)->m_ToRemove;
642 response <<
m->m_Callstack;
652 JSStackFrame *iter = 0;
656 jsArray = JS_NewArrayObject(
m->m_pScriptInterface->GetContext(), 0, 0);
657 JSString* functionID;
659 fp = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
664 fun = JS_GetFrameFunction(
m->m_pScriptInterface->GetContext(), fp);
666 functionID = JS_NewStringCopyZ(
m->m_pScriptInterface->GetContext(),
"null");
669 functionID = JS_GetFunctionId(fun);
670 if (NULL == functionID)
671 functionID = JS_NewStringCopyZ(
m->m_pScriptInterface->GetContext(),
"anonymous");
674 JSBool ret = JS_DefineElement(
m->m_pScriptInterface->GetContext(), jsArray,
counter, STRING_TO_JSVAL(functionID), NULL, NULL, 0);
676 fp = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
680 m->m_Callstack.clear();
681 m->m_Callstack =
m->m_pScriptInterface->StringifyJSON(OBJECT_TO_JSVAL(jsArray),
false).c_str();
687 bool dataCached =
false;
690 dataCached = (!
m->m_StackFrameData.empty() &&
m->m_StackFrameData[stackInfoKind].end() !=
m->m_StackFrameData[stackInfoKind].find(nestingLevel));
703 response.str(std::string());
704 response <<
m->m_StackFrameData[stackInfoKind][nestingLevel];
714 m->m_StackInfoRequests.push(request);
722 JSStackFrame *iter = 0;
729 obj = JS_GetGlobalForScopeChain(
m->m_pScriptInterface->GetContext());
730 m->m_StackFrameData[stackInfo][nestingLevel] =
StringifyCyclicJSON(OBJECT_TO_JSVAL(obj),
false);
734 JSStackFrame *fp = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
737 if (counter == nestingLevel)
742 obj = JS_GetFrameCallObject(
m->m_pScriptInterface->GetContext(), fp);
744 m->m_StackFrameData[stackInfo][nestingLevel] =
StringifyCyclicJSON(OBJECT_TO_JSVAL(obj),
false);
748 if (JS_GetFrameThis(
m->m_pScriptInterface->GetContext(), fp, &val))
751 m->m_StackFrameData[stackInfo][nestingLevel] =
"";
756 fp = JS_FrameIterator(
m->m_pScriptInterface->GetContext(), &iter);
775 namespace CyclicRefWorkaround
785 static JSBool
callback(
const jschar* buf, uint32 len,
void* data)
788 std::wstring strw(str.begin(), str.end());
800 jsval value = JS_ARGV(cx, vp)[1];
801 jsval
key = JS_ARGV(cx, vp)[0];
807 if (JSVAL_IS_OBJECT(value))
820 jsval ret = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,
"Debugger: object removed from output because of cyclic reference."));
821 JS_SET_RVAL(cx, vp, ret);
830 JS_SET_RVAL(cx, vp, JS_ARGV(cx, vp)[1]);
841 JSObject* pGlob = JSVAL_TO_OBJECT(
m->m_pScriptInterface->GetGlobalObject());
842 JSFunction* fun = JS_DefineFunction(
m->m_pScriptInterface->GetContext(), pGlob,
"replacer",
CyclicRefWorkaround::replacer, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
843 JSObject*
replacer = JS_GetFunctionObject(fun);
849 if (JS_GetPendingException(
m->m_pScriptInterface->GetContext(), &exec))
851 if (JSVAL_IS_OBJECT(exec))
853 JS_GetProperty(
m->m_pScriptInterface->GetContext(), JSVAL_TO_OBJECT(exec),
"message", &execString);
855 if (JSVAL_IS_STRING(execString))
857 std::string strExec = JS_EncodeString(
m->m_pScriptInterface->GetContext(), JSVAL_TO_STRING(execString));
858 LOGERROR(L
"Error: %hs", strExec.c_str());
863 JS_ClearPendingException(
m->m_pScriptInterface->GetContext());
864 return std::string();
873 return (pScriptInterface ==
m->m_pScriptInterface);
880 return m->m_BreakFileName;
886 m->m_BreakFileName = breakFileName;
892 return m->m_LastBreakLine;
898 m->m_LastBreakLine = breakLine;
904 return m->m_IsInBreak;
910 m->m_IsInBreak = isInBreak;
916 m->m_NextDbgCmd = dbgCmd;
922 return m->m_NextDbgCmd;
CMutex NewScriptHookMutex
std::auto_ptr< ThreadDebugger_impl > m
std::string m_Name
shared between multiple mongoose threads (initialization may be an exception)
void ReturnActiveBreakPoints(jsbytecode *pBytecode)
JSBool replacer(JSContext *cx, uintN argc, jsval *vp)
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
CMutex m_ActiveBreakpointsMutex
void SetLastBreakLine(uint breakLine)
bool CurrentFrameIsChildOf(JSStackFrame *pParentFrame)
JSTrapStatus StepOutHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
void SDL_Delay(Uint32 ms)
JSTrapStatus ThrowHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
Hook to capture exceptions and breakpoints in code (throw "Breakpoint";)
std::string utf8_from_wstring(const std::wstring &src, Status *err)
opposite of wstring_from_utf8
static ICounter * counter
JSTrapStatus StepHandler_(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
static JSTrapStatus ThrowHandler_(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
Locks a CMutex over this object's lifetime.
CMutex StepOutHandlerMutex
std::string m_BreakFileName
shared between multiple mongoose threads and one scriptinterface thread
JSTrapStatus StepIntoHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
bool CheckIfMappingPresent(std::string filename, uint line)
Checks if a mapping for the specified filename and line number exists in this CThreadDebugger's conte...
void SetAllNewTraps()
Checks if a mapping exists for each breakpoint in the list of breakpoints that aren't set yet...
A non-recursive mutual exclusion lock.
JSTrapStatus StepOutHandler_(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
void ClearTrap(CActiveBreakPoint *activeBreakPoint)
std::map< std::string, std::map< uint, trapLocation > > m_LineToPCMap
bool CompareScriptInterfacePtr(ScriptInterface *pScriptInterface) const
Compares the object's associated scriptinterface with the pointer passed as parameter.
JSTrapStatus CheckForBreakRequestHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
This is an interrup-hook that can be called multiple times per line of code and is used to break into...
JSTrapStatus CheckForBreakRequestHandler_(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
void SetNextDbgCmd(DBGCMD dbgCmd)
void Initialize(uint id, std::string name, ScriptInterface *pScriptInterface, CDebuggingServer *pDebuggingServer)
Initialize the object (required before using the object!).
JSStackFrame ** m_pLastBreakFrame
JSTrapStatus StepIntoHandler_(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
void SDL_DestroySemaphore(SDL_sem *sem)
#define ENSURE(expr)
ensure the expression <expr> evaluates to non-zero.
#define PROFILE2(region)
Starts timing from now until the end of the current scope.
CMutex CheckForBreakRequestHandlerMutex
void NewScriptHook_(JSContext *cx, const char *filename, unsigned lineno, JSScript *script, JSFunction *fun, void *callerdata)
ScriptInterface * m_pScriptInterface
static JSBool callback(const jschar *buf, uint32 len, void *data)
void DestroyScriptHook(JSContext *cx, JSScript *script)
This hook makes sure that invalid mappings between filename plus line-number and jsbytecode points ge...
bool ToggleBreakPoint(std::string filename, uint userLine)
Toggle a breakpoint if it's active in this threadDebugger object.
void GetStackFrameData(std::stringstream &response, uint nestingLevel, STACK_INFO stackInfoKind)
void SaveStackFrameData(STACK_INFO stackInfo, uint nestingLevel)
std::list< CActiveBreakPoint * > m_ActiveBreakPoints
bool g_RecursionDetectedInPrevReplacer
std::set< JSObject * > g_ProcessedObjects
JSTrapStatus BreakHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, jsval closure, BREAK_SRC breakSrc)
All other hooks call this one if the execution should be paused.
void ClearTrapsToRemove()
Used only in the scriptinterface's thread.
i64 Status
Error handling system.
void NewScriptHook(JSContext *cx, const char *filename, unsigned lineno, JSScript *script, JSFunction *fun, void *callerdata)
This hook is used to update the mapping between filename plus line-numbers and jsbytecode pointers...
std::string StringifyCyclicJSON(jsval obj, bool indent)
SDL_sem * SDL_CreateSemaphore(int cnt)
void ExecuteHook(JSContext *cx, const char *filename, unsigned lineno, JSScript *script, JSFunction *fun, void *callerdata)
The callback function which gets executed for each new script that gets loaded and each function insi...
std::basic_string< utf16_t, utf16_traits > utf16string
CMutex StepIntoHandlerMutex
std::string GetBreakFileName()
static void * CallHook_(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure)
void GetCallstack(std::stringstream &response)
void SetNewTrap(CActiveBreakPoint *activeBreakPoint, std::string filename, uint line)
Sets a new trap and stores the information in the CActiveBreakPoint pointer Make sure that a mapping ...
NONCOPYABLE(ThreadDebugger_impl)
std::queue< StackInfoRequest > m_StackInfoRequests
Abstraction around a SpiderMonkey JSContext.
CDebuggingServer * m_pDebuggingServer
#define debug_warn(expr)
display the error dialog with the given text.
void SetBreakFileName(std::string breakFileName)
JSTrapStatus TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, jsval closure)
Simply calls BreakHandler with BREAK_SRC_TRAP.
std::map< STACK_INFO, std::map< uint, std::string > > m_StackFrameData
int SDL_SemPost(SDL_sem *sem)
static JSTrapStatus TrapHandler_(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, jsval closure)
void SetIsInBreak(bool isInBreak)
int SDL_SemWait(SDL_sem *sem)
void DestroyScriptHook_(JSContext *cx, JSScript *script, void *callerdata)
JSTrapStatus StepHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure)
CMutex DestroyScriptHookMutex
void AddStackInfoRequest(STACK_INFO requestType, uint nestingLevel, SDL_sem *semaphore)