1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3
* ***** BEGIN LICENSE BLOCK *****
4
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
16
* The Original Code is Mozilla Communicator client code, released
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.
25
* John Bandhauer <jband@netscape.com> (original author)
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.
39
* ***** END LICENSE BLOCK ***** */
41
#include "xpcprivate.h"
42
#if defined(DEBUG_xpc_hacker) || defined(DEBUG)
49
static const char* JSVAL2String(JSContext* cx, jsval val, JSBool* isString)
51
const char* value = nsnull;
52
JSString* value_str = JS_ValueToString(cx, val);
54
value = JS_GetStringBytes(value_str);
57
const char* found = strstr(value, "function ");
58
if(found && (value == found || value+1 == found || value+2 == found))
63
*isString = JSVAL_IS_STRING(val);
67
static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp,
69
JSBool showArgs, JSBool showLocals, JSBool showThisProps)
71
if(JS_IsNativeFrame(cx, fp))
72
return JS_sprintf_append(buf, "%d [native frame]\n", num);
74
JSPropertyDescArray callProps = {0, nsnull};
75
JSPropertyDescArray thisProps = {0, nsnull};
76
JSObject* thisObj = nsnull;
77
JSObject* callObj = nsnull;
78
const char* funname = nsnull;
79
const char* filename = nsnull;
81
JSFunction* fun = nsnull;
82
uint32 namedArgCount = 0;
88
// get the info for this stack frame
90
JSScript* script = JS_GetFrameScript(cx, fp);
91
jsbytecode* pc = JS_GetFramePC(cx, fp);
94
filename = JS_GetScriptFilename(cx, script);
95
lineno = (PRInt32) JS_PCToLineNumber(cx, script, pc);
96
fun = JS_GetFrameFunction(cx, fp);
98
funname = JS_GetFunctionName(fun);
100
if(showArgs || showLocals)
102
callObj = JS_GetFrameCallObject(cx, fp);
104
if(!JS_GetPropertyDescArray(cx, callObj, &callProps))
105
callProps.array = nsnull; // just to be sure
108
thisObj = JS_GetFrameThis(cx, fp);
112
if(!JS_GetPropertyDescArray(cx, thisObj, &thisProps))
113
thisProps.array = nsnull; // just to be sure
117
// print the frame number and function name
120
buf = JS_sprintf_append(buf, "%d %s(", num, funname);
122
buf = JS_sprintf_append(buf, "%d anonymous(", num);
124
buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
127
// print the function arguments
129
if(showArgs && callObj)
131
for(uint32 i = 0; i < callProps.length; i++)
133
JSPropertyDesc* desc = &callProps.array[i];
134
if(desc->flags & JSPD_ARGUMENT)
136
name = JSVAL2String(cx, desc->id, &isString);
139
value = JSVAL2String(cx, desc->value, &isString);
141
buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
142
namedArgCount ? ", " : "",
145
isString ? "\"" : "",
146
value ? value : "?unknown?",
147
isString ? "\"" : "");
153
// print any unnamed trailing args (found in 'arguments' object)
155
if(JS_GetProperty(cx, callObj, "arguments", &val) &&
156
JSVAL_IS_OBJECT(val))
159
JSObject* argsObj = JSVAL_TO_OBJECT(val);
160
if(JS_GetProperty(cx, argsObj, "length", &val) &&
161
JS_ValueToECMAUint32(cx, val, &argCount) &&
162
argCount > namedArgCount)
164
for(uint32 k = namedArgCount; k < argCount; k++)
167
JS_snprintf(number, 8, "%d", (int) k);
169
if(JS_GetProperty(cx, argsObj, number, &val))
171
value = JSVAL2String(cx, val, &isString);
172
buf = JS_sprintf_append(buf, "%s%s%s%s",
174
isString ? "\"" : "",
175
value ? value : "?unknown?",
176
isString ? "\"" : "");
184
// print filename and line number
186
buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
188
filename ? filename : "<unknown>",
192
// print local variables
194
if(showLocals && callProps.array)
196
for(uint32 i = 0; i < callProps.length; i++)
198
JSPropertyDesc* desc = &callProps.array[i];
199
if(desc->flags & JSPD_VARIABLE)
201
name = JSVAL2String(cx, desc->id, nsnull);
202
value = JSVAL2String(cx, desc->value, &isString);
206
buf = JS_sprintf_append(buf, TAB "%s = %s%s%s\n",
208
isString ? "\"" : "",
210
isString ? "\"" : "");
217
// print the value of 'this'
219
if(showLocals && thisObj)
221
jsval thisJSVal = OBJECT_TO_JSVAL(thisObj);
222
JSString* thisValStr;
225
if(nsnull != (thisValStr = JS_ValueToString(cx, thisJSVal)) &&
226
nsnull != (thisVal = JS_GetStringBytes(thisValStr)))
228
buf = JS_sprintf_append(buf, TAB "this = %s\n", thisVal);
233
// print the properties of 'this'
235
if(showThisProps && thisProps.array)
238
for(uint32 i = 0; i < thisProps.length; i++)
240
JSPropertyDesc* desc = &thisProps.array[i];
241
if(desc->flags & JSPD_ENUMERATE)
244
name = JSVAL2String(cx, desc->id, nsnull);
245
value = JSVAL2String(cx, desc->value, &isString);
248
buf = JS_sprintf_append(buf, TAB "this.%s = %s%s%s\n",
250
isString ? "\"" : "",
252
isString ? "\"" : "");
261
JS_PutPropertyDescArray(cx, &callProps);
263
JS_PutPropertyDescArray(cx, &thisProps);
267
static char* FormatJSStackDump(JSContext* cx, char* buf,
268
JSBool showArgs, JSBool showLocals,
269
JSBool showThisProps)
272
JSStackFrame* iter = nsnull;
275
while(nsnull != (fp = JS_FrameIterator(cx, &iter)))
277
buf = FormatJSFrame(cx, fp, buf, num, showArgs, showLocals, showThisProps);
282
buf = JS_sprintf_append(buf, "JavaScript stack is empty\n");
288
xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, JSBool showThisProps)
292
buf = FormatJSStackDump(cx, nsnull, showArgs, showLocals, showThisProps);
296
JS_smprintf_free(buf);
299
printf("Failed to format JavaScript stack for dump\n");
303
/***************************************************************************/
305
JS_STATIC_DLL_CALLBACK(void)
306
xpcDumpEvalErrorReporter(JSContext *cx, const char *message,
307
JSErrorReport *report)
309
printf("Error: %s\n", message);
313
xpc_DumpEvalInJSStackFrame(JSContext* cx, JSUint32 frameno, const char* text)
316
JSStackFrame* iter = nsnull;
321
printf("invalid params passed to xpc_DumpEvalInJSStackFrame!\n");
325
printf("js[%d]> %s\n", frameno, text);
327
while(nsnull != (fp = JS_FrameIterator(cx, &iter)))
336
printf("invalid frame number!\n");
340
JSExceptionState* exceptionState = JS_SaveExceptionState(cx);
341
JSErrorReporter older = JS_SetErrorReporter(cx, xpcDumpEvalErrorReporter);
346
if(JS_EvaluateInStackFrame(cx, fp, text, strlen(text), "eval", 1, &rval) &&
347
nsnull != (str = JS_ValueToString(cx, rval)) &&
348
nsnull != (chars = JS_GetStringBytes(str)))
350
printf("%s\n", chars);
353
printf("eval failed!\n");
354
JS_SetErrorReporter(cx, older);
355
JS_RestoreExceptionState(cx, exceptionState);
359
/***************************************************************************/
361
JSTrapStatus JS_DLL_CALLBACK
362
xpc_DebuggerKeywordHandler(JSContext *cx, JSScript *script, jsbytecode *pc,
363
jsval *rval, void *closure)
365
static const char line[] =
366
"------------------------------------------------------------------------\n";
368
printf("Hit JavaScript \"debugger\" keyword. JS call stack...\n");
369
xpc_DumpJSStack(cx, JS_TRUE, JS_TRUE, JS_FALSE);
371
return JSTRAP_CONTINUE;
374
JSBool xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt)
376
return JS_SetDebuggerHandler(rt, xpc_DebuggerKeywordHandler, nsnull);
379
/***************************************************************************/
381
// The following will dump info about an object to stdout...
384
// Quick and dirty (debug only damnit!) class to track which JSObjects have
385
// been visited as we traverse.
390
enum result {primary, seen, overflow};
392
result Visit(JSObject* obj)
394
if(member_count == max_count)
396
for(int i = 0; i < member_count; i++)
399
array[member_count++] = obj;
403
ObjectPile() : member_count(0){}
406
enum {max_count = 50};
407
JSObject* array[max_count];
412
static const int tab_width = 2;
413
#define INDENT(_d) (_d)*tab_width, " "
415
static void PrintObjectBasics(JSObject* obj)
417
if(OBJ_IS_NATIVE(obj))
418
printf("%#p 'native' <%s>",
420
((JSClass*)(obj->slots[JSSLOT_CLASS]-1))->name);
422
printf("%#p 'host'", obj);
426
static void PrintObject(JSObject* obj, int depth, ObjectPile* pile)
428
PrintObjectBasics(obj);
430
switch(pile->Visit(obj))
432
case ObjectPile::primary:
435
case ObjectPile::seen:
436
printf(" (SEE ABOVE)\n");
438
case ObjectPile::overflow:
439
printf(" (TOO MANY OBJECTS)\n");
443
if(!OBJ_IS_NATIVE(obj))
446
JSObject* parent = (JSObject*)(obj->slots[JSSLOT_PARENT]);
447
JSObject* proto = (JSObject*)(obj->slots[JSSLOT_PROTO]);
449
printf("%*sparent: ", INDENT(depth+1));
451
PrintObject(parent, depth+1, pile);
454
printf("%*sproto: ", INDENT(depth+1));
456
PrintObject(proto, depth+1, pile);
462
xpc_DumpJSObject(JSObject* obj)
466
printf("Debugging reminders...\n");
467
printf(" class: (JSClass*)(obj->slots[2]-1)\n");
468
printf(" parent: (JSObject*)(obj->slots[1])\n");
469
printf(" proto: (JSObject*)(obj->slots[0])\n");
473
PrintObject(obj, 0, &pile);
475
printf("xpc_DumpJSObject passed null!\n");