From 321d0f862492f539c7b1a5b71416716369967265 Mon Sep 17 00:00:00 2001 From: "jband%netscape.com" Date: Tue, 1 Feb 2000 08:48:17 +0000 Subject: [PATCH] add improved support for dumping the JS stack from the native debugger or using the 'debugger' statement in JavaScript. Doc coming to the mozilla site soon. r=mccabe --- dom/src/build/nsDOMFactory.cpp | 14 +- js/src/xpconnect/idl/nsIXPConnect.idl | 6 +- js/src/xpconnect/src/Makefile.in | 1 + js/src/xpconnect/src/makefile.win | 1 + js/src/xpconnect/src/nsXPConnect.cpp | 124 +++--- js/src/xpconnect/src/xpcdebug.cpp | 374 ++++++++++++++++++ js/src/xpconnect/src/xpcjsruntime.cpp | 22 +- js/src/xpconnect/src/xpcprivate.h | 16 +- .../tests/components/xpctest_echo.cpp | 2 +- 9 files changed, 496 insertions(+), 64 deletions(-) create mode 100644 js/src/xpconnect/src/xpcdebug.cpp diff --git a/dom/src/build/nsDOMFactory.cpp b/dom/src/build/nsDOMFactory.cpp index c528f732166..2e7043b4423 100644 --- a/dom/src/build/nsDOMFactory.cpp +++ b/dom/src/build/nsDOMFactory.cpp @@ -717,7 +717,7 @@ void XXXDomNeverCalled() } #ifdef DEBUG -/* This is here to be callable from a debugger */ +/* These are here to be callable from a debugger */ #include "nsIServiceManager.h" #include "nsIXPConnect.h" JS_BEGIN_EXTERN_C @@ -726,7 +726,17 @@ void DumpJSStack() nsresult rv; NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); if(NS_SUCCEEDED(rv)) - xpc->DebugDumpJSStack(); + xpc->DebugDumpJSStack(PR_TRUE, PR_TRUE, PR_FALSE); + else + printf("failed to get XPConnect service!\n"); +} + +void DumpJSEval(PRUint32 frame, const char* text) +{ + nsresult rv; + NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); + if(NS_SUCCEEDED(rv)) + xpc->DebugDumpEvalInJSStackFrame(frame, text); else printf("failed to get XPConnect service!\n"); } diff --git a/js/src/xpconnect/idl/nsIXPConnect.idl b/js/src/xpconnect/idl/nsIXPConnect.idl index 0ee80603f8c..7ae43d4eb67 100644 --- a/js/src/xpconnect/idl/nsIXPConnect.idl +++ b/js/src/xpconnect/idl/nsIXPConnect.idl @@ -338,6 +338,10 @@ interface nsIXPConnect : nsISupports void debugDump(in short depth); void debugDumpObject(in nsISupports aCOMObj, in short depth); - void debugDumpJSStack(); + void debugDumpJSStack(in PRBool showArgs, + in PRBool showLocals, + in PRBool showThisProps); + void debugDumpEvalInJSStackFrame(in PRUint32 aFrameNumber, + in string aSourceText); }; diff --git a/js/src/xpconnect/src/Makefile.in b/js/src/xpconnect/src/Makefile.in index 63f5f028bed..d06a136b659 100644 --- a/js/src/xpconnect/src/Makefile.in +++ b/js/src/xpconnect/src/Makefile.in @@ -48,6 +48,7 @@ CPPSRCS = \ xpccomponents.cpp \ xpccontext.cpp \ xpcconvert.cpp \ + xpcdebug.cpp \ xpcexception.cpp \ xpcjsid.cpp \ xpcjsruntime.cpp \ diff --git a/js/src/xpconnect/src/makefile.win b/js/src/xpconnect/src/makefile.win index 9f7df511359..1abc0c1eac7 100644 --- a/js/src/xpconnect/src/makefile.win +++ b/js/src/xpconnect/src/makefile.win @@ -52,6 +52,7 @@ OBJS= \ .\$(OBJDIR)\xpccomponents.obj \ .\$(OBJDIR)\xpccontext.obj \ .\$(OBJDIR)\xpcconvert.obj \ + .\$(OBJDIR)\xpcdebug.obj \ .\$(OBJDIR)\xpcexception.obj \ .\$(OBJDIR)\xpcjsid.obj \ .\$(OBJDIR)\xpcjsruntime.obj \ diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 3aef78583e7..0a19c772ab3 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -725,6 +725,28 @@ nsXPConnect::SyncJSContexts(void) return NS_OK; } +/* void debugDump (in short depth); */ +NS_IMETHODIMP +nsXPConnect::DebugDump(PRInt16 depth) +{ +#ifdef DEBUG + depth-- ; + XPC_LOG_ALWAYS(("nsXPConnect @ %x with mRefCnt = %d", this, mRefCnt)); + XPC_LOG_INDENT(); + XPC_LOG_ALWAYS(("mArbitraryScriptable @ %x", mArbitraryScriptable)); + XPC_LOG_ALWAYS(("mInterfaceInfoManager @ %x", mInterfaceInfoManager)); + XPC_LOG_ALWAYS(("mContextStack @ %x", mContextStack)); + XPC_LOG_ALWAYS(("mThrower @ %x", mThrower)); + if(mRuntime) + mRuntime->DebugDump(depth); + else + XPC_LOG_ALWAYS(("mRuntime is null")); + nsXPCWrappedNativeScope::DebugDumpAllScopes(depth); + XPC_LOG_OUTDENT(); +#endif + return NS_OK; +} + /* void debugDumpObject (in nsISupports aCOMObj, in short depth); */ NS_IMETHODIMP nsXPConnect::DebugDumpObject(nsISupports *p, PRInt16 depth) @@ -785,76 +807,66 @@ nsXPConnect::DebugDumpObject(nsISupports *p, PRInt16 depth) return NS_OK; } -/* void debugDumpJSStack (); */ -NS_IMETHODIMP -nsXPConnect::DebugDumpJSStack(void) +/* void debugDumpJSStack (in PRBool showArgs, in PRBool showLocals, in PRBool showThisProps); */ +NS_IMETHODIMP +nsXPConnect::DebugDumpJSStack(PRBool showArgs, PRBool showLocals, PRBool showThisProps) { #ifdef DEBUG + JSContext* cx; + nsresult rv; + NS_WITH_SERVICE(nsIJSContextStack, stack, "nsThreadJSContextStack", &rv); + if(NS_FAILED(rv)) + printf("failed to get nsIJSContextStack service!\n"); + else if(NS_FAILED(stack->Peek(&cx))) + printf("failed to peek into nsIJSContextStack service!\n"); + else if(!cx) + printf("there is no JSContext on the nsIJSContextStack!\n"); + else + xpc_DumpJSStack(cx, showArgs, showLocals, showThisProps); +#endif + return NS_OK; +} - nsIJSStackFrameLocation* stack; - if(NS_FAILED(GetCurrentJSStack(&stack)) || !stack) - { - printf("call to GetCurrentJSStack failed\n"); - return NS_OK; - } - - nsIJSStackFrameLocation* current = stack; - NS_ADDREF(current); - - while(1) - { - char* text; - if(NS_FAILED(current->ToString(&text))) - { - printf("nsIJSStackFrameLocation::ToString failed!\n"); - NS_RELEASE(current); - break; - } - printf("%s\n", text); - nsAllocator::Free(text); - nsIJSStackFrameLocation* prev = current; - nsresult rv = prev->GetCaller(¤t); - NS_RELEASE(prev); - if(NS_FAILED(rv) || !current) - break; - } - NS_RELEASE(stack); -#endif - return NS_OK; -} - - -/* void debugDump (in short depth); */ -NS_IMETHODIMP -nsXPConnect::DebugDump(PRInt16 depth) -{ -#ifdef DEBUG - depth-- ; - XPC_LOG_ALWAYS(("nsXPConnect @ %x with mRefCnt = %d", this, mRefCnt)); - XPC_LOG_INDENT(); - XPC_LOG_ALWAYS(("mArbitraryScriptable @ %x", mArbitraryScriptable)); - XPC_LOG_ALWAYS(("mInterfaceInfoManager @ %x", mInterfaceInfoManager)); - XPC_LOG_ALWAYS(("mContextStack @ %x", mContextStack)); - XPC_LOG_ALWAYS(("mThrower @ %x", mThrower)); - if(mRuntime) - mRuntime->DebugDump(depth); - else - XPC_LOG_ALWAYS(("mRuntime is null")); - nsXPCWrappedNativeScope::DebugDumpAllScopes(depth); - XPC_LOG_OUTDENT(); +/* void debugDumpEvalInJSStackFrame (in PRUint32 aFrameNumber, in string aSourceText); */ +NS_IMETHODIMP +nsXPConnect::DebugDumpEvalInJSStackFrame(PRUint32 aFrameNumber, const char *aSourceText) +{ +#ifdef DEBUG + JSContext* cx; + nsresult rv; + NS_WITH_SERVICE(nsIJSContextStack, stack, "nsThreadJSContextStack", &rv); + if(NS_FAILED(rv)) + printf("failed to get nsIJSContextStack service!\n"); + else if(NS_FAILED(stack->Peek(&cx))) + printf("failed to peek into nsIJSContextStack service!\n"); + else if(!cx) + printf("there is no JSContext on the nsIJSContextStack!\n"); + else + xpc_DumpEvalInJSStackFrame(cx, aFrameNumber, aSourceText); #endif return NS_OK; } + #ifdef DEBUG -/* This is here to be callable from a debugger */ +/* These are here to be callable from a debugger */ JS_BEGIN_EXTERN_C void DumpJSStack() { nsresult rv; NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); if(NS_SUCCEEDED(rv)) - xpc->DebugDumpJSStack(); + xpc->DebugDumpJSStack(PR_TRUE, PR_TRUE, PR_FALSE); + else + printf("failed to get XPConnect service!\n"); +} + +void DumpJSEval(PRUint32 frameno, const char* text) +{ + nsresult rv; + NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); + if(NS_SUCCEEDED(rv)) + xpc->DebugDumpEvalInJSStackFrame(frameno, text); else printf("failed to get XPConnect service!\n"); } diff --git a/js/src/xpconnect/src/xpcdebug.cpp b/js/src/xpconnect/src/xpcdebug.cpp new file mode 100644 index 00000000000..7b79d2ef9ec --- /dev/null +++ b/js/src/xpconnect/src/xpcdebug.cpp @@ -0,0 +1,374 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1999 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * John Bandhauer + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU Public License (the "GPL"), in which case the + * provisions of the GPL are applicable instead of those above. + * If you wish to allow use of your version of this file only + * under the terms of the GPL and not to allow others to use your + * version of this file under the NPL, indicate your decision by + * deleting the provisions above and replace them with the notice + * and other provisions required by the GPL. If you do not delete + * the provisions above, a recipient may use your version of this + * file under either the NPL or the GPL. + */ + +#ifdef DEBUG +#include "xpcprivate.h" + +#ifdef TAB +#undef TAB +#endif +#define TAB " " + +static const char* JSVAL2String(JSContext* cx, jsval val, JSBool* isString) +{ + const char* value = nsnull; + JSString* value_str = JS_ValueToString(cx, val); + if(value_str) + value = JS_GetStringBytes(value_str); + if(value) + { + const char* found = strstr(value, "function "); + if(found && (value == found || value+1 == found || value+2 == found)) + value = "[function]"; + } + + if(isString) + *isString = JSVAL_IS_STRING(val); + return value; +} + +static char* FormatJSFrame(JSContext* cx, JSStackFrame* fp, + char* buf, int num, + JSBool showArgs, JSBool showLocals, JSBool showThisProps) +{ + if(JS_IsNativeFrame(cx, fp)) + return JS_sprintf_append(buf, "%d [native frame]\n", num); + + JSPropertyDescArray callProps = {0, nsnull}; + JSPropertyDescArray thisProps = {0, nsnull}; + JSObject* thisObj = nsnull; + JSObject* callObj = nsnull; + const char* funname = nsnull; + const char* filename = nsnull; + PRInt32 lineno = 0; + JSFunction* fun = nsnull; + uint32 namedArgCount = 0; + jsval val; + const char* name; + const char* value; + JSBool isString; + + // get the info for this stack frame + + JSScript* script = JS_GetFrameScript(cx, fp); + jsbytecode* pc = JS_GetFramePC(cx, fp); + if(script && pc) + { + filename = JS_GetScriptFilename(cx, script); + lineno = (PRInt32) JS_PCToLineNumber(cx, script, pc); + fun = JS_GetFrameFunction(cx, fp); + if(fun) + funname = JS_GetFunctionName(fun); + + if(showArgs || showLocals) + { + callObj = JS_GetFrameCallObject(cx, fp); + if(callObj) + if(!JS_GetPropertyDescArray(cx, callObj, &callProps)) + callProps.array = nsnull; // just to be sure + } + + thisObj = JS_GetFrameThis(cx, fp); + if(showThisProps) + { + if(thisObj) + if(!JS_GetPropertyDescArray(cx, thisObj, &thisProps)) + thisProps.array = nsnull; // just to be sure + } + } + + // print the frame number and function name + + if(funname) + buf = JS_sprintf_append(buf, "%d %s(", num, funname); + else if(fun) + buf = JS_sprintf_append(buf, "%d anonymous(", num); + else + buf = JS_sprintf_append(buf, "%d ", num); + if(!buf) goto out; + + // print the function arguments + + if(showArgs && callObj) + { + for(uint32 i = 0; i < callProps.length; i++) + { + JSPropertyDesc* desc = &callProps.array[i]; + if(desc->flags & JSPD_ARGUMENT) + { + name = JSVAL2String(cx, desc->id, &isString); + if(!isString) + name = nsnull; + value = JSVAL2String(cx, desc->value, &isString); + + buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", + namedArgCount ? ", " : "", + name ? name :"", + name ? " = " : "", + isString ? "\"" : "", + value ? value : "?unknown?", + isString ? "\"" : ""); + if(!buf) goto out; + namedArgCount++; + } + } + + // print any unnamed trailing args (found in 'arguments' object) + + if(JS_GetProperty(cx, callObj, "arguments", &val) && + JSVAL_IS_OBJECT(val)) + { + uint32 argCount; + JSObject* argsObj = JSVAL_TO_OBJECT(val); + if(JS_GetProperty(cx, argsObj, "length", &val) && + JS_ValueToECMAUint32(cx, val, &argCount) && + argCount > namedArgCount) + { + for(uint32 i = namedArgCount; i < argCount; i++) + { + char num[8]; + JS_snprintf(num, 8, "%d", (int) i); + + if(JS_GetProperty(cx, argsObj, num, &val)) + { + value = JSVAL2String(cx, val, &isString); + buf = JS_sprintf_append(buf, "%s%s%s%s", + i ? ", " : "", + isString ? "\"" : "", + value ? value : "?unknown?", + isString ? "\"" : ""); + if(!buf) goto out; + } + } + } + } + } + + // print filename and line number + + buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", + fun ? ")" : "", + filename ? filename : "", + lineno); + if(!buf) goto out; + + // print local variables + + if(showLocals && callProps.array) + { + for(uint32 i = 0; i < callProps.length; i++) + { + JSPropertyDesc* desc = &callProps.array[i]; + if(desc->flags & JSPD_VARIABLE) + { + name = JSVAL2String(cx, desc->id, nsnull); + value = JSVAL2String(cx, desc->value, &isString); + + if(name && value) + { + buf = JS_sprintf_append(buf, TAB "%s = %s%s%s\n", + name, + isString ? "\"" : "", + value, + isString ? "\"" : ""); + if(!buf) goto out; + } + } + } + } + + // print the value of 'this' + + if(showLocals && thisObj) + { + jsval thisJSVal = OBJECT_TO_JSVAL(thisObj); + JSString* thisValStr; + char* thisVal; + + if(nsnull != (thisValStr = JS_ValueToString(cx, thisJSVal)) && + nsnull != (thisVal = JS_GetStringBytes(thisValStr))) + { + buf = JS_sprintf_append(buf, TAB "this = %s\n", thisVal); + if(!buf) goto out; + } + } + + // print the properties of 'this' + + if(showThisProps && thisProps.array) + { + + for(uint32 i = 0; i < thisProps.length; i++) + { + JSPropertyDesc* desc = &thisProps.array[i]; + if(desc->flags & JSPD_ENUMERATE) + { + + name = JSVAL2String(cx, desc->id, nsnull); + value = JSVAL2String(cx, desc->value, &isString); + if(name && value) + { + buf = JS_sprintf_append(buf, TAB "this.%s = %s%s%s\n", + name, + isString ? "\"" : "", + value, + isString ? "\"" : ""); + if(!buf) goto out; + } + } + } + } + +out: + if(callProps.array) + JS_PutPropertyDescArray(cx, &callProps); + if(thisProps.array) + JS_PutPropertyDescArray(cx, &thisProps); + return buf; +} + +static char* FormatJSStackDump(JSContext* cx, char* buf, + JSBool showArgs, JSBool showLocals, + JSBool showThisProps) +{ + JSStackFrame* fp; + JSStackFrame* iter = nsnull; + int num = 0; + + while(nsnull != (fp = JS_FrameIterator(cx, &iter))) + { + buf = FormatJSFrame(cx, fp, buf, num, showArgs, showLocals, showThisProps); + num++; + } + + if(!num) + buf = JS_sprintf_append(buf, "JavaScript stack is empty\n"); + + return buf; +} + +JSBool +xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, JSBool showThisProps) +{ + char* buf; + + buf = FormatJSStackDump(cx, nsnull, showArgs, showLocals, showThisProps); + if(buf) + { + printf(buf); + JS_smprintf_free(buf); + } + else + printf("Failed to format JavaScript stack for dump\n"); + return JS_TRUE; +} + +/***************************************************************************/ + +JS_STATIC_DLL_CALLBACK(void) +xpcDumpEvalErrorReporter(JSContext *cx, const char *message, + JSErrorReport *report) +{ + printf("Error: %s\n", message); +} + +JSBool +xpc_DumpEvalInJSStackFrame(JSContext* cx, JSUint32 frameno, const char* text) +{ + JSStackFrame* fp; + JSStackFrame* iter = nsnull; + JSUint32 num = 0; + + if(!cx || !text) + { + printf("invalid params passed to xpc_DumpEvalInJSStackFrame!\n"); + return JS_FALSE; + } + + printf("js[%d]> %s\n", frameno, text); + + while(nsnull != (fp = JS_FrameIterator(cx, &iter))) + { + if(num == frameno) + break; + num++; + } + + if(!fp) + { + printf("invalid frame number!\n"); + return JS_FALSE; + } + + JSExceptionState* exceptionState = JS_SaveExceptionState(cx); + JSErrorReporter older = JS_SetErrorReporter(cx, xpcDumpEvalErrorReporter); + + jsval rval; + JSString* str; + const char* chars; + if(JS_EvaluateInStackFrame(cx, fp, text, strlen(text), "eval", 1, &rval) && + nsnull != (str = JS_ValueToString(cx, rval)) && + nsnull != (chars = JS_GetStringBytes(str))) + { + printf("%s\n", chars); + } + else + printf("eval failed!\n"); + JS_SetErrorReporter(cx, older); + JS_RestoreExceptionState(cx, exceptionState); + return JS_TRUE; +} + +/***************************************************************************/ + +JSTrapStatus JS_DLL_CALLBACK +xpc_DebuggerKeywordHandler(JSContext *cx, JSScript *script, jsbytecode *pc, + jsval *rval, void *closure) +{ + static const char line[] = + "------------------------------------------------------------------------\n"; + printf(line); + printf("Hit JavaScript \"debugger\" keyword. JS call stack...\n"); + xpc_DumpJSStack(cx, JS_TRUE, JS_TRUE, JS_FALSE); + printf(line); + return JSTRAP_CONTINUE; +} + +JSBool xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt) +{ + return JS_SetDebuggerHandler(rt, xpc_DebuggerKeywordHandler, nsnull); +} + +#endif diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index 4df1613b889..df60a1267bd 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -53,6 +53,18 @@ const char* XPCJSRuntime::mStrings[] = { XPCJSRuntime::~XPCJSRuntime() { +#ifdef DEBUG_jband + { + // count the total JSContexts in use + JSContext* iter = nsnull; + int count = 0; + while(JS_ContextIterator(mJSRuntime, &iter)) + count ++; + if(count) + printf("deleting XPCJSRuntime with %d total live JSContexts\n", count); + } +#endif + // clean up and destroy maps... if(mContextMap) @@ -61,7 +73,7 @@ XPCJSRuntime::~XPCJSRuntime() #ifdef DEBUG_jband uint32 count = mContextMap->Count(); if(count) - printf("deleting XPCJSRuntime with %d live JSContexts\n", (int)count); + printf("deleting XPCJSRuntime with %d live JSContexts known by xpconnect\n", (int)count); #endif delete mContextMap; } @@ -119,7 +131,13 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect, { NS_ADDREF(mJSRuntimeService); mJSRuntimeService->GetRuntime(&mJSRuntime); - } + } + + // Install a JavaScript 'debugger' keyword handler in debug builds only +#ifdef DEBUG + if(mJSRuntime) + xpc_InstallJSDebuggerKeywordHandler(mJSRuntime); +#endif } // static diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 4f84598dc3c..39f945deeb4 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -1379,14 +1379,26 @@ private: /***************************************************************************/ -JSObject* +extern JSObject* xpc_NewIDObject(JSContext *cx, JSObject* jsobj, const nsID& aID); -nsID* +extern nsID* xpc_JSObjectToID(JSContext *cx, JSObject* obj); /***************************************************************************/ +// in xpcdebug.cpp +extern JSBool +xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals, + JSBool showThisProps); + +extern JSBool +xpc_DumpEvalInJSStackFrame(JSContext* cx, JSUint32 frameno, const char* text); + +extern JSBool +xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt); + +/***************************************************************************/ // the include of declarations of the maps comes last because they have // inlines which call methods on classes above. diff --git a/js/src/xpconnect/tests/components/xpctest_echo.cpp b/js/src/xpconnect/tests/components/xpctest_echo.cpp index 5a7e191af22..72d5ff5d4d3 100644 --- a/js/src/xpconnect/tests/components/xpctest_echo.cpp +++ b/js/src/xpconnect/tests/components/xpctest_echo.cpp @@ -307,7 +307,7 @@ xpctestEcho::DebugDumpJSStack() NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); if(NS_SUCCEEDED(rv)) { - rv = xpc->DebugDumpJSStack(); + rv = xpc->DebugDumpJSStack(JS_TRUE, JS_TRUE, JS_TRUE); } return rv; }