/* -*- 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; }