Back to index

lightning-sunbird  0.9+nobinonly
xpcdebug.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1999
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   John Bandhauer <jband@netscape.com> (original author)
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "xpcprivate.h"
00042 #if defined(DEBUG_xpc_hacker) || defined(DEBUG)
00043 
00044 #ifdef TAB
00045 #undef TAB
00046 #endif
00047 #define TAB "    "
00048 
00049 static const char* JSVAL2String(JSContext* cx, jsval val, JSBool* isString)
00050 {
00051     const char* value = nsnull;
00052     JSString* value_str = JS_ValueToString(cx, val);
00053     if(value_str)
00054         value = JS_GetStringBytes(value_str);
00055     if(value)
00056     {
00057         const char* found = strstr(value, "function ");
00058         if(found && (value == found || value+1 == found || value+2 == found))
00059             value = "[function]";
00060     }
00061 
00062     if(isString)
00063         *isString = JSVAL_IS_STRING(val);
00064     return value;
00065 }
00066 
00067 static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp,
00068                            char* buf, int num,
00069                            JSBool showArgs, JSBool showLocals, JSBool showThisProps)
00070 {
00071     if(JS_IsNativeFrame(cx, fp))
00072         return JS_sprintf_append(buf, "%d [native frame]\n", num);
00073 
00074     JSPropertyDescArray callProps = {0, nsnull};
00075     JSPropertyDescArray thisProps = {0, nsnull};
00076     JSObject* thisObj = nsnull;
00077     JSObject* callObj = nsnull;
00078     const char* funname = nsnull;
00079     const char* filename = nsnull;
00080     PRInt32 lineno = 0;
00081     JSFunction* fun = nsnull;
00082     uint32 namedArgCount = 0;
00083     jsval val;
00084     const char* name;
00085     const char* value;
00086     JSBool isString;
00087 
00088     // get the info for this stack frame
00089 
00090     JSScript* script = JS_GetFrameScript(cx, fp);
00091     jsbytecode* pc = JS_GetFramePC(cx, fp);
00092     if(script && pc)
00093     {
00094         filename = JS_GetScriptFilename(cx, script);
00095         lineno =  (PRInt32) JS_PCToLineNumber(cx, script, pc);
00096         fun = JS_GetFrameFunction(cx, fp);
00097         if(fun)
00098             funname = JS_GetFunctionName(fun);
00099 
00100         if(showArgs || showLocals)
00101         {
00102             callObj = JS_GetFrameCallObject(cx, fp);
00103             if(callObj)
00104                 if(!JS_GetPropertyDescArray(cx, callObj, &callProps))
00105                     callProps.array = nsnull;  // just to be sure
00106         }
00107 
00108         thisObj = JS_GetFrameThis(cx, fp);
00109         if(showThisProps)
00110         {
00111             if(thisObj)
00112                 if(!JS_GetPropertyDescArray(cx, thisObj, &thisProps))
00113                     thisProps.array = nsnull;  // just to be sure
00114         }
00115     }
00116 
00117     // print the frame number and function name
00118 
00119     if(funname)
00120         buf = JS_sprintf_append(buf, "%d %s(", num, funname);
00121     else if(fun)
00122         buf = JS_sprintf_append(buf, "%d anonymous(", num);
00123     else
00124         buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
00125     if(!buf) goto out;
00126 
00127     // print the function arguments
00128 
00129     if(showArgs && callObj)
00130     {
00131         for(uint32 i = 0; i < callProps.length; i++)
00132         {
00133             JSPropertyDesc* desc = &callProps.array[i];
00134             if(desc->flags & JSPD_ARGUMENT)
00135             {
00136                 name = JSVAL2String(cx, desc->id, &isString);
00137                 if(!isString)
00138                     name = nsnull;
00139                 value = JSVAL2String(cx, desc->value, &isString);
00140 
00141                 buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
00142                                         namedArgCount ? ", " : "",
00143                                         name ? name :"",
00144                                         name ? " = " : "",
00145                                         isString ? "\"" : "",
00146                                         value ? value : "?unknown?",
00147                                         isString ? "\"" : "");
00148                 if(!buf) goto out;
00149                 namedArgCount++;
00150             }
00151         }
00152 
00153         // print any unnamed trailing args (found in 'arguments' object)
00154 
00155         if(JS_GetProperty(cx, callObj, "arguments", &val) &&
00156            JSVAL_IS_OBJECT(val))
00157         {
00158             uint32 argCount;
00159             JSObject* argsObj = JSVAL_TO_OBJECT(val);
00160             if(JS_GetProperty(cx, argsObj, "length", &val) &&
00161                JS_ValueToECMAUint32(cx, val, &argCount) &&
00162                argCount > namedArgCount)
00163             {
00164                 for(uint32 k = namedArgCount; k < argCount; k++)
00165                 {
00166                     char number[8];
00167                     JS_snprintf(number, 8, "%d", (int) k);
00168 
00169                     if(JS_GetProperty(cx, argsObj, number, &val))
00170                     {
00171                         value = JSVAL2String(cx, val, &isString);
00172                         buf = JS_sprintf_append(buf, "%s%s%s%s",
00173                                         k ? ", " : "",
00174                                         isString ? "\"" : "",
00175                                         value ? value : "?unknown?",
00176                                         isString ? "\"" : "");
00177                         if(!buf) goto out;
00178                     }
00179                 }
00180             }
00181         }
00182     }
00183 
00184     // print filename and line number
00185 
00186     buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
00187                             fun ? ")" : "",
00188                             filename ? filename : "<unknown>",
00189                             lineno);
00190     if(!buf) goto out;
00191 
00192     // print local variables
00193 
00194     if(showLocals && callProps.array)
00195     {
00196         for(uint32 i = 0; i < callProps.length; i++)
00197         {
00198             JSPropertyDesc* desc = &callProps.array[i];
00199             if(desc->flags & JSPD_VARIABLE)
00200             {
00201                 name = JSVAL2String(cx, desc->id, nsnull);
00202                 value = JSVAL2String(cx, desc->value, &isString);
00203 
00204                 if(name && value)
00205                 {
00206                     buf = JS_sprintf_append(buf, TAB "%s = %s%s%s\n",
00207                                             name,
00208                                             isString ? "\"" : "",
00209                                             value,
00210                                             isString ? "\"" : "");
00211                     if(!buf) goto out;
00212                 }
00213             }
00214         }
00215     }
00216 
00217     // print the value of 'this'
00218 
00219     if(showLocals && thisObj)
00220     {
00221         jsval thisJSVal = OBJECT_TO_JSVAL(thisObj);
00222         JSString* thisValStr;
00223         char* thisVal;
00224 
00225         if(nsnull != (thisValStr = JS_ValueToString(cx, thisJSVal)) &&
00226            nsnull != (thisVal = JS_GetStringBytes(thisValStr)))
00227         {
00228             buf = JS_sprintf_append(buf, TAB "this = %s\n", thisVal);
00229             if(!buf) goto out;
00230         }
00231     }
00232 
00233     // print the properties of 'this'
00234 
00235     if(showThisProps && thisProps.array)
00236     {
00237 
00238         for(uint32 i = 0; i < thisProps.length; i++)
00239         {
00240             JSPropertyDesc* desc = &thisProps.array[i];
00241             if(desc->flags & JSPD_ENUMERATE)
00242             {
00243 
00244                 name = JSVAL2String(cx, desc->id, nsnull);
00245                 value = JSVAL2String(cx, desc->value, &isString);
00246                 if(name && value)
00247                 {
00248                     buf = JS_sprintf_append(buf, TAB "this.%s = %s%s%s\n",
00249                                             name,
00250                                             isString ? "\"" : "",
00251                                             value,
00252                                             isString ? "\"" : "");
00253                     if(!buf) goto out;
00254                 }
00255             }
00256         }
00257     }
00258 
00259 out:
00260     if(callProps.array)
00261         JS_PutPropertyDescArray(cx, &callProps);
00262     if(thisProps.array)
00263         JS_PutPropertyDescArray(cx, &thisProps);
00264     return buf;
00265 }
00266 
00267 static char* FormatJSStackDump(JSContext* cx, char* buf,
00268                                JSBool showArgs, JSBool showLocals,
00269                                JSBool showThisProps)
00270 {
00271     JSStackFrame* fp;
00272     JSStackFrame* iter = nsnull;
00273     int num = 0;
00274 
00275     while(nsnull != (fp = JS_FrameIterator(cx, &iter)))
00276     {
00277         buf = FormatJSFrame(cx, fp, buf, num, showArgs, showLocals, showThisProps);
00278         num++;
00279     }
00280 
00281     if(!num)
00282         buf = JS_sprintf_append(buf, "JavaScript stack is empty\n");
00283 
00284     return buf;
00285 }
00286 
00287 JSBool
00288 xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, JSBool showThisProps)
00289 {
00290     char* buf;
00291 
00292     buf = FormatJSStackDump(cx, nsnull, showArgs, showLocals, showThisProps);
00293     if(buf)
00294     {
00295         fputs(buf, stdout);
00296         JS_smprintf_free(buf);
00297     }
00298     else
00299         puts("Failed to format JavaScript stack for dump");
00300     return JS_TRUE;
00301 }
00302 
00303 /***************************************************************************/
00304 
00305 JS_STATIC_DLL_CALLBACK(void)
00306 xpcDumpEvalErrorReporter(JSContext *cx, const char *message,
00307                          JSErrorReport *report)
00308 {
00309     printf("Error: %s\n", message);
00310 }
00311 
00312 JSBool
00313 xpc_DumpEvalInJSStackFrame(JSContext* cx, JSUint32 frameno, const char* text)
00314 {
00315     JSStackFrame* fp;
00316     JSStackFrame* iter = nsnull;
00317     JSUint32 num = 0;
00318 
00319     if(!cx || !text)
00320     {
00321         puts("invalid params passed to xpc_DumpEvalInJSStackFrame!");
00322         return JS_FALSE;
00323     }
00324 
00325     printf("js[%d]> %s\n", frameno, text);
00326 
00327     while(nsnull != (fp = JS_FrameIterator(cx, &iter)))
00328     {
00329         if(num == frameno)
00330             break;
00331         num++;
00332     }
00333 
00334     if(!fp)
00335     {
00336         puts("invalid frame number!");
00337         return JS_FALSE;
00338     }
00339 
00340     JSExceptionState* exceptionState = JS_SaveExceptionState(cx);
00341     JSErrorReporter older = JS_SetErrorReporter(cx, xpcDumpEvalErrorReporter);
00342 
00343     jsval rval;
00344     JSString* str;
00345     const char* chars;
00346     if(JS_EvaluateInStackFrame(cx, fp, text, strlen(text), "eval", 1, &rval) &&
00347        nsnull != (str = JS_ValueToString(cx, rval)) &&
00348        nsnull != (chars = JS_GetStringBytes(str)))
00349     {
00350         printf("%s\n", chars);
00351     }
00352     else
00353         puts("eval failed!");
00354     JS_SetErrorReporter(cx, older);
00355     JS_RestoreExceptionState(cx, exceptionState);
00356     return JS_TRUE;
00357 }
00358 
00359 /***************************************************************************/
00360 
00361 JSTrapStatus JS_DLL_CALLBACK
00362 xpc_DebuggerKeywordHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
00363                            jsval *rval, void *closure)
00364 {
00365     static const char line[] =
00366     "------------------------------------------------------------------------";
00367     puts(line);
00368     puts("Hit JavaScript \"debugger\" keyword. JS call stack...");
00369     xpc_DumpJSStack(cx, JS_TRUE, JS_TRUE, JS_FALSE);
00370     puts(line);
00371     return JSTRAP_CONTINUE;
00372 }
00373 
00374 JSBool xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt)
00375 {
00376     return JS_SetDebuggerHandler(rt, xpc_DebuggerKeywordHandler, nsnull);
00377 }
00378 
00379 /***************************************************************************/
00380 
00381 // The following will dump info about an object to stdout...
00382 
00383 
00384 // Quick and dirty (debug only damnit!) class to track which JSObjects have
00385 // been visited as we traverse.
00386 
00387 class ObjectPile
00388 {
00389 public:
00390     enum result {primary, seen, overflow};
00391 
00392     result Visit(JSObject* obj)
00393     {
00394         if(member_count == max_count)
00395             return overflow;
00396         for(int i = 0; i < member_count; i++)
00397             if(array[i] == obj)
00398                 return seen;
00399         array[member_count++] = obj;
00400         return primary;
00401     }
00402 
00403     ObjectPile() : member_count(0){}
00404 
00405 private:
00406     enum {max_count = 50};
00407     JSObject* array[max_count];
00408     int member_count;
00409 };
00410 
00411 
00412 static const int tab_width = 2;
00413 #define INDENT(_d) (_d)*tab_width, " "
00414 
00415 static void PrintObjectBasics(JSObject* obj)
00416 {
00417     if(OBJ_IS_NATIVE(obj))
00418         printf("%p 'native' <%s>",
00419                (void *)obj,
00420                ((JSClass*)(obj->slots[JSSLOT_CLASS]-1))->name);
00421     else
00422         printf("%p 'host'", (void *)obj);
00423 
00424 }
00425 
00426 static void PrintObject(JSObject* obj, int depth, ObjectPile* pile)
00427 {
00428     PrintObjectBasics(obj);
00429 
00430     switch(pile->Visit(obj))
00431     {
00432     case ObjectPile::primary:
00433         puts("");
00434         break;
00435     case ObjectPile::seen:
00436         puts(" (SEE ABOVE)");
00437         return;
00438     case ObjectPile::overflow:
00439         puts(" (TOO MANY OBJECTS)");
00440         return;
00441     }
00442 
00443     if(!OBJ_IS_NATIVE(obj))
00444         return;
00445 
00446     JSObject* parent = (JSObject*)(obj->slots[JSSLOT_PARENT]);
00447     JSObject* proto  = (JSObject*)(obj->slots[JSSLOT_PROTO]);
00448 
00449     printf("%*sparent: ", INDENT(depth+1));
00450     if(parent)
00451         PrintObject(parent, depth+1, pile);
00452     else
00453         puts("null");
00454     printf("%*sproto: ", INDENT(depth+1));
00455     if(proto)
00456         PrintObject(proto, depth+1, pile);
00457     else
00458         puts("null");
00459 }
00460 
00461 JSBool
00462 xpc_DumpJSObject(JSObject* obj)
00463 {
00464     ObjectPile pile;
00465 
00466     puts("Debugging reminders...");
00467     puts("  class:  (JSClass*)(obj->slots[2]-1)");
00468     puts("  parent: (JSObject*)(obj->slots[1])");
00469     puts("  proto:  (JSObject*)(obj->slots[0])");
00470     puts("");
00471 
00472     if(obj)
00473         PrintObject(obj, 0, &pile);
00474     else
00475         puts("xpc_DumpJSObject passed null!");
00476 
00477     return JS_TRUE;
00478 }
00479 #endif