static void * callCountHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure) { callCount[before]++; jsval thisv; JS_GetFrameThis(cx, fp, &thisv); // assert if fp is incomplete return cx; // any non-null value causes the hook to be called again after }
static void * strictThisHook(JSContext *cx, JSStackFrame *fp, JSBool before, JSBool *ok, void *closure) { if (before) { bool *anyWrapped = (bool *) closure; jsval thisv; JS_GetFrameThis(cx, fp, &thisv); *anyWrapped = *anyWrapped || !JSVAL_IS_PRIMITIVE(thisv); } return NULL; }
void CThreadDebugger::SaveStackFrameData(STACK_INFO stackInfo, uint nestingLevel) { ENSURE(GetIsInBreak()); CScopeLock lock(m->m_Mutex); JSStackFrame *fp; JSStackFrame *iter = 0; uint counter = 0; jsval val; if (stackInfo == STACK_INFO_GLOBALOBJECT) { JSObject* obj; obj = JS_GetGlobalForScopeChain(m->m_pScriptInterface->GetContext()); m->m_StackFrameData[stackInfo][nestingLevel] = StringifyCyclicJSON(OBJECT_TO_JSVAL(obj), false); } else { fp = JS_FrameIterator(m->m_pScriptInterface->GetContext(), &iter); while (fp) { if (counter == nestingLevel) { if (stackInfo == STACK_INFO_LOCALS) { JSObject* obj; obj = JS_GetFrameCallObject(m->m_pScriptInterface->GetContext(), fp); //obj = JS_GetFrameScopeChain(m->m_pScriptInterface->GetContext(), fp); m->m_StackFrameData[stackInfo][nestingLevel] = StringifyCyclicJSON(OBJECT_TO_JSVAL(obj), false); } else if (stackInfo == STACK_INFO_THIS) { if (JS_GetFrameThis(m->m_pScriptInterface->GetContext(), fp, &val)) m->m_StackFrameData[stackInfo][nestingLevel] = StringifyCyclicJSON(val, false); else m->m_StackFrameData[stackInfo][nestingLevel] = ""; } } counter++; fp = JS_FrameIterator(m->m_pScriptInterface->GetContext(), &iter); } } }
static void _interpreterTrace(JSDContext* jsdc, JSContext *cx, JSStackFrame *fp, JSBool before) { JSDScript* jsdscript = NULL; JSScript * script; static indent = 0; char* buf; const char* funName = NULL; script = JS_GetFrameScript(cx, fp); if(script) { JSD_LOCK_SCRIPTS(jsdc); jsdscript = jsd_FindJSDScript(jsdc, script); JSD_UNLOCK_SCRIPTS(jsdc); if(jsdscript) funName = JSD_GetScriptFunctionName(jsdc, jsdscript); } if(!funName) funName = "TOP_LEVEL"; if(before) { buf = JS_smprintf("%sentering %s %s this: %0x\n", _indentSpaces(indent++), funName, JS_IsConstructorFrame(cx, fp) ? "constructing":"", (int)JS_GetFrameThis(cx, fp)); } else { buf = JS_smprintf("%sleaving %s\n", _indentSpaces(--indent), funName); } JS_ASSERT(indent >= 0); if(!buf) return; printf(buf); free(buf); }
static void _interpreterTrace(JSDContext* jsdc, JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, JSBool before) { JSDScript* jsdscript = NULL; JSScript * script; static indent = 0; JSString* funName = NULL; script = frame.script(); if(script) { JSD_LOCK_SCRIPTS(jsdc); jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame); JSD_UNLOCK_SCRIPTS(jsdc); if(jsdscript) funName = JSD_GetScriptFunctionId(jsdc, jsdscript); } if(before) printf("%sentering ", _indentSpaces(indent++)); else printf("%sleaving ", _indentSpaces(--indent)); if (!funName) printf("TOP_LEVEL"); else JS_FileEscapedString(stdout, funName, 0); if(before) { jsval thisVal; printf("%s this: ", isConstructing ? "constructing":""); if (JS_GetFrameThis(cx, frame, &thisVal)) printf("0x%0llx", (uintptr_t) thisVal); else puts("<unavailable>"); } printf("\n"); JS_ASSERT(indent >= 0); }
JSDValue* jsd_GetThisForStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe) { JSObject* obj; JSDValue* jsdval = NULL; JSD_LOCK_THREADSTATES(jsdc); if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) ) { obj = JS_GetFrameThis(jsdthreadstate->context, jsdframe->fp); if(obj) jsdval = JSD_NewValue(jsdc, OBJECT_TO_JSVAL(obj)); } JSD_UNLOCK_THREADSTATES(jsdc); return jsdval; }
JSDValue* jsd_GetThisForStackFrame(JSDContext* jsdc, JSDThreadState* jsdthreadstate, JSDStackFrameInfo* jsdframe) { JSObject* obj; JSDValue* jsdval = NULL; JSD_LOCK_THREADSTATES(jsdc); if( jsd_IsValidFrameInThreadState(jsdc, jsdthreadstate, jsdframe) ) { JSBool ok; jsval thisval; JS_BeginRequest(jsdthreadstate->context); ok = JS_GetFrameThis(jsdthreadstate->context, jsdframe->fp, &thisval); JS_EndRequest(jsdthreadstate->context); if(ok) jsdval = JSD_NewValue(jsdc, thisval); } JSD_UNLOCK_THREADSTATES(jsdc); return jsdval; }
JSDThreadState* jsd_NewThreadState(JSDContext* jsdc, JSContext *cx ) { JSDThreadState* jsdthreadstate; JSStackFrame * iter = NULL; JSStackFrame * fp; jsdthreadstate = (JSDThreadState*)calloc(1, sizeof(JSDThreadState)); if( ! jsdthreadstate ) return NULL; jsdthreadstate->context = cx; jsdthreadstate->thread = JSD_CURRENT_THREAD(); JS_INIT_CLIST(&jsdthreadstate->stack); jsdthreadstate->stackDepth = 0; JS_BeginRequest(jsdthreadstate->context); while( NULL != (fp = JS_FrameIterator(cx, &iter)) ) { JSScript* script = JS_GetFrameScript(cx, fp); jsuword pc = (jsuword) JS_GetFramePC(cx, fp); /* * don't construct a JSDStackFrame for dummy frames (those without a * |this| object, or native frames, if JSD_INCLUDE_NATIVE_FRAMES * isn't set. */ if (JS_GetFrameThis(cx, fp) && ((jsdc->flags & JSD_INCLUDE_NATIVE_FRAMES) || !JS_IsNativeFrame(cx, fp))) { JSDStackFrameInfo *frame; frame = _addNewFrame( jsdc, jsdthreadstate, script, pc, fp ); if ((jsdthreadstate->stackDepth == 0 && !frame) || (jsdthreadstate->stackDepth == 1 && frame && frame->jsdscript && !JSD_IS_DEBUG_ENABLED(jsdc, frame->jsdscript))) { /* * if we failed to create the first frame, or the top frame * is not enabled for debugging, fail the entire thread state. */ JS_INIT_CLIST(&jsdthreadstate->links); JS_EndRequest(jsdthreadstate->context); jsd_DestroyThreadState(jsdc, jsdthreadstate); return NULL; } } } JS_EndRequest(jsdthreadstate->context); if (jsdthreadstate->stackDepth == 0) { free(jsdthreadstate); return NULL; } JSD_LOCK_THREADSTATES(jsdc); JS_APPEND_LINK(&jsdthreadstate->links, &jsdc->threadsStates); JSD_UNLOCK_THREADSTATES(jsdc); return jsdthreadstate; }
static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp, char* buf, int num, JSBool showArgs, JSBool showLocals, JSBool showThisProps) { JSPropertyDescArray callProps = {0, nsnull}; JSPropertyDescArray thisProps = {0, nsnull}; JSBool gotThisVal = JS_FALSE; jsval thisVal; JSObject* callObj = nsnull; JSString* funname = nsnull; JSAutoByteString funbytes; const char* filename = nsnull; PRInt32 lineno = 0; JSFunction* fun = nsnull; uint32 namedArgCount = 0; jsval val; JSBool isString; // get the info for this stack frame JSScript* script = JS_GetFrameScript(cx, fp); jsbytecode* pc = JS_GetFramePC(cx, fp); JSAutoRequest ar(cx); JSAutoEnterCompartment ac; if(!ac.enter(cx, JS_GetFrameScopeChain(cx, fp))) return buf; if(script && pc) { filename = JS_GetScriptFilename(cx, script); lineno = (PRInt32) JS_PCToLineNumber(cx, script, pc); fun = JS_GetFrameFunction(cx, fp); if(fun) funname = JS_GetFunctionId(fun); if(showArgs || showLocals) { callObj = JS_GetFrameCallObject(cx, fp); if(callObj) if(!JS_GetPropertyDescArray(cx, callObj, &callProps)) callProps.array = nsnull; // just to be sure } gotThisVal = JS_GetFrameThis(cx, fp, &thisVal); if (!gotThisVal || !showThisProps || JSVAL_IS_PRIMITIVE(thisVal) || !JS_GetPropertyDescArray(cx, JSVAL_TO_OBJECT(thisVal), &thisProps)) { thisProps.array = nsnull; // just to be sure } } // print the frame number and function name if(funname) buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encode(cx, funname)); else if(fun) buf = JS_sprintf_append(buf, "%d anonymous(", num); else buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num); if(!buf) goto out; // print the function arguments if(showArgs && callObj) { for(uint32 i = 0; i < callProps.length; i++) { JSPropertyDesc* desc = &callProps.array[i]; if(desc->flags & JSPD_ARGUMENT) { JSAutoByteString nameBytes; const char* name = JSVAL2String(cx, desc->id, &isString, &nameBytes); if(!isString) name = nsnull; JSAutoByteString valueBytes; const char* value = JSVAL2String(cx, desc->value, &isString, &valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", namedArgCount ? ", " : "", name ? name :"", name ? " = " : "", isString ? "\"" : "", value ? value : "?unknown?", isString ? "\"" : ""); if(!buf) goto out; namedArgCount++; } } // print any unnamed trailing args (found in 'arguments' object) if(JS_GetProperty(cx, callObj, "arguments", &val) && JSVAL_IS_OBJECT(val)) { uint32 argCount; JSObject* argsObj = JSVAL_TO_OBJECT(val); if(JS_GetProperty(cx, argsObj, "length", &val) && JS_ValueToECMAUint32(cx, val, &argCount) && argCount > namedArgCount) { for(uint32 k = namedArgCount; k < argCount; k++) { char number[8]; JS_snprintf(number, 8, "%d", (int) k); if(JS_GetProperty(cx, argsObj, number, &val)) { JSAutoByteString valueBytes; const char *value = JSVAL2String(cx, val, &isString, &valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s", k ? ", " : "", isString ? "\"" : "", value ? value : "?unknown?", isString ? "\"" : ""); if(!buf) goto out; } } } } } // print filename and line number buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", fun ? ")" : "", filename ? filename : "<unknown>", lineno); if(!buf) goto out; // print local variables if(showLocals && callProps.array) { for(uint32 i = 0; i < callProps.length; i++) { JSPropertyDesc* desc = &callProps.array[i]; if(desc->flags & JSPD_VARIABLE) { JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = JSVAL2String(cx, desc->id, nsnull, &nameBytes); const char *value = JSVAL2String(cx, desc->value, &isString, &valueBytes); if(name && value) { buf = JS_sprintf_append(buf, TAB "%s = %s%s%s\n", name, isString ? "\"" : "", value, isString ? "\"" : ""); if(!buf) goto out; } } } } // print the value of 'this' if(showLocals) { if(gotThisVal) { JSString* thisValStr; JSAutoByteString thisValBytes; if(nsnull != (thisValStr = JS_ValueToString(cx, thisVal)) && thisValBytes.encode(cx, thisValStr)) { buf = JS_sprintf_append(buf, TAB "this = %s\n", thisValBytes.ptr()); if(!buf) goto out; } } else buf = JS_sprintf_append(buf, TAB "<failed to get 'this' value>\n"); } // print the properties of 'this', if it is an object if(showThisProps && thisProps.array) { for(uint32 i = 0; i < thisProps.length; i++) { JSPropertyDesc* desc = &thisProps.array[i]; if(desc->flags & JSPD_ENUMERATE) { JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = JSVAL2String(cx, desc->id, nsnull, &nameBytes); const char *value = JSVAL2String(cx, desc->value, &isString, &valueBytes); if(name && value) { buf = JS_sprintf_append(buf, TAB "this.%s = %s%s%s\n", name, isString ? "\"" : "", value, isString ? "\"" : ""); if(!buf) goto out; } } } } out: if(callProps.array) JS_PutPropertyDescArray(cx, &callProps); if(thisProps.array) JS_PutPropertyDescArray(cx, &thisProps); return buf; }
JSBool _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before, uintN type, JSD_CallHookProc hook, void *hookData) { JSDScript* jsdscript; JSScript* jsscript; JSBool hookresult = JS_TRUE; if (!jsdc || !jsdc->inited) return JS_FALSE; if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA) && jsdc->flags & JSD_DISABLE_OBJECT_TRACE) { /* no hook to call, no profile data needs to be collected, and * the client has object tracing disabled, so there is nothing * to do here. */ return hookresult; } if (before && JS_IsConstructorFrame(cx, fp)) jsd_Constructing(jsdc, cx, JS_GetFrameThis(cx, fp), fp); jsscript = JS_GetFrameScript(cx, fp); if (jsscript) { JSD_LOCK_SCRIPTS(jsdc); jsdscript = jsd_FindJSDScript(jsdc, jsscript); JSD_UNLOCK_SCRIPTS(jsdc); if (jsdscript) { if (JSD_IS_PROFILE_ENABLED(jsdc, jsdscript)) { JSDProfileData *pdata; pdata = jsd_GetScriptProfileData (jsdc, jsdscript); if (pdata) { if (before) { if (JSLL_IS_ZERO(pdata->lastCallStart)) { int64 now; JSDProfileData *callerpdata; /* Get the time just the once, for consistency. */ now = JS_Now(); /* This contains a pointer to the profile data for * the caller of this function. */ callerpdata = jsdc->callingFunctionPData; if (callerpdata) { int64 ll_delta; pdata->caller = callerpdata; /* We need to 'stop' the timer for the caller. * Use time since last return if appropriate. */ if (JSLL_IS_ZERO(jsdc->lastReturnTime)) { JSLL_SUB(ll_delta, now, callerpdata->lastCallStart); } else { JSLL_SUB(ll_delta, now, jsdc->lastReturnTime); } JSLL_ADD(callerpdata->runningTime, callerpdata->runningTime, ll_delta); } /* We're the new current function, and no return * has happened yet. */ jsdc->callingFunctionPData = pdata; jsdc->lastReturnTime = JSLL_ZERO; /* This function has no running time (just been * called!), and we'll need the call start time. */ pdata->runningTime = JSLL_ZERO; pdata->lastCallStart = now; } else { if (++pdata->recurseDepth > pdata->maxRecurseDepth) pdata->maxRecurseDepth = pdata->recurseDepth; } /* make sure we're called for the return too. */ hookresult = JS_TRUE; } else if (!pdata->recurseDepth && !JSLL_IS_ZERO(pdata->lastCallStart)) { int64 now, ll_delta; jsdouble delta; now = JS_Now(); JSLL_SUB(ll_delta, now, pdata->lastCallStart); JSLL_L2D(delta, ll_delta); delta /= 1000.0; pdata->totalExecutionTime += delta; /* minExecutionTime starts as 0, so we need to overwrite * it on the first call always. */ if ((0 == pdata->callCount) || delta < pdata->minExecutionTime) { pdata->minExecutionTime = delta; } if (delta > pdata->maxExecutionTime) pdata->maxExecutionTime = delta; /* If we last returned from a function (as opposed to * having last entered this function), we need to inc. * the running total by the time delta since the last * return, and use the running total instead of the * delta calculated above. */ if (!JSLL_IS_ZERO(jsdc->lastReturnTime)) { /* Add last chunk to running time, and use total * running time as 'delta'. */ JSLL_SUB(ll_delta, now, jsdc->lastReturnTime); JSLL_ADD(pdata->runningTime, pdata->runningTime, ll_delta); JSLL_L2D(delta, pdata->runningTime); delta /= 1000.0; } pdata->totalOwnExecutionTime += delta; /* See minExecutionTime comment above. */ if ((0 == pdata->callCount) || delta < pdata->minOwnExecutionTime) { pdata->minOwnExecutionTime = delta; } if (delta > pdata->maxOwnExecutionTime) pdata->maxOwnExecutionTime = delta; /* Current function is now our caller. */ jsdc->callingFunctionPData = pdata->caller; /* No hanging pointers, please. */ pdata->caller = NULL; /* Mark the time we returned, and indicate this * function is no longer running. */ jsdc->lastReturnTime = now; pdata->lastCallStart = JSLL_ZERO; ++pdata->callCount; } else if (pdata->recurseDepth) { --pdata->recurseDepth; ++pdata->callCount; } } if (hook) jsd_CallCallHook (jsdc, cx, type, hook, hookData); } else { if (hook) hookresult = jsd_CallCallHook (jsdc, cx, type, hook, hookData); else hookresult = JS_TRUE; } } } #ifdef JSD_TRACE _interpreterTrace(jsdc, cx, fp, before); return JS_TRUE; #else return hookresult; #endif }