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

This commit is contained in:
jband%netscape.com 2000-02-01 08:48:17 +00:00
Родитель 669ded1e87
Коммит 321d0f8624
9 изменённых файлов: 496 добавлений и 64 удалений

Просмотреть файл

@ -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");
}

Просмотреть файл

@ -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);
};

Просмотреть файл

@ -48,6 +48,7 @@ CPPSRCS = \
xpccomponents.cpp \
xpccontext.cpp \
xpcconvert.cpp \
xpcdebug.cpp \
xpcexception.cpp \
xpcjsid.cpp \
xpcjsruntime.cpp \

Просмотреть файл

@ -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 \

Просмотреть файл

@ -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(&current);
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");
}

Просмотреть файл

@ -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 <jband@netscape.com>
*
* 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 <TOP LEVEL>", 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 : "<unknown>",
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

Просмотреть файл

@ -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

Просмотреть файл

@ -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.

Просмотреть файл

@ -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;
}