diff -r 000000000000 -r 6474c204b198 js/jsd/jsd_step.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/jsd/jsd_step.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,286 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * JavaScript Debugging support - Stepping support + */ + +#include "jsd.h" + +/* +* #define JSD_TRACE 1 +*/ + +#ifdef JSD_TRACE + +static char* +_indentSpaces(int i) +{ +#define MAX_INDENT 63 + static char* p = nullptr; + if(!p) + { + p = calloc(1, MAX_INDENT+1); + if(!p) return ""; + memset(p, ' ', MAX_INDENT); + } + if(i > MAX_INDENT) return p; + return p + MAX_INDENT-i; +} + +static void +_interpreterTrace(JSDContext* jsdc, JSContext *cx, JSAbstractFramePtr frame, + bool isConstructing, bool before) +{ + JSDScript* jsdscript = nullptr; + JSScript * script; + static indent = 0; + JSString* funName = nullptr; + + 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(""); + } + printf("\n"); + MOZ_ASSERT(indent >= 0); +} +#endif + +bool +_callHook(JSDContext *jsdc, JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, + bool before, unsigned type, JSD_CallHookProc hook, void *hookData) +{ + JSDScript* jsdscript; + JSScript* jsscript; + bool hookresult = true; + + if (!jsdc || !jsdc->inited) + return false; + + if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) + { + /* no hook to call, no profile data needs to be collected, + * so there is nothing to do here. + */ + return hookresult; + } + + if (before && isConstructing) { + JS::RootedValue newObj(cx); + if (!frame.getThisValue(cx, &newObj)) + return false; + jsd_Constructing(jsdc, cx, JSVAL_TO_OBJECT(newObj), frame); + } + + jsscript = frame.script(); + if (jsscript) + { + JSD_LOCK_SCRIPTS(jsdc); + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, jsscript, frame); + 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 (!pdata->lastCallStart) + { + int64_t 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_t ll_delta; + pdata->caller = callerpdata; + /* We need to 'stop' the timer for the caller. + * Use time since last return if appropriate. */ + ll_delta = jsdc->lastReturnTime + ? now - jsdc->lastReturnTime + : now - callerpdata->lastCallStart; + callerpdata->runningTime += ll_delta; + } + /* We're the new current function, and no return + * has happened yet. */ + jsdc->callingFunctionPData = pdata; + jsdc->lastReturnTime = 0; + /* This function has no running time (just been + * called!), and we'll need the call start time. */ + pdata->runningTime = 0; + pdata->lastCallStart = now; + } else { + if (++pdata->recurseDepth > pdata->maxRecurseDepth) + pdata->maxRecurseDepth = pdata->recurseDepth; + } + /* make sure we're called for the return too. */ + hookresult = true; + } else if (!pdata->recurseDepth && pdata->lastCallStart) { + int64_t now, ll_delta; + double delta; + now = JS_Now(); + ll_delta = now - pdata->lastCallStart; + 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 (jsdc->lastReturnTime) + { + /* Add last chunk to running time, and use total + * running time as 'delta'. */ + ll_delta = now - jsdc->lastReturnTime; + pdata->runningTime += ll_delta; + 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 = nullptr; + /* Mark the time we returned, and indicate this + * function is no longer running. */ + jsdc->lastReturnTime = now; + pdata->lastCallStart = 0; + ++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 = true; + } + } + } + +#ifdef JSD_TRACE + _interpreterTrace(jsdc, cx, frame, isConstructing, before); + return true; +#else + return hookresult; +#endif + +} + +void * +jsd_FunctionCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, + bool before, bool *ok, void *closure) +{ + JSDContext* jsdc; + JSD_CallHookProc hook; + void* hookData; + + jsdc = (JSDContext*) closure; + + /* local in case jsdc->functionHook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->functionHook; + hookData = jsdc->functionHookData; + JSD_UNLOCK(); + + if (_callHook (jsdc, cx, frame, isConstructing, before, + (before) ? JSD_HOOK_FUNCTION_CALL : JSD_HOOK_FUNCTION_RETURN, + hook, hookData)) + { + return closure; + } + + return nullptr; +} + +void * +jsd_TopLevelCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, + bool before, bool *ok, void *closure) +{ + JSDContext* jsdc; + JSD_CallHookProc hook; + void* hookData; + + jsdc = (JSDContext*) closure; + + /* local in case jsdc->toplevelHook gets cleared on another thread */ + JSD_LOCK(); + hook = jsdc->toplevelHook; + hookData = jsdc->toplevelHookData; + JSD_UNLOCK(); + + if (_callHook (jsdc, cx, frame, isConstructing, before, + (before) ? JSD_HOOK_TOPLEVEL_START : JSD_HOOK_TOPLEVEL_END, + hook, hookData)) + { + return closure; + } + + return nullptr; + +}