Back to index

lightning-sunbird  0.9+nobinonly
jsd_step.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * JavaScript Debugging support - Stepping support
00040  */
00041 
00042 #include "jsd.h"
00043 
00044 /*
00045 * #define JSD_TRACE 1
00046 */
00047 
00048 #ifdef JSD_TRACE
00049 
00050 static char*
00051 _indentSpaces(int i)
00052 {
00053 #define MAX_INDENT 63
00054     static char* p = NULL;
00055     if(!p)
00056     {
00057         p = calloc(1, MAX_INDENT+1);
00058         if(!p) return "";
00059         memset(p, ' ', MAX_INDENT);
00060     }
00061     if(i > MAX_INDENT) return p;
00062     return p + MAX_INDENT-i;
00063 }
00064 
00065 static void
00066 _interpreterTrace(JSDContext* jsdc, JSContext *cx, JSStackFrame *fp,
00067                   JSBool before)
00068 {
00069     JSDScript* jsdscript = NULL;
00070     JSScript * script;
00071     static indent = 0;
00072     char* buf;
00073     const char* funName = NULL;
00074 
00075     script = JS_GetFrameScript(cx, fp);
00076     if(script)
00077     {
00078         JSD_LOCK_SCRIPTS(jsdc);
00079         jsdscript = jsd_FindJSDScript(jsdc, script);
00080         JSD_UNLOCK_SCRIPTS(jsdc);
00081         if(jsdscript)
00082             funName = JSD_GetScriptFunctionName(jsdc, jsdscript);
00083     }
00084     if(!funName)
00085         funName = "TOP_LEVEL";
00086 
00087     if(before)
00088     {
00089         buf = JS_smprintf("%sentering %s %s this: %0x\n",
00090                 _indentSpaces(indent++),
00091                 funName,
00092                 JS_IsConstructorFrame(cx, fp) ? "constructing":"",
00093                 (int)JS_GetFrameThis(cx, fp));
00094     }
00095     else
00096     {
00097         buf = JS_smprintf("%sleaving %s\n",
00098                 _indentSpaces(--indent),
00099                 funName);
00100     }
00101     JS_ASSERT(indent >= 0);
00102 
00103     if(!buf)
00104         return;
00105 
00106     printf(buf);
00107     free(buf);
00108 }
00109 #endif
00110 
00111 JSBool
00112 _callHook(JSDContext *jsdc, JSContext *cx, JSStackFrame *fp, JSBool before,
00113           uintN type, JSD_CallHookProc hook, void *hookData)
00114 {
00115     JSDScript*        jsdscript;
00116     JSScript*         jsscript;
00117     JSBool            hookresult = JS_TRUE;
00118     
00119     if (!jsdc || !jsdc->inited)
00120         return JS_FALSE;
00121 
00122     if (!hook && !(jsdc->flags & JSD_COLLECT_PROFILE_DATA) &&
00123         jsdc->flags & JSD_DISABLE_OBJECT_TRACE)
00124     {
00125         /* no hook to call, no profile data needs to be collected, and
00126          * the client has object tracing disabled, so there is nothing
00127          * to do here.
00128          */
00129         return hookresult;
00130     }
00131     
00132     if (before && JS_IsConstructorFrame(cx, fp))
00133         jsd_Constructing(jsdc, cx, JS_GetFrameThis(cx, fp), fp);
00134 
00135     jsscript = JS_GetFrameScript(cx, fp);
00136     if (jsscript)
00137     {
00138         JSD_LOCK_SCRIPTS(jsdc);
00139         jsdscript = jsd_FindJSDScript(jsdc, jsscript);
00140         JSD_UNLOCK_SCRIPTS(jsdc);
00141     
00142         if (jsdscript)
00143         {
00144             if (JSD_IS_PROFILE_ENABLED(jsdc, jsdscript))
00145             {
00146                 JSDProfileData *pdata;
00147                 pdata = jsd_GetScriptProfileData (jsdc, jsdscript);
00148                 if (pdata)
00149                 {
00150                     if (before)
00151                     {
00152                         if (JSLL_IS_ZERO(pdata->lastCallStart))
00153                         {
00154                             int64 now;
00155                             JSDProfileData *callerpdata;
00156                             
00157                             /* Get the time just the once, for consistency. */
00158                             now = JS_Now();
00159                             /* This contains a pointer to the profile data for
00160                              * the caller of this function. */
00161                             callerpdata = jsdc->callingFunctionPData;
00162                             if (callerpdata)
00163                             {
00164                                 int64 ll_delta;
00165                                 pdata->caller = callerpdata;
00166                                 /* We need to 'stop' the timer for the caller.
00167                                  * Use time since last return if appropriate. */
00168                                 if (JSLL_IS_ZERO(jsdc->lastReturnTime))
00169                                 {
00170                                     JSLL_SUB(ll_delta, now, callerpdata->lastCallStart);
00171                                 } else {
00172                                     JSLL_SUB(ll_delta, now, jsdc->lastReturnTime);
00173                                 }
00174                                 JSLL_ADD(callerpdata->runningTime, callerpdata->runningTime, ll_delta);
00175                             }
00176                             /* We're the new current function, and no return
00177                              * has happened yet. */
00178                             jsdc->callingFunctionPData = pdata;
00179                             jsdc->lastReturnTime = JSLL_ZERO;
00180                             /* This function has no running time (just been
00181                              * called!), and we'll need the call start time. */
00182                             pdata->runningTime = JSLL_ZERO;
00183                             pdata->lastCallStart = now;
00184                         } else {
00185                             if (++pdata->recurseDepth > pdata->maxRecurseDepth)
00186                                 pdata->maxRecurseDepth = pdata->recurseDepth;
00187                         }
00188                         /* make sure we're called for the return too. */
00189                         hookresult = JS_TRUE;
00190                     } else if (!pdata->recurseDepth &&
00191                                !JSLL_IS_ZERO(pdata->lastCallStart)) {
00192                         int64 now, ll_delta;
00193                         jsdouble delta;
00194                         now = JS_Now();
00195                         JSLL_SUB(ll_delta, now, pdata->lastCallStart);
00196                         JSLL_L2D(delta, ll_delta);
00197                         delta /= 1000.0;
00198                         pdata->totalExecutionTime += delta;
00199                         /* minExecutionTime starts as 0, so we need to overwrite
00200                          * it on the first call always. */
00201                         if ((0 == pdata->callCount) ||
00202                             delta < pdata->minExecutionTime)
00203                         {
00204                             pdata->minExecutionTime = delta;
00205                         }
00206                         if (delta > pdata->maxExecutionTime)
00207                             pdata->maxExecutionTime = delta;
00208                         
00209                         /* If we last returned from a function (as opposed to
00210                          * having last entered this function), we need to inc.
00211                          * the running total by the time delta since the last
00212                          * return, and use the running total instead of the
00213                          * delta calculated above. */
00214                         if (!JSLL_IS_ZERO(jsdc->lastReturnTime))
00215                         {
00216                             /* Add last chunk to running time, and use total
00217                              * running time as 'delta'. */
00218                             JSLL_SUB(ll_delta, now, jsdc->lastReturnTime);
00219                             JSLL_ADD(pdata->runningTime, pdata->runningTime, ll_delta);
00220                             JSLL_L2D(delta, pdata->runningTime);
00221                             delta /= 1000.0;
00222                         }
00223                         
00224                         pdata->totalOwnExecutionTime += delta;
00225                         /* See minExecutionTime comment above. */
00226                         if ((0 == pdata->callCount) ||
00227                             delta < pdata->minOwnExecutionTime)
00228                         {
00229                             pdata->minOwnExecutionTime = delta;
00230                         }
00231                         if (delta > pdata->maxOwnExecutionTime)
00232                             pdata->maxOwnExecutionTime = delta;
00233                         
00234                         /* Current function is now our caller. */
00235                         jsdc->callingFunctionPData = pdata->caller;
00236                         /* No hanging pointers, please. */
00237                         pdata->caller = NULL;
00238                         /* Mark the time we returned, and indicate this
00239                          * function is no longer running. */
00240                         jsdc->lastReturnTime = now;
00241                         pdata->lastCallStart = JSLL_ZERO;
00242                         ++pdata->callCount;
00243                     } else if (pdata->recurseDepth) {
00244                         --pdata->recurseDepth;
00245                         ++pdata->callCount;
00246                     }
00247                 }
00248                 if (hook)
00249                     jsd_CallCallHook (jsdc, cx, type, hook, hookData);
00250             } else {
00251                 if (hook)
00252                     hookresult = 
00253                         jsd_CallCallHook (jsdc, cx, type, hook, hookData);
00254                 else
00255                     hookresult = JS_TRUE;
00256             }
00257         }
00258     }
00259 
00260 #ifdef JSD_TRACE
00261     _interpreterTrace(jsdc, cx, fp, before);
00262     return JS_TRUE;
00263 #else
00264     return hookresult;
00265 #endif
00266 
00267 }
00268 
00269 void * JS_DLL_CALLBACK
00270 jsd_FunctionCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
00271                      JSBool *ok, void *closure)
00272 {
00273     JSDContext*       jsdc;
00274     JSD_CallHookProc  hook;
00275     void*             hookData;
00276 
00277     jsdc = (JSDContext*) closure;
00278     
00279     /* local in case jsdc->functionHook gets cleared on another thread */
00280     JSD_LOCK();
00281     hook     = jsdc->functionHook;
00282     hookData = jsdc->functionHookData;
00283     JSD_UNLOCK();
00284     
00285     if (_callHook (jsdc, cx, fp, before,
00286                    (before) ? JSD_HOOK_FUNCTION_CALL : JSD_HOOK_FUNCTION_RETURN,
00287                    hook, hookData))
00288     {
00289         return closure;
00290     }
00291     
00292     return NULL;
00293 }
00294 
00295 void * JS_DLL_CALLBACK
00296 jsd_TopLevelCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
00297                      JSBool *ok, void *closure)
00298 {
00299     JSDContext*       jsdc;
00300     JSD_CallHookProc  hook;
00301     void*             hookData;
00302 
00303     jsdc = (JSDContext*) closure;
00304 
00305     /* local in case jsdc->toplevelHook gets cleared on another thread */
00306     JSD_LOCK();
00307     hook     = jsdc->toplevelHook;
00308     hookData = jsdc->toplevelHookData;
00309     JSD_UNLOCK();
00310     
00311     if (_callHook (jsdc, cx, fp, before,
00312                    (before) ? JSD_HOOK_TOPLEVEL_START : JSD_HOOK_TOPLEVEL_END,
00313                    hook, hookData))
00314     {
00315         return closure;
00316     }
00317     
00318     return NULL;
00319     
00320 }