1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/jsd/jsd_step.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,286 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * JavaScript Debugging support - Stepping support 1.12 + */ 1.13 + 1.14 +#include "jsd.h" 1.15 + 1.16 +/* 1.17 +* #define JSD_TRACE 1 1.18 +*/ 1.19 + 1.20 +#ifdef JSD_TRACE 1.21 + 1.22 +static char* 1.23 +_indentSpaces(int i) 1.24 +{ 1.25 +#define MAX_INDENT 63 1.26 + static char* p = nullptr; 1.27 + if(!p) 1.28 + { 1.29 + p = calloc(1, MAX_INDENT+1); 1.30 + if(!p) return ""; 1.31 + memset(p, ' ', MAX_INDENT); 1.32 + } 1.33 + if(i > MAX_INDENT) return p; 1.34 + return p + MAX_INDENT-i; 1.35 +} 1.36 + 1.37 +static void 1.38 +_interpreterTrace(JSDContext* jsdc, JSContext *cx, JSAbstractFramePtr frame, 1.39 + bool isConstructing, bool before) 1.40 +{ 1.41 + JSDScript* jsdscript = nullptr; 1.42 + JSScript * script; 1.43 + static indent = 0; 1.44 + JSString* funName = nullptr; 1.45 + 1.46 + script = frame.script(); 1.47 + if(script) 1.48 + { 1.49 + JSD_LOCK_SCRIPTS(jsdc); 1.50 + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, script, frame); 1.51 + JSD_UNLOCK_SCRIPTS(jsdc); 1.52 + if(jsdscript) 1.53 + funName = JSD_GetScriptFunctionId(jsdc, jsdscript); 1.54 + } 1.55 + 1.56 + if(before) 1.57 + printf("%sentering ", _indentSpaces(indent++)); 1.58 + else 1.59 + printf("%sleaving ", _indentSpaces(--indent)); 1.60 + 1.61 + if (!funName) 1.62 + printf("TOP_LEVEL"); 1.63 + else 1.64 + JS_FileEscapedString(stdout, funName, 0); 1.65 + 1.66 + if(before) 1.67 + { 1.68 + jsval thisVal; 1.69 + 1.70 + printf("%s this: ", isConstructing ? "constructing":""); 1.71 + 1.72 + if (JS_GetFrameThis(cx, frame, &thisVal)) 1.73 + printf("0x%0llx", (uintptr_t) thisVal); 1.74 + else 1.75 + puts("<unavailable>"); 1.76 + } 1.77 + printf("\n"); 1.78 + MOZ_ASSERT(indent >= 0); 1.79 +} 1.80 +#endif 1.81 + 1.82 +bool 1.83 +_callHook(JSDContext *jsdc, JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, 1.84 + bool before, unsigned type, JSD_CallHookProc hook, void *hookData) 1.85 +{ 1.86 + JSDScript* jsdscript; 1.87 + JSScript* jsscript; 1.88 + bool hookresult = true; 1.89 + 1.90 + if (!jsdc || !jsdc->inited) 1.91 + return false; 1.92 + 1.93 + if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA)) 1.94 + { 1.95 + /* no hook to call, no profile data needs to be collected, 1.96 + * so there is nothing to do here. 1.97 + */ 1.98 + return hookresult; 1.99 + } 1.100 + 1.101 + if (before && isConstructing) { 1.102 + JS::RootedValue newObj(cx); 1.103 + if (!frame.getThisValue(cx, &newObj)) 1.104 + return false; 1.105 + jsd_Constructing(jsdc, cx, JSVAL_TO_OBJECT(newObj), frame); 1.106 + } 1.107 + 1.108 + jsscript = frame.script(); 1.109 + if (jsscript) 1.110 + { 1.111 + JSD_LOCK_SCRIPTS(jsdc); 1.112 + jsdscript = jsd_FindOrCreateJSDScript(jsdc, cx, jsscript, frame); 1.113 + JSD_UNLOCK_SCRIPTS(jsdc); 1.114 + 1.115 + if (jsdscript) 1.116 + { 1.117 + if (JSD_IS_PROFILE_ENABLED(jsdc, jsdscript)) 1.118 + { 1.119 + JSDProfileData *pdata; 1.120 + pdata = jsd_GetScriptProfileData (jsdc, jsdscript); 1.121 + if (pdata) 1.122 + { 1.123 + if (before) 1.124 + { 1.125 + if (!pdata->lastCallStart) 1.126 + { 1.127 + int64_t now; 1.128 + JSDProfileData *callerpdata; 1.129 + 1.130 + /* Get the time just the once, for consistency. */ 1.131 + now = JS_Now(); 1.132 + /* This contains a pointer to the profile data for 1.133 + * the caller of this function. */ 1.134 + callerpdata = jsdc->callingFunctionPData; 1.135 + if (callerpdata) 1.136 + { 1.137 + int64_t ll_delta; 1.138 + pdata->caller = callerpdata; 1.139 + /* We need to 'stop' the timer for the caller. 1.140 + * Use time since last return if appropriate. */ 1.141 + ll_delta = jsdc->lastReturnTime 1.142 + ? now - jsdc->lastReturnTime 1.143 + : now - callerpdata->lastCallStart; 1.144 + callerpdata->runningTime += ll_delta; 1.145 + } 1.146 + /* We're the new current function, and no return 1.147 + * has happened yet. */ 1.148 + jsdc->callingFunctionPData = pdata; 1.149 + jsdc->lastReturnTime = 0; 1.150 + /* This function has no running time (just been 1.151 + * called!), and we'll need the call start time. */ 1.152 + pdata->runningTime = 0; 1.153 + pdata->lastCallStart = now; 1.154 + } else { 1.155 + if (++pdata->recurseDepth > pdata->maxRecurseDepth) 1.156 + pdata->maxRecurseDepth = pdata->recurseDepth; 1.157 + } 1.158 + /* make sure we're called for the return too. */ 1.159 + hookresult = true; 1.160 + } else if (!pdata->recurseDepth && pdata->lastCallStart) { 1.161 + int64_t now, ll_delta; 1.162 + double delta; 1.163 + now = JS_Now(); 1.164 + ll_delta = now - pdata->lastCallStart; 1.165 + delta = ll_delta; 1.166 + delta /= 1000.0; 1.167 + pdata->totalExecutionTime += delta; 1.168 + /* minExecutionTime starts as 0, so we need to overwrite 1.169 + * it on the first call always. */ 1.170 + if ((0 == pdata->callCount) || 1.171 + delta < pdata->minExecutionTime) 1.172 + { 1.173 + pdata->minExecutionTime = delta; 1.174 + } 1.175 + if (delta > pdata->maxExecutionTime) 1.176 + pdata->maxExecutionTime = delta; 1.177 + 1.178 + /* If we last returned from a function (as opposed to 1.179 + * having last entered this function), we need to inc. 1.180 + * the running total by the time delta since the last 1.181 + * return, and use the running total instead of the 1.182 + * delta calculated above. */ 1.183 + if (jsdc->lastReturnTime) 1.184 + { 1.185 + /* Add last chunk to running time, and use total 1.186 + * running time as 'delta'. */ 1.187 + ll_delta = now - jsdc->lastReturnTime; 1.188 + pdata->runningTime += ll_delta; 1.189 + delta = pdata->runningTime; 1.190 + delta /= 1000.0; 1.191 + } 1.192 + 1.193 + pdata->totalOwnExecutionTime += delta; 1.194 + /* See minExecutionTime comment above. */ 1.195 + if ((0 == pdata->callCount) || 1.196 + delta < pdata->minOwnExecutionTime) 1.197 + { 1.198 + pdata->minOwnExecutionTime = delta; 1.199 + } 1.200 + if (delta > pdata->maxOwnExecutionTime) 1.201 + pdata->maxOwnExecutionTime = delta; 1.202 + 1.203 + /* Current function is now our caller. */ 1.204 + jsdc->callingFunctionPData = pdata->caller; 1.205 + /* No hanging pointers, please. */ 1.206 + pdata->caller = nullptr; 1.207 + /* Mark the time we returned, and indicate this 1.208 + * function is no longer running. */ 1.209 + jsdc->lastReturnTime = now; 1.210 + pdata->lastCallStart = 0; 1.211 + ++pdata->callCount; 1.212 + } else if (pdata->recurseDepth) { 1.213 + --pdata->recurseDepth; 1.214 + ++pdata->callCount; 1.215 + } 1.216 + } 1.217 + if (hook) 1.218 + jsd_CallCallHook (jsdc, cx, type, hook, hookData); 1.219 + } else { 1.220 + if (hook) 1.221 + hookresult = 1.222 + jsd_CallCallHook (jsdc, cx, type, hook, hookData); 1.223 + else 1.224 + hookresult = true; 1.225 + } 1.226 + } 1.227 + } 1.228 + 1.229 +#ifdef JSD_TRACE 1.230 + _interpreterTrace(jsdc, cx, frame, isConstructing, before); 1.231 + return true; 1.232 +#else 1.233 + return hookresult; 1.234 +#endif 1.235 + 1.236 +} 1.237 + 1.238 +void * 1.239 +jsd_FunctionCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, 1.240 + bool before, bool *ok, void *closure) 1.241 +{ 1.242 + JSDContext* jsdc; 1.243 + JSD_CallHookProc hook; 1.244 + void* hookData; 1.245 + 1.246 + jsdc = (JSDContext*) closure; 1.247 + 1.248 + /* local in case jsdc->functionHook gets cleared on another thread */ 1.249 + JSD_LOCK(); 1.250 + hook = jsdc->functionHook; 1.251 + hookData = jsdc->functionHookData; 1.252 + JSD_UNLOCK(); 1.253 + 1.254 + if (_callHook (jsdc, cx, frame, isConstructing, before, 1.255 + (before) ? JSD_HOOK_FUNCTION_CALL : JSD_HOOK_FUNCTION_RETURN, 1.256 + hook, hookData)) 1.257 + { 1.258 + return closure; 1.259 + } 1.260 + 1.261 + return nullptr; 1.262 +} 1.263 + 1.264 +void * 1.265 +jsd_TopLevelCallHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, 1.266 + bool before, bool *ok, void *closure) 1.267 +{ 1.268 + JSDContext* jsdc; 1.269 + JSD_CallHookProc hook; 1.270 + void* hookData; 1.271 + 1.272 + jsdc = (JSDContext*) closure; 1.273 + 1.274 + /* local in case jsdc->toplevelHook gets cleared on another thread */ 1.275 + JSD_LOCK(); 1.276 + hook = jsdc->toplevelHook; 1.277 + hookData = jsdc->toplevelHookData; 1.278 + JSD_UNLOCK(); 1.279 + 1.280 + if (_callHook (jsdc, cx, frame, isConstructing, before, 1.281 + (before) ? JSD_HOOK_TOPLEVEL_START : JSD_HOOK_TOPLEVEL_END, 1.282 + hook, hookData)) 1.283 + { 1.284 + return closure; 1.285 + } 1.286 + 1.287 + return nullptr; 1.288 + 1.289 +}