Bug 151803, "Debugger is leaking JSDValues", r=peterv, sr=jst

Stop caching jsdIValues, it doesn't seem to be worth the lookup cost.  This has the side effect of plugging the leak, as described in the bug.
also...
* Convert a few raw pointers to nsCOMPtrs
* Fix a bug where removing the last filter did not null out the list head,
causing a crash the next time filters were used.
* Track live jsdStackFrames, so we can invalidate them all when execution
continues.  Without this, only the top frame is properly invalidated, and any
other frame accessed after a continue will do Bad Things.
* Add some debugging prints to GetInitAtService, which seems to be failing at
random times.
This commit is contained in:
rginda%netscape.com 2002-06-19 21:56:23 +00:00
Родитель 0b0b2a8cbb
Коммит f239119376
2 изменённых файлов: 131 добавлений и 66 удалений

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

@ -120,6 +120,7 @@ PRUint32 gScriptCount = 0;
PRUint32 gValueCount = 0;
PRUint32 gPropertyCount = 0;
PRUint32 gContextCount = 0;
PRUint32 gFrameCount = 0;
#endif
static jsdService *gJsds = 0;
@ -151,9 +152,10 @@ static struct FilterRecord {
PRUint32 endLine;
} *gFilters = nsnull;
static struct LiveEphemeral *gLiveValues = nsnull;
static struct LiveEphemeral *gLiveProperties = nsnull;
static struct LiveEphemeral *gLiveContexts = nsnull;
static struct LiveEphemeral *gLiveValues = nsnull;
static struct LiveEphemeral *gLiveProperties = nsnull;
static struct LiveEphemeral *gLiveContexts = nsnull;
static struct LiveEphemeral *gLiveStackFrames = nsnull;
/*******************************************************************************
* utility functions for ephemeral lists
@ -525,12 +527,12 @@ jsds_ErrorHookProc (JSDContext *jsdc, JSContext *cx, const char *message,
running = PR_TRUE;
jsdIValue *val = 0;
nsCOMPtr<jsdIValue> val;
if (JS_IsExceptionPending(cx)) {
jsval jv;
JS_GetPendingException(cx, &jv);
JSDValue *jsdv = JSD_NewValue (jsdc, jv);
val = jsdValue::FromPtr(jsdc, jsdv);
val = getter_AddRefs(jsdValue::FromPtr(jsdc, jsdv));
}
const char *fileName;
@ -601,7 +603,7 @@ jsds_CallHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
gJsds->Pause(nsnull);
hook->OnCall(frame, type);
gJsds->UnPause(nsnull);
frame->Invalidate();
jsdStackFrame::InvalidateAll();
return JS_TRUE;
}
@ -612,7 +614,7 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
{
nsCOMPtr<jsdIExecutionHook> hook(0);
PRUint32 hook_rv = JSD_HOOK_RETURN_CONTINUE;
jsdIValue *js_rv = 0;
nsCOMPtr<jsdIValue> js_rv;
switch (type)
{
@ -642,7 +644,7 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
gJsds->GetThrowHook(getter_AddRefs(hook));
if (hook) {
JSDValue *jsdv = JSD_GetException (jsdc, jsdthreadstate);
js_rv = jsdValue::FromPtr (jsdc, jsdv);
js_rv = getter_AddRefs(jsdValue::FromPtr (jsdc, jsdv));
}
break;
}
@ -661,16 +663,22 @@ jsds_ExecutionHookProc (JSDContext* jsdc, JSDThreadState* jsdthreadstate,
getter_AddRefs(jsdStackFrame::FromPtr(jsdc, jsdthreadstate,
native_frame));
gJsds->Pause(nsnull);
hook->OnExecute (frame, type, &js_rv, &hook_rv);
jsdIValue *inout_rv = js_rv;
NS_IF_ADDREF(inout_rv);
hook->OnExecute (frame, type, &inout_rv, &hook_rv);
js_rv = inout_rv;
gJsds->UnPause(nsnull);
frame->Invalidate();
jsdStackFrame::InvalidateAll();
if (hook_rv == JSD_HOOK_RETURN_RET_WITH_VAL ||
hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL)
{
JSDValue *jsdv;
js_rv->GetJSDValue (&jsdv);
*rval = JSD_GetValueWrappedJSVal(jsdc, jsdv);
hook_rv == JSD_HOOK_RETURN_THROW_WITH_VAL) {
if (js_rv) {
JSDValue *jsdv;
js_rv->GetJSDValue (&jsdv);
*rval = JSD_GetValueWrappedJSVal(jsdc, jsdv);
} else {
*rval = JSVAL_VOID;
}
}
NS_IF_RELEASE(js_rv);
@ -1017,7 +1025,7 @@ jsdScript::CreatePPLineMap()
fun = JS_CompileUCFunction (cx, obj, "ppfun", fun->nargs, argnames,
JS_GetStringChars(jsstr),
JS_GetStringLength(jsstr),
"x-jsd:internal:ppbuffer:function", 3);
"x-jsd:ppbuffer?type=function", 3);
if (!fun || !(script = JS_GetFunctionScript(cx, fun)))
return nsnull;
baseLine = 3;
@ -1030,7 +1038,7 @@ jsdScript::CreatePPLineMap()
script = JS_CompileUCScript (cx, obj,
JS_GetStringChars(jsstr),
JS_GetStringLength(jsstr),
"x-jsd:internal:ppbuffer:script", 1);
"x-jsd:ppbuffer?type=script", 1);
if (!script)
return nsnull;
scriptOwner = PR_TRUE;
@ -1582,6 +1590,74 @@ jsdContext::SetScriptsEnabled (PRBool _rval)
/* Stack Frames */
NS_IMPL_THREADSAFE_ISUPPORTS2(jsdStackFrame, jsdIStackFrame, jsdIEphemeral);
jsdStackFrame::jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState,
JSDStackFrameInfo *aStackFrameInfo) :
mCx(aCx), mThreadState(aThreadState), mStackFrameInfo(aStackFrameInfo)
{
DEBUG_CREATE ("jsdStackFrame", gFrameCount);
mValid = (aCx && aThreadState && aStackFrameInfo);
NS_INIT_ISUPPORTS();
if (mValid) {
mLiveListEntry.key = aStackFrameInfo;
mLiveListEntry.value = this;
jsds_InsertEphemeral (&gLiveStackFrames, &mLiveListEntry);
}
}
jsdStackFrame::~jsdStackFrame()
{
DEBUG_DESTROY ("jsdStackFrame", gFrameCount);
if (mValid)
{
/* call Invalidate() to take ourselves out of the live list */
Invalidate();
}
}
jsdIStackFrame *
jsdStackFrame::FromPtr (JSDContext *aCx, JSDThreadState *aThreadState,
JSDStackFrameInfo *aStackFrameInfo)
{
if (!aStackFrameInfo)
return nsnull;
jsdIStackFrame *rv;
nsCOMPtr<jsdIStackFrame> frame;
nsCOMPtr<jsdIEphemeral> eph =
jsds_FindEphemeral (&gLiveStackFrames,
NS_REINTERPRET_CAST(void *, aStackFrameInfo));
if (eph)
{
frame = do_QueryInterface(eph);
rv = frame;
}
else
{
rv = new jsdStackFrame (aCx, aThreadState, aStackFrameInfo);
}
NS_IF_ADDREF(rv);
return rv;
}
NS_IMETHODIMP
jsdStackFrame::Invalidate()
{
ASSERT_VALID_EPHEMERAL;
mValid = PR_FALSE;
jsds_RemoveEphemeral (&gLiveStackFrames, &mLiveListEntry);
return NS_OK;
}
void
jsdStackFrame::InvalidateAll()
{
if (gLiveStackFrames)
jsds_InvalidateAllEphemerals (&gLiveStackFrames);
}
NS_IMETHODIMP
jsdStackFrame::GetJSDContext(JSDContext **_rval)
{
@ -1613,14 +1689,6 @@ jsdStackFrame::GetIsValid(PRBool *_rval)
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::Invalidate()
{
ASSERT_VALID_EPHEMERAL;
mValid = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
jsdStackFrame::GetCallingFrame(jsdIStackFrame **_rval)
{
@ -1803,23 +1871,12 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(jsdValue, jsdIValue, jsdIEphemeral);
jsdIValue *
jsdValue::FromPtr (JSDContext *aCx, JSDValue *aValue)
{
/* value will be dropped by te jsdValue destructor. */
if (!aValue)
return nsnull;
nsCOMPtr<jsdIValue> jsdiv;
jsval jv = JSD_GetValueWrappedJSVal (aCx, aValue);
nsCOMPtr<jsdIEphemeral> eph =
jsds_FindEphemeral (&gLiveValues, NS_REINTERPRET_CAST(void *, jv));
if (eph)
{
jsdiv = do_QueryInterface(eph);
}
else
{
jsdiv = new jsdValue (aCx, aValue);
}
jsdIValue *rv = jsdiv;
jsdIValue *rv = new jsdValue (aCx, aValue);
NS_IF_ADDREF(rv);
return rv;
}
@ -1831,8 +1888,6 @@ jsdValue::jsdValue (JSDContext *aCx, JSDValue *aValue) : mValid(PR_TRUE),
DEBUG_CREATE ("jsdValue", gValueCount);
NS_INIT_ISUPPORTS();
mLiveListEntry.value = this;
mLiveListEntry.key = NS_REINTERPRET_CAST(void *,
JSD_GetValueWrappedJSVal (aCx, aValue));
jsds_InsertEphemeral (&gLiveValues, &mLiveListEntry);
}
@ -2168,8 +2223,12 @@ jsdService::GetInitAtStartup (PRBool *_rval)
nsresult rv;
nsCOMPtr<nsICategoryManager>
categoryManager(do_GetService(NS_CATMAN_CTRID, &rv));
if (NS_FAILED(rv))
{
NS_WARNING("couldn't get category manager");
return rv;
}
if (mInitAtStartup == triUnknown) {
nsXPIDLCString notused;
@ -2189,12 +2248,19 @@ jsdService::GetInitAtStartup (PRBool *_rval)
mInitAtStartup = triYes;
rv = SetInitAtStartup (PR_FALSE);
if (NS_FAILED(rv))
{
NS_WARNING("SetInitAtStartup failed");
return rv;
}
} else if (autoreg_rv == NS_ERROR_NOT_AVAILABLE) {
mInitAtStartup = triNo;
} else if (NS_SUCCEEDED(autoreg_rv)) {
mInitAtStartup = triYes;
} else {
NS_WARN_IF_FALSE(NS_SUCCEEDED(autoreg_rv),
"couldn't get autoreg category");
NS_WARN_IF_FALSE(NS_SUCCEEDED(appstart_rv),
"couldn't get appstart category");
return rv;
}
}
@ -2525,12 +2591,24 @@ jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
return rv;
}
#ifdef GC_MARK_DEBUG
extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
#endif
NS_IMETHODIMP
jsdService::GC (void)
{
ASSERT_VALID_CONTEXT;
JSContext *cx = JSD_GetDefaultJSContext (mCx);
#ifdef GC_MARK_DEBUG
FILE *file = fopen("jsds-roots.txt", "w");
js_DumpGCHeap = file;
#endif
JS_GC(cx);
#ifdef GC_MARK_DEBUG
fclose (file);
js_DumpGCHeap = NULL;
#endif
return NS_OK;
}
@ -2614,9 +2692,14 @@ jsdService::RemoveFilter (jsdIFilter *filter)
if (!rec)
return NS_ERROR_INVALID_ARG;
if (gFilters == rec)
if (gFilters == rec) {
gFilters = NS_REINTERPRET_CAST(FilterRecord *,
PR_NEXT_LINK(&rec->links));
/* If we're the only filter left, null out the list head. */
if (gFilters == rec)
gFilters = nsnull;
}
PR_REMOVE_LINK(&rec->links);
jsds_FreeFilter (rec);

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

@ -221,38 +221,20 @@ class jsdStackFrame : public jsdIStackFrame
/* you'll normally use use FromPtr() instead of directly constructing one */
jsdStackFrame (JSDContext *aCx, JSDThreadState *aThreadState,
JSDStackFrameInfo *aStackFrameInfo) :
mCx(aCx), mThreadState(aThreadState), mStackFrameInfo(aStackFrameInfo)
{
mValid = (aCx && aThreadState && aStackFrameInfo);
NS_INIT_ISUPPORTS();
}
JSDStackFrameInfo *aStackFrameInfo);
virtual ~jsdStackFrame();
/* XXX These things are only valid for a short period of time, they reflect
* state in the js engine that will go away after stepping past wherever
* we were stopped at when this was created. We could keep a list of every
* instance of this we've created, and "invalidate" them before we let the
* engine continue. The next time we need a threadstate, we can search the
* list to find an invalidated one, and just reuse it.
*/
static jsdIStackFrame *FromPtr (JSDContext *aCx,
static void InvalidateAll();
static jsdIStackFrame* FromPtr (JSDContext *aCx,
JSDThreadState *aThreadState,
JSDStackFrameInfo *aStackFrameInfo)
{
if (!aStackFrameInfo)
return nsnull;
jsdIStackFrame *rv = new jsdStackFrame (aCx, aThreadState,
aStackFrameInfo);
NS_IF_ADDREF(rv);
return rv;
}
JSDStackFrameInfo *aStackFrameInfo);
private:
jsdStackFrame(); /* no implementation */
jsdStackFrame(const jsdStackFrame&); /* no implementation */
PRBool mValid;
LiveEphemeral mLiveListEntry;
JSDContext *mCx;
JSDThreadState *mThreadState;
JSDStackFrameInfo *mStackFrameInfo;