LCOV - code coverage report
Current view: directory - js/xpconnect/src - XPCDebug.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 220 127 57.7 %
Date: 2012-06-02 Functions: 14 7 50.0 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  *
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla Communicator client code, released
      17                 :  * March 31, 1998.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Netscape Communications Corporation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 1999
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *   John Bandhauer <jband@netscape.com> (original author)
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : #include "xpcprivate.h"
      42                 : 
      43                 : #ifdef TAB
      44                 : #undef TAB
      45                 : #endif
      46                 : #define TAB "    "
      47                 : 
      48             617 : static const char* JSVAL2String(JSContext* cx, jsval val, JSBool* isString,
      49                 :                                 JSAutoByteString *bytes)
      50                 : {
      51            1234 :     JSAutoRequest ar(cx);
      52             617 :     const char* value = nsnull;
      53             617 :     JSString* value_str = JS_ValueToString(cx, val);
      54             617 :     if (value_str)
      55             617 :         value = bytes->encode(cx, value_str);
      56             617 :     if (value) {
      57             617 :         const char* found = strstr(value, "function ");
      58             617 :         if (found && (value == found || value+1 == found || value+2 == found))
      59              37 :             value = "[function]";
      60                 :     }
      61                 : 
      62             617 :     if (isString)
      63             562 :         *isString = JSVAL_IS_STRING(val);
      64             617 :     return value;
      65                 : }
      66                 : 
      67             445 : static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp,
      68                 :                            char* buf, int num,
      69                 :                            JSBool showArgs, JSBool showLocals, JSBool showThisProps)
      70                 : {
      71             445 :     JSPropertyDescArray callProps = {0, nsnull};
      72             445 :     JSPropertyDescArray thisProps = {0, nsnull};
      73             445 :     JSBool gotThisVal = false;
      74                 :     jsval thisVal;
      75             445 :     JSObject* callObj = nsnull;
      76             445 :     JSString* funname = nsnull;
      77             890 :     JSAutoByteString funbytes;
      78             445 :     const char* filename = nsnull;
      79             445 :     PRInt32 lineno = 0;
      80             445 :     JSFunction* fun = nsnull;
      81             445 :     uint32_t namedArgCount = 0;
      82                 :     jsval val;
      83                 :     JSBool isString;
      84                 : 
      85                 :     // get the info for this stack frame
      86                 : 
      87             445 :     JSScript* script = JS_GetFrameScript(cx, fp);
      88             445 :     jsbytecode* pc = JS_GetFramePC(cx, fp);
      89                 : 
      90             890 :     JSAutoRequest ar(cx);
      91             890 :     JSAutoEnterCompartment ac;
      92             445 :     if (!ac.enter(cx, JS_GetGlobalForFrame(fp)))
      93               0 :         return buf;
      94                 : 
      95             445 :     if (script && pc) {
      96             396 :         filename = JS_GetScriptFilename(cx, script);
      97             396 :         lineno =  (PRInt32) JS_PCToLineNumber(cx, script, pc);
      98             396 :         fun = JS_GetFrameFunction(cx, fp);
      99             396 :         if (fun)
     100             344 :             funname = JS_GetFunctionId(fun);
     101                 : 
     102             396 :         if (showArgs || showLocals) {
     103             396 :             callObj = JS_GetFrameCallObject(cx, fp);
     104             396 :             if (callObj)
     105             344 :                 if (!JS_GetPropertyDescArray(cx, callObj, &callProps))
     106               0 :                     callProps.array = nsnull;  // just to be sure
     107                 :         }
     108                 : 
     109             396 :         gotThisVal = JS_GetFrameThis(cx, fp, &thisVal);
     110             396 :         if (!gotThisVal ||
     111                 :             !showThisProps ||
     112               0 :             JSVAL_IS_PRIMITIVE(thisVal) ||
     113                 :             !JS_GetPropertyDescArray(cx, JSVAL_TO_OBJECT(thisVal),
     114               0 :                                      &thisProps)) {
     115             396 :             thisProps.array = nsnull;  // just to be sure
     116                 :         }
     117                 :     }
     118                 : 
     119                 :     // print the frame number and function name
     120                 : 
     121             445 :     if (funname)
     122             141 :         buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encode(cx, funname));
     123             304 :     else if (fun)
     124             203 :         buf = JS_sprintf_append(buf, "%d anonymous(", num);
     125                 :     else
     126             101 :         buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
     127             445 :     if (!buf) goto out;
     128                 : 
     129                 :     // print the function arguments
     130                 : 
     131             445 :     if (showArgs && callObj) {
     132             875 :         for (uint32_t i = 0; i < callProps.length; i++) {
     133             531 :             JSPropertyDesc* desc = &callProps.array[i];
     134             531 :             if (desc->flags & JSPD_ARGUMENT) {
     135             506 :                 JSAutoByteString nameBytes;
     136             253 :                 const char* name = JSVAL2String(cx, desc->id, &isString, &nameBytes);
     137             253 :                 if (!isString)
     138               0 :                     name = nsnull;
     139             506 :                 JSAutoByteString valueBytes;
     140             253 :                 const char* value = JSVAL2String(cx, desc->value, &isString, &valueBytes);
     141                 : 
     142                 :                 buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
     143                 :                                         namedArgCount ? ", " : "",
     144                 :                                         name ? name :"",
     145                 :                                         name ? " = " : "",
     146                 :                                         isString ? "\"" : "",
     147                 :                                         value ? value : "?unknown?",
     148             253 :                                         isString ? "\"" : "");
     149             253 :                 if (!buf) goto out;
     150             506 :                 namedArgCount++;
     151                 :             }
     152                 :         }
     153                 : 
     154                 :         // print any unnamed trailing args (found in 'arguments' object)
     155                 : 
     156             688 :         if (JS_GetProperty(cx, callObj, "arguments", &val) &&
     157             344 :             JSVAL_IS_OBJECT(val)) {
     158                 :             uint32_t argCount;
     159             344 :             JSObject* argsObj = JSVAL_TO_OBJECT(val);
     160             688 :             if (JS_GetProperty(cx, argsObj, "length", &val) &&
     161             344 :                 JS_ValueToECMAUint32(cx, val, &argCount) &&
     162                 :                 argCount > namedArgCount) {
     163               2 :                 for (uint32_t k = namedArgCount; k < argCount; k++) {
     164                 :                     char number[8];
     165               1 :                     JS_snprintf(number, 8, "%d", (int) k);
     166                 : 
     167               1 :                     if (JS_GetProperty(cx, argsObj, number, &val)) {
     168               2 :                         JSAutoByteString valueBytes;
     169               1 :                         const char *value = JSVAL2String(cx, val, &isString, &valueBytes);
     170                 :                         buf = JS_sprintf_append(buf, "%s%s%s%s",
     171                 :                                                 k ? ", " : "",
     172                 :                                                 isString ? "\"" : "",
     173                 :                                                 value ? value : "?unknown?",
     174               1 :                                                 isString ? "\"" : "");
     175               1 :                         if (!buf) goto out;
     176                 :                     }
     177                 :                 }
     178                 :             }
     179                 :         }
     180                 :     }
     181                 : 
     182                 :     // print filename and line number
     183                 : 
     184                 :     buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
     185                 :                             fun ? ")" : "",
     186                 :                             filename ? filename : "<unknown>",
     187             445 :                             lineno);
     188             445 :     if (!buf) goto out;
     189                 : 
     190                 :     // print local variables
     191                 : 
     192             445 :     if (showLocals && callProps.array) {
     193             820 :         for (uint32_t i = 0; i < callProps.length; i++) {
     194             531 :             JSPropertyDesc* desc = &callProps.array[i];
     195             531 :             if (desc->flags & JSPD_VARIABLE) {
     196             110 :                 JSAutoByteString nameBytes;
     197             110 :                 JSAutoByteString valueBytes;
     198              55 :                 const char *name = JSVAL2String(cx, desc->id, nsnull, &nameBytes);
     199              55 :                 const char *value = JSVAL2String(cx, desc->value, &isString, &valueBytes);
     200                 : 
     201              55 :                 if (name && value) {
     202                 :                     buf = JS_sprintf_append(buf, TAB "%s = %s%s%s\n",
     203                 :                                             name,
     204                 :                                             isString ? "\"" : "",
     205                 :                                             value,
     206              55 :                                             isString ? "\"" : "");
     207              55 :                     if (!buf) goto out;
     208                 :                 }
     209                 :             }
     210                 :         }
     211                 :     }
     212                 : 
     213                 :     // print the value of 'this'
     214                 : 
     215             445 :     if (showLocals) {
     216             445 :         if (gotThisVal) {
     217                 :             JSString* thisValStr;
     218             792 :             JSAutoByteString thisValBytes;
     219                 : 
     220             792 :             if (nsnull != (thisValStr = JS_ValueToString(cx, thisVal)) &&
     221             396 :                 thisValBytes.encode(cx, thisValStr)) {
     222             396 :                 buf = JS_sprintf_append(buf, TAB "this = %s\n", thisValBytes.ptr());
     223             396 :                 if (!buf) goto out;
     224                 :             }
     225                 :         } else
     226              49 :             buf = JS_sprintf_append(buf, TAB "<failed to get 'this' value>\n");
     227                 :     }
     228                 : 
     229                 :     // print the properties of 'this', if it is an object
     230                 : 
     231             445 :     if (showThisProps && thisProps.array) {
     232                 : 
     233               0 :         for (uint32_t i = 0; i < thisProps.length; i++) {
     234               0 :             JSPropertyDesc* desc = &thisProps.array[i];
     235               0 :             if (desc->flags & JSPD_ENUMERATE) {
     236               0 :                 JSAutoByteString nameBytes;
     237               0 :                 JSAutoByteString valueBytes;
     238               0 :                 const char *name = JSVAL2String(cx, desc->id, nsnull, &nameBytes);
     239               0 :                 const char *value = JSVAL2String(cx, desc->value, &isString, &valueBytes);
     240               0 :                 if (name && value) {
     241                 :                     buf = JS_sprintf_append(buf, TAB "this.%s = %s%s%s\n",
     242                 :                                             name,
     243                 :                                             isString ? "\"" : "",
     244                 :                                             value,
     245               0 :                                             isString ? "\"" : "");
     246               0 :                     if (!buf) goto out;
     247                 :                 }
     248                 :             }
     249                 :         }
     250                 :     }
     251                 : 
     252                 : out:
     253             445 :     if (callProps.array)
     254             289 :         JS_PutPropertyDescArray(cx, &callProps);
     255             445 :     if (thisProps.array)
     256               0 :         JS_PutPropertyDescArray(cx, &thisProps);
     257             445 :     return buf;
     258                 : }
     259                 : 
     260              49 : static char* FormatJSStackDump(JSContext* cx, char* buf,
     261                 :                                JSBool showArgs, JSBool showLocals,
     262                 :                                JSBool showThisProps)
     263                 : {
     264                 :     JSStackFrame* fp;
     265              49 :     JSStackFrame* iter = nsnull;
     266              49 :     int num = 0;
     267                 : 
     268             543 :     while (nsnull != (fp = JS_FrameIterator(cx, &iter))) {
     269             445 :         buf = FormatJSFrame(cx, fp, buf, num, showArgs, showLocals, showThisProps);
     270             445 :         num++;
     271                 :     }
     272                 : 
     273              49 :     if (!num)
     274               0 :         buf = JS_sprintf_append(buf, "JavaScript stack is empty\n");
     275                 : 
     276              49 :     return buf;
     277                 : }
     278                 : 
     279                 : JSBool
     280              49 : xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, JSBool showThisProps)
     281                 : {
     282              49 :     if (char* buf = xpc_PrintJSStack(cx, showArgs, showLocals, showThisProps)) {
     283              49 :         fputs(buf, stdout);
     284              49 :         JS_smprintf_free(buf);
     285                 :     }
     286              49 :     return true;
     287                 : }
     288                 : 
     289                 : char*
     290              49 : xpc_PrintJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals,
     291                 :                  JSBool showThisProps)
     292                 : {
     293                 :     char* buf;
     294              49 :     JSExceptionState *state = JS_SaveExceptionState(cx);
     295              49 :     if (!state)
     296               0 :         puts("Call to a debug function modifying state!");
     297                 : 
     298              49 :     JS_ClearPendingException(cx);
     299                 : 
     300              49 :     buf = FormatJSStackDump(cx, nsnull, showArgs, showLocals, showThisProps);
     301              49 :     if (!buf)
     302               0 :         puts("Failed to format JavaScript stack for dump");
     303                 : 
     304              49 :     JS_RestoreExceptionState(cx, state);
     305              49 :     return buf;
     306                 : }
     307                 : 
     308                 : /***************************************************************************/
     309                 : 
     310                 : static void
     311               0 : xpcDumpEvalErrorReporter(JSContext *cx, const char *message,
     312                 :                          JSErrorReport *report)
     313                 : {
     314               0 :     printf("Error: %s\n", message);
     315               0 : }
     316                 : 
     317                 : JSBool
     318               0 : xpc_DumpEvalInJSStackFrame(JSContext* cx, uint32_t frameno, const char* text)
     319                 : {
     320                 :     JSStackFrame* fp;
     321               0 :     JSStackFrame* iter = nsnull;
     322               0 :     uint32_t num = 0;
     323                 : 
     324               0 :     if (!cx || !text) {
     325               0 :         puts("invalid params passed to xpc_DumpEvalInJSStackFrame!");
     326               0 :         return false;
     327                 :     }
     328                 : 
     329               0 :     printf("js[%d]> %s\n", frameno, text);
     330                 : 
     331               0 :     while (nsnull != (fp = JS_FrameIterator(cx, &iter))) {
     332               0 :         if (num == frameno)
     333               0 :             break;
     334               0 :         num++;
     335                 :     }
     336                 : 
     337               0 :     if (!fp) {
     338               0 :         puts("invalid frame number!");
     339               0 :         return false;
     340                 :     }
     341                 : 
     342               0 :     JSAutoRequest ar(cx);
     343                 : 
     344               0 :     JSExceptionState* exceptionState = JS_SaveExceptionState(cx);
     345               0 :     JSErrorReporter older = JS_SetErrorReporter(cx, xpcDumpEvalErrorReporter);
     346                 : 
     347                 :     jsval rval;
     348                 :     JSString* str;
     349               0 :     JSAutoByteString bytes;
     350               0 :     if (JS_EvaluateInStackFrame(cx, fp, text, strlen(text), "eval", 1, &rval) &&
     351                 :         nsnull != (str = JS_ValueToString(cx, rval)) &&
     352               0 :         bytes.encode(cx, str)) {
     353               0 :         printf("%s\n", bytes.ptr());
     354                 :     } else
     355               0 :         puts("eval failed!");
     356               0 :     JS_SetErrorReporter(cx, older);
     357               0 :     JS_RestoreExceptionState(cx, exceptionState);
     358               0 :     return true;
     359                 : }
     360                 : 
     361                 : /***************************************************************************/
     362                 : 
     363                 : JSTrapStatus
     364              49 : xpc_DebuggerKeywordHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
     365                 :                            jsval *rval, void *closure)
     366                 : {
     367                 :     static const char line[] =
     368                 :     "------------------------------------------------------------------------";
     369              49 :     puts(line);
     370              49 :     puts("Hit JavaScript \"debugger\" keyword. JS call stack...");
     371              49 :     xpc_DumpJSStack(cx, true, true, false);
     372              49 :     puts(line);
     373              49 :     return JSTRAP_CONTINUE;
     374                 : }
     375                 : 
     376            1404 : JSBool xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt)
     377                 : {
     378            1404 :     return JS_SetDebuggerHandler(rt, xpc_DebuggerKeywordHandler, nsnull);
     379                 : }
     380                 : 
     381                 : /***************************************************************************/
     382                 : 
     383                 : // The following will dump info about an object to stdout...
     384                 : 
     385                 : 
     386                 : // Quick and dirty (debug only damnit!) class to track which JSObjects have
     387                 : // been visited as we traverse.
     388                 : 
     389                 : class ObjectPile
     390                 : {
     391                 : public:
     392                 :     enum result {primary, seen, overflow};
     393                 : 
     394               0 :     result Visit(JSObject* obj)
     395                 :     {
     396               0 :         if (member_count == max_count)
     397               0 :             return overflow;
     398               0 :         for (int i = 0; i < member_count; i++)
     399               0 :             if (array[i] == obj)
     400               0 :                 return seen;
     401               0 :         array[member_count++] = obj;
     402               0 :         return primary;
     403                 :     }
     404                 : 
     405               0 :     ObjectPile() : member_count(0){}
     406                 : 
     407                 : private:
     408                 :     enum {max_count = 50};
     409                 :     JSObject* array[max_count];
     410                 :     int member_count;
     411                 : };
     412                 : 
     413                 : 
     414                 : static const int tab_width = 2;
     415                 : #define INDENT(_d) (_d)*tab_width, " "
     416                 : 
     417               0 : static void PrintObjectBasics(JSObject* obj)
     418                 : {
     419               0 :     if (JS_IsNative(obj))
     420                 :         printf("%p 'native' <%s>",
     421               0 :                (void *)obj, js::GetObjectClass(obj)->name);
     422                 :     else
     423               0 :         printf("%p 'host'", (void *)obj);
     424               0 : }
     425                 : 
     426               0 : static void PrintObject(JSObject* obj, int depth, ObjectPile* pile)
     427                 : {
     428               0 :     PrintObjectBasics(obj);
     429                 : 
     430               0 :     switch (pile->Visit(obj)) {
     431                 :     case ObjectPile::primary:
     432               0 :         puts("");
     433               0 :         break;
     434                 :     case ObjectPile::seen:
     435               0 :         puts(" (SEE ABOVE)");
     436               0 :         return;
     437                 :     case ObjectPile::overflow:
     438               0 :         puts(" (TOO MANY OBJECTS)");
     439               0 :         return;
     440                 :     }
     441                 : 
     442               0 :     if (!JS_IsNative(obj))
     443               0 :         return;
     444                 : 
     445               0 :     JSObject* parent = js::GetObjectParent(obj);
     446               0 :     JSObject* proto  = js::GetObjectProto(obj);
     447                 : 
     448               0 :     printf("%*sparent: ", INDENT(depth+1));
     449               0 :     if (parent)
     450               0 :         PrintObject(parent, depth+1, pile);
     451                 :     else
     452               0 :         puts("null");
     453               0 :     printf("%*sproto: ", INDENT(depth+1));
     454               0 :     if (proto)
     455               0 :         PrintObject(proto, depth+1, pile);
     456                 :     else
     457               0 :         puts("null");
     458                 : }
     459                 : 
     460                 : JSBool
     461               0 : xpc_DumpJSObject(JSObject* obj)
     462                 : {
     463               0 :     ObjectPile pile;
     464                 : 
     465               0 :     puts("Debugging reminders...");
     466               0 :     puts("  class:  (JSClass*)(obj->fslots[2]-1)");
     467               0 :     puts("  parent: (JSObject*)(obj->fslots[1])");
     468               0 :     puts("  proto:  (JSObject*)(obj->fslots[0])");
     469               0 :     puts("");
     470                 : 
     471               0 :     if (obj)
     472               0 :         PrintObject(obj, 0, &pile);
     473                 :     else
     474               0 :         puts("xpc_DumpJSObject passed null!");
     475                 : 
     476               0 :     return true;
     477                 : }

Generated by: LCOV version 1.7