mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 378261: Replacing GC_MARK_DEBUG by DumpHeap. r=brendan
This commit is contained in:
parent
1d9fcd63fd
commit
68d7b74a96
@ -271,6 +271,13 @@ interface jsdIDebuggerService : nsISupports
|
||||
*/
|
||||
void GC();
|
||||
|
||||
/**
|
||||
* Output dump of JS heap.
|
||||
*
|
||||
* @param fileName Filename to dump the heap into.
|
||||
*/
|
||||
void DumpHeap(in string fileName);
|
||||
|
||||
/**
|
||||
* Clear profile data for all scripts.
|
||||
*/
|
||||
|
@ -2701,30 +2701,40 @@ jsdService::EnumerateScripts (jsdIScriptEnumerator *enumerator)
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef GC_MARK_DEBUG
|
||||
JS_BEGIN_EXTERN_C
|
||||
JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
|
||||
JS_END_EXTERN_C
|
||||
#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
|
||||
if (file)
|
||||
fclose (file);
|
||||
js_DumpGCHeap = NULL;
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdService::DumpHeap(const char* fileName)
|
||||
{
|
||||
ASSERT_VALID_CONTEXT;
|
||||
#ifndef DEBUG
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
#else
|
||||
nsresult rv = NS_OK;
|
||||
FILE *file = fileName ? fopen(fileName, "w") : stdout;
|
||||
if (!file) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
} else {
|
||||
JSContext *cx = JSD_GetDefaultJSContext (mCx);
|
||||
if (!JS_DumpHeap(cx, NULL, 0, NULL, (size_t)-1, NULL,
|
||||
NS_REINTERPRET_CAST(JSPrintfFormater, &fprintf),
|
||||
file)) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
if (file != stdout)
|
||||
fclose(file);
|
||||
}
|
||||
return rv;
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdService::ClearProfileData ()
|
||||
{
|
||||
|
89
js/src/js.c
89
js/src/js.c
@ -1352,45 +1352,56 @@ DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
static JSBool
|
||||
DumpHeap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
jsval v = JSVAL_NULL;
|
||||
char *fileName = NULL;
|
||||
size_t maxRecursionLevel = (size_t)-1;
|
||||
void* startThing = NULL;
|
||||
uint32 startTraceKind = 0;
|
||||
void *thingToFind = NULL;
|
||||
size_t maxDepth = (size_t)-1;
|
||||
void *thingToIgnore = NULL;
|
||||
jsval *vp;
|
||||
FILE *dumpFile;
|
||||
JSBool ok;
|
||||
JSTracer *trc;
|
||||
|
||||
if (argc != 0 && argv[0] != JSVAL_NULL && argv[0] != JSVAL_VOID) {
|
||||
v = argv[0];
|
||||
if (!JSVAL_IS_TRACEABLE(v)) {
|
||||
fprintf(gErrFile,
|
||||
"dumpHeap: the first argument is not null or "
|
||||
"a heap-allocated thing\n");
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > 1 && argv[1] != JSVAL_NULL && argv[1] != JSVAL_VOID) {
|
||||
vp = &argv[0];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
JSString *str;
|
||||
|
||||
str = JS_ValueToString(cx, argv[1]);
|
||||
str = JS_ValueToString(cx, *vp);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
argv[1] = STRING_TO_JSVAL(str);
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
fileName = JS_GetStringBytes(str);
|
||||
}
|
||||
|
||||
if (argc > 2 && argv[2] != JSVAL_NULL && argv[2] != JSVAL_VOID) {
|
||||
uint32 depth;
|
||||
|
||||
if (!JS_ValueToECMAUint32(cx, argv[2], &depth))
|
||||
return JS_FALSE;
|
||||
maxRecursionLevel = depth;
|
||||
vp = &argv[1];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
if (!JSVAL_IS_TRACEABLE(*vp))
|
||||
goto not_traceable_arg;
|
||||
startThing = JSVAL_TO_TRACEABLE(*vp);
|
||||
startTraceKind = JSVAL_TRACE_KIND(*vp);
|
||||
}
|
||||
|
||||
if (argc > 3 && argv[3] != JSVAL_NULL && argv[3] != JSVAL_VOID) {
|
||||
if (JSVAL_IS_GCTHING(argv[3]))
|
||||
thingToIgnore = JSVAL_TO_GCTHING(argv[3]);
|
||||
vp = &argv[2];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
if (!JSVAL_IS_TRACEABLE(*vp))
|
||||
goto not_traceable_arg;
|
||||
thingToFind = JSVAL_TO_TRACEABLE(*vp);
|
||||
}
|
||||
|
||||
vp = &argv[3];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
uint32 depth;
|
||||
|
||||
if (!JS_ValueToECMAUint32(cx, *vp, &depth))
|
||||
return JS_FALSE;
|
||||
maxDepth = depth;
|
||||
}
|
||||
|
||||
vp = &argv[4];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
if (!JSVAL_IS_TRACEABLE(*vp))
|
||||
goto not_traceable_arg;
|
||||
thingToIgnore = JSVAL_TO_TRACEABLE(*vp);
|
||||
}
|
||||
|
||||
if (!fileName) {
|
||||
@ -1404,23 +1415,18 @@ DumpHeap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
}
|
||||
}
|
||||
|
||||
trc = js_NewGCHeapDumper(cx, NULL, dumpFile, maxRecursionLevel,
|
||||
thingToIgnore);
|
||||
if (!trc) {
|
||||
ok = JS_FALSE;
|
||||
} else {
|
||||
if (v == JSVAL_NULL) {
|
||||
js_TraceRuntime(trc);
|
||||
} else {
|
||||
JS_TraceChildren(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
|
||||
}
|
||||
ok = js_FreeGCHeapDumper(trc);
|
||||
}
|
||||
|
||||
ok = JS_DumpHeap(cx, startThing, startTraceKind, thingToFind,
|
||||
maxDepth, thingToIgnore,
|
||||
(JSPrintfFormater)fprintf, dumpFile);
|
||||
if (dumpFile != stdout)
|
||||
fclose(dumpFile);
|
||||
|
||||
return ok;
|
||||
|
||||
not_traceable_arg:
|
||||
fprintf(gErrFile,
|
||||
"dumpHeap: argument %u is not null or a heap-allocated thing\n",
|
||||
(unsigned)(vp - argv));
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
@ -2203,7 +2209,7 @@ static JSFunctionSpec shell_functions[] = {
|
||||
#ifdef DEBUG
|
||||
{"dis", Disassemble, 1,0,0},
|
||||
{"dissrc", DisassWithSrc, 1,0,0},
|
||||
{"dumpHeap", DumpHeap, 3,0,0},
|
||||
{"dumpHeap", DumpHeap, 5,0,0},
|
||||
{"notes", Notes, 1,0,0},
|
||||
{"tracing", Tracing, 0,0,0},
|
||||
{"stats", DumpStats, 1,0,0},
|
||||
@ -2247,7 +2253,8 @@ static char *shell_help_messages[] = {
|
||||
#ifdef DEBUG
|
||||
"dis([fun]) Disassemble functions into bytecodes",
|
||||
"dissrc([fun]) Disassemble functions with source lines",
|
||||
"dumpHeap([obj]) Display reachable objects",
|
||||
"dumpHeap([fileName], [start], [toFind], [maxDepth], [toIgnore])\n"
|
||||
" Interface to JS_DumpHeap with output sent to file",
|
||||
"notes([fun]) Show source notes for functions",
|
||||
"tracing([toggle]) Turn tracing on or off",
|
||||
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
|
||||
|
458
js/src/jsapi.c
458
js/src/jsapi.c
@ -1863,6 +1863,464 @@ JS_UnlockGCThingRT(JSRuntime *rt, void *thing)
|
||||
return js_UnlockGCThingRT(rt, thing);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_TraceRuntime(JSTracer *trc)
|
||||
{
|
||||
JSBool allAtoms = trc->context->runtime->gcKeepAtoms != 0;
|
||||
|
||||
js_TraceRuntime(trc, allAtoms);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#ifdef HAVE_XPCONNECT
|
||||
#include "dump_xpc.h"
|
||||
#endif
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
|
||||
void *thing, uint32 kind, JSBool details)
|
||||
{
|
||||
const char *name;
|
||||
size_t n;
|
||||
|
||||
if (bufsize == 0)
|
||||
return;
|
||||
|
||||
switch (kind) {
|
||||
case JSTRACE_OBJECT:
|
||||
{
|
||||
JSObject *obj = (JSObject *)thing;
|
||||
JSClass *clasp = STOBJ_GET_CLASS(obj);
|
||||
|
||||
name = clasp->name;
|
||||
#ifdef HAVE_XPCONNECT
|
||||
if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
|
||||
jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
|
||||
|
||||
JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE);
|
||||
if (!JSVAL_IS_VOID(privateValue)) {
|
||||
void *privateThing = JSVAL_TO_PRIVATE(privateValue);
|
||||
const char *xpcClassName = GetXPCObjectClassName(privateThing);
|
||||
|
||||
if (xpcClassName)
|
||||
name = xpcClassName;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_STRING:
|
||||
name = JSSTRING_IS_DEPENDENT((JSString *)thing)
|
||||
? "substring"
|
||||
: "string";
|
||||
break;
|
||||
|
||||
case JSTRACE_DOUBLE:
|
||||
name = "double";
|
||||
break;
|
||||
|
||||
case JSTRACE_FUNCTION:
|
||||
name = "function";
|
||||
break;
|
||||
|
||||
case JSTRACE_ATOM:
|
||||
name = "atom";
|
||||
break;
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case JSTRACE_NAMESPACE:
|
||||
name = "namespace";
|
||||
break;
|
||||
|
||||
case JSTRACE_QNAME:
|
||||
name = "qname";
|
||||
break;
|
||||
|
||||
case JSTRACE_XML:
|
||||
name = "xml";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
JS_ASSERT(0);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
n = strlen(name);
|
||||
if (n > bufsize - 1)
|
||||
n = bufsize - 1;
|
||||
memcpy(buf, name, n + 1);
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
|
||||
if (details && bufsize > 2) {
|
||||
*buf++ = ' ';
|
||||
bufsize--;
|
||||
|
||||
switch (kind) {
|
||||
case JSTRACE_OBJECT:
|
||||
{
|
||||
JSObject *obj = (JSObject *)thing;
|
||||
jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
|
||||
void *privateThing = JSVAL_IS_VOID(privateValue)
|
||||
? NULL
|
||||
: JSVAL_TO_PRIVATE(privateValue);
|
||||
|
||||
JS_snprintf(buf, bufsize, "%p", privateThing);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_STRING:
|
||||
js_PutEscapedString(buf, bufsize, (JSString *)thing, 0);
|
||||
break;
|
||||
|
||||
case JSTRACE_DOUBLE:
|
||||
JS_snprintf(buf, bufsize, "%g", *(jsdouble *)thing);
|
||||
break;
|
||||
|
||||
case JSTRACE_FUNCTION:
|
||||
{
|
||||
JSFunction *fun = (JSFunction *)thing;
|
||||
|
||||
if (fun->atom && ATOM_IS_STRING(fun->atom))
|
||||
js_PutEscapedString(buf, bufsize, ATOM_TO_STRING(fun->atom), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_ATOM:
|
||||
{
|
||||
JSAtom *atom = (JSAtom *)thing;
|
||||
|
||||
if (ATOM_IS_INT(atom))
|
||||
JS_snprintf(buf, bufsize, "%d", ATOM_TO_INT(atom));
|
||||
else if (ATOM_IS_STRING(atom))
|
||||
js_PutEscapedString(buf, bufsize, ATOM_TO_STRING(atom), 0);
|
||||
else
|
||||
JS_snprintf(buf, bufsize, "object");
|
||||
break;
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case GCX_NAMESPACE:
|
||||
{
|
||||
JSXMLNamespace *ns = (JSXMLNamespace *)thing;
|
||||
|
||||
if (ns->prefix) {
|
||||
n = js_PutEscapedString(buf, bufsize, ns->prefix, 0);
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
}
|
||||
if (bufsize > 2) {
|
||||
*buf++ = ':';
|
||||
bufsize--;
|
||||
js_PutEscapedString(buf, bufsize, ns->uri, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GCX_QNAME:
|
||||
{
|
||||
JSXMLQName *qn = (JSXMLQName *)thing;
|
||||
|
||||
if (qn->prefix) {
|
||||
n = js_PutEscapedString(buf, bufsize, qn->prefix, 0);
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
}
|
||||
if (bufsize > 2) {
|
||||
*buf++ = '(';
|
||||
bufsize--;
|
||||
n = js_PutEscapedString(buf, bufsize, qn->uri, 0);
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
if (bufsize > 3) {
|
||||
*buf++ = ')';
|
||||
*buf++ = ':';
|
||||
bufsize -= 2;
|
||||
js_PutEscapedString(buf, bufsize, qn->localName, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GCX_XML:
|
||||
{
|
||||
extern const char *js_xml_class_str[];
|
||||
JSXML *xml = (JSXML *)thing;
|
||||
|
||||
JS_snprintf(buf, bufsize, "%s", js_xml_class_str[xml->xml_class]);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
JS_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf[bufsize - 1] = '\0';
|
||||
}
|
||||
|
||||
typedef struct JSHeapDumpNode JSHeapDumpNode;
|
||||
|
||||
struct JSHeapDumpNode {
|
||||
void *thing;
|
||||
uint32 kind;
|
||||
JSHeapDumpNode *next; /* next sibling */
|
||||
JSHeapDumpNode *parent; /* node with the thing that refer to thing
|
||||
from this node */
|
||||
char edgeName[1]; /* name of the edge from parent->thing
|
||||
into thing */
|
||||
};
|
||||
|
||||
typedef struct JSDumpingTracer {
|
||||
JSTracer base;
|
||||
JSDHashTable visited;
|
||||
JSBool ok;
|
||||
void *startThing;
|
||||
void *thingToFind;
|
||||
void *thingToIgnore;
|
||||
JSHeapDumpNode *parentNode;
|
||||
JSHeapDumpNode **lastNodep;
|
||||
char buffer[200];
|
||||
} JSDumpingTracer;
|
||||
|
||||
static void
|
||||
DumpNotify(JSTracer *trc, void *thing, uint32 kind)
|
||||
{
|
||||
JSDumpingTracer *dtrc;
|
||||
JSContext *cx;
|
||||
JSDHashEntryStub *entry;
|
||||
JSHeapDumpNode *node;
|
||||
const char *edgeName;
|
||||
size_t edgeNameSize;
|
||||
|
||||
JS_ASSERT(trc->callback == DumpNotify);
|
||||
dtrc = (JSDumpingTracer *)trc;
|
||||
|
||||
if (!dtrc->ok || thing == dtrc->thingToIgnore)
|
||||
return;
|
||||
|
||||
cx = trc->context;
|
||||
|
||||
/*
|
||||
* Check if we have already seen thing unless it is thingToFind to include
|
||||
* it to the graph each time we reach it and print all live things that
|
||||
* refer to thingToFind.
|
||||
*
|
||||
* This does not print all possible paths leading to thingToFind since
|
||||
* when a thing A refers directly or indirectly to thingToFind and A is
|
||||
* present several times in the graph, we will print only the first path
|
||||
* leading to A and thingToFind, other ways to reach A will be ignored.
|
||||
*/
|
||||
if (dtrc->thingToFind != thing) {
|
||||
/*
|
||||
* The startThing check allows to avoid putting startThing into the
|
||||
* hash table before tracing startThing in JS_DumpHeap.
|
||||
*/
|
||||
if (thing == dtrc->startThing)
|
||||
return;
|
||||
entry = (JSDHashEntryStub *)
|
||||
JS_DHashTableOperate(&dtrc->visited, thing, JS_DHASH_ADD);
|
||||
if (!entry) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
dtrc->ok = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
if (entry->key)
|
||||
return;
|
||||
entry->key = thing;
|
||||
}
|
||||
|
||||
if (dtrc->base.debugPrinter) {
|
||||
dtrc->base.debugPrinter(trc, dtrc->buffer, sizeof(dtrc->buffer));
|
||||
edgeName = dtrc->buffer;
|
||||
} else if (dtrc->base.debugPrintIndex != (size_t)-1) {
|
||||
JS_snprintf(dtrc->buffer, sizeof(dtrc->buffer), "%s[%lu]",
|
||||
(const char *)dtrc->base.debugPrintArg,
|
||||
dtrc->base.debugPrintIndex);
|
||||
edgeName = dtrc->buffer;
|
||||
} else {
|
||||
edgeName = (const char*)dtrc->base.debugPrintArg;
|
||||
}
|
||||
|
||||
edgeNameSize = strlen(edgeName) + 1;
|
||||
node = (JSHeapDumpNode *)
|
||||
JS_malloc(cx, offsetof(JSHeapDumpNode, edgeName) + edgeNameSize);
|
||||
if (!node) {
|
||||
dtrc->ok = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
node->thing = thing;
|
||||
node->kind = kind;
|
||||
node->next = NULL;
|
||||
node->parent = dtrc->parentNode;
|
||||
memcpy(node->edgeName, edgeName, edgeNameSize);
|
||||
|
||||
JS_ASSERT(!*dtrc->lastNodep);
|
||||
*dtrc->lastNodep = node;
|
||||
dtrc->lastNodep = &node->next;
|
||||
}
|
||||
|
||||
/* Dump node and the chain that leads to thing it contains. */
|
||||
static JSBool
|
||||
DumpNode(JSDumpingTracer *dtrc, JSHeapDumpNode *node,
|
||||
JSPrintfFormater format, void *closure)
|
||||
{
|
||||
JSHeapDumpNode *prev, *following;
|
||||
size_t chainLimit;
|
||||
JSBool ok;
|
||||
enum { MAX_PARENTS_TO_PRINT = 10 };
|
||||
|
||||
JS_PrintTraceThingInfo(dtrc->buffer, sizeof dtrc->buffer,
|
||||
&dtrc->base, node->thing, node->kind, JS_TRUE);
|
||||
if (format(closure, "%p %-22s via ", node->thing, dtrc->buffer) < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* We need to print the parent chain in the reverse order. To do it in
|
||||
* O(N) time where N is the chain length we first reverse the chain while
|
||||
* searching for the top and then print each node while restoring the
|
||||
* chain order.
|
||||
*/
|
||||
chainLimit = MAX_PARENTS_TO_PRINT;
|
||||
prev = NULL;
|
||||
for (;;) {
|
||||
following = node->parent;
|
||||
node->parent = prev;
|
||||
prev = node;
|
||||
node = following;
|
||||
if (!node)
|
||||
break;
|
||||
if (chainLimit == 0) {
|
||||
if (format(closure, "...") < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
--chainLimit;
|
||||
}
|
||||
|
||||
node = prev;
|
||||
prev = following;
|
||||
ok = JS_TRUE;
|
||||
do {
|
||||
/* Loop must continue even when !ok to restore the parent chain. */
|
||||
if (ok) {
|
||||
if (!prev) {
|
||||
/* Print edge from some runtime root or startThing. */
|
||||
if (format(closure, "%s", node->edgeName) < 0)
|
||||
ok = JS_FALSE;
|
||||
} else {
|
||||
JS_PrintTraceThingInfo(dtrc->buffer, sizeof dtrc->buffer,
|
||||
&dtrc->base, prev->thing, prev->kind,
|
||||
JS_FALSE);
|
||||
if (format(closure, "(%p %s).%s",
|
||||
prev->thing, dtrc->buffer, node->edgeName) < 0) {
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
following = node->parent;
|
||||
node->parent = prev;
|
||||
prev = node;
|
||||
node = following;
|
||||
} while (node);
|
||||
|
||||
return ok && format(closure, "\n") >= 0;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_DumpHeap(JSContext *cx, void* startThing, uint32 startKind,
|
||||
void *thingToFind, size_t maxDepth, void *thingToIgnore,
|
||||
JSPrintfFormater format, void *closure)
|
||||
{
|
||||
JSDumpingTracer dtrc;
|
||||
JSHeapDumpNode *node, *children, *next, *parent;
|
||||
size_t depth;
|
||||
JSBool thingToFindWasTraced;
|
||||
|
||||
if (maxDepth == 0)
|
||||
return JS_TRUE;
|
||||
|
||||
JS_TRACER_INIT(&dtrc.base, cx, DumpNotify);
|
||||
if (!JS_DHashTableInit(&dtrc.visited, JS_DHashGetStubOps(),
|
||||
NULL, sizeof(JSDHashEntryStub),
|
||||
JS_DHASH_DEFAULT_CAPACITY(100))) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
dtrc.ok = JS_TRUE;
|
||||
dtrc.startThing = startThing;
|
||||
dtrc.thingToFind = thingToFind;
|
||||
dtrc.thingToIgnore = thingToIgnore;
|
||||
dtrc.parentNode = NULL;
|
||||
node = NULL;
|
||||
dtrc.lastNodep = &node;
|
||||
if (!startThing) {
|
||||
JS_ASSERT(startKind == 0);
|
||||
JS_TraceRuntime(&dtrc.base);
|
||||
} else {
|
||||
JS_TraceChildren(&dtrc.base, startThing, startKind);
|
||||
}
|
||||
|
||||
depth = 1;
|
||||
if (!node)
|
||||
goto dump_out;
|
||||
|
||||
thingToFindWasTraced = thingToFind && thingToFind == startThing;
|
||||
for (;;) {
|
||||
/*
|
||||
* Loop must continue even when !dtrc.ok to free all nodes allocated
|
||||
* so far.
|
||||
*/
|
||||
if (dtrc.ok) {
|
||||
if (thingToFind == NULL || thingToFind == node->thing)
|
||||
dtrc.ok = DumpNode(&dtrc, node, format, closure);
|
||||
|
||||
/* Descend into children. */
|
||||
if (dtrc.ok &&
|
||||
depth < maxDepth &&
|
||||
(thingToFind != node->thing || !thingToFindWasTraced)) {
|
||||
dtrc.parentNode = node;
|
||||
children = NULL;
|
||||
dtrc.lastNodep = &children;
|
||||
JS_TraceChildren(&dtrc.base, node->thing, node->kind);
|
||||
if (thingToFind == node->thing)
|
||||
thingToFindWasTraced = JS_TRUE;
|
||||
if (children != NULL) {
|
||||
++depth;
|
||||
node = children;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Move to next or parents next and free the node. */
|
||||
for (;;) {
|
||||
next = node->next;
|
||||
parent = node->parent;
|
||||
JS_free(cx, node);
|
||||
node = next;
|
||||
if (node)
|
||||
break;
|
||||
if (!parent)
|
||||
goto dump_out;
|
||||
JS_ASSERT(depth > 1);
|
||||
--depth;
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
|
||||
dump_out:
|
||||
JS_ASSERT(depth == 1);
|
||||
JS_DHashTableFinish(&dtrc.visited);
|
||||
return dtrc.ok;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg)
|
||||
{
|
||||
|
@ -910,7 +910,6 @@ struct JSTracer {
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_CallTracer(JSTracer *trc, void *thing, uint32 kind);
|
||||
|
||||
|
||||
/*
|
||||
* Set debugging information about a reference to a traceable thing to prepare
|
||||
* for the following call to JS_CallTracer.
|
||||
@ -1009,10 +1008,33 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind);
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_TraceRuntime(JSTracer *trc);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
|
||||
void *thing, uint32 kind, JSBool includeDetails);
|
||||
/*
|
||||
* DEBUG-only method to dump an object graph of heap-allocated things.
|
||||
*
|
||||
* start: when non-null, dump only things reachable from start thing. Otherwise
|
||||
* dump all things rechable from runtime roots.
|
||||
* startKind: trace kind of start if start is not null. Must be 0 when start
|
||||
* is null.
|
||||
* thingToFind: dump only paths in the object graph leading to thingToFind
|
||||
* when non-null.
|
||||
* maxDepth: the upper bound on the number of edges to descend from the graph
|
||||
* roots.
|
||||
* thingToIgnore: thing to ignore during graph traversal when non-null.
|
||||
* format: callback to format the dump output.
|
||||
* closure: an argument to pass to formater.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_DumpHeap(JSContext *cx, void* startThing, uint32 startKind,
|
||||
void *thingToFind, size_t maxDepth, void *thingToIgnore,
|
||||
JSPrintfFormater format, void *closure);
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -449,12 +449,14 @@ js_locked_atom_tracer(JSHashEntry *he, intN i, void *arg)
|
||||
atom = (JSAtom *)he;
|
||||
args = (TraceArgs *)arg;
|
||||
if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || args->allAtoms) {
|
||||
JS_CALL_TRACER(args->trc, atom, JSTRACE_ATOM,
|
||||
(atom->flags & ATOM_PINNED)
|
||||
? "pinned_atom"
|
||||
: (atom->flags & ATOM_INTERNED)
|
||||
? "interned_atom"
|
||||
: "locked_atom");
|
||||
JS_SET_TRACING_INDEX(args->trc,
|
||||
(atom->flags & ATOM_PINNED)
|
||||
? "pinned_atom"
|
||||
: (atom->flags & ATOM_INTERNED)
|
||||
? "interned_atom"
|
||||
: "locked_atom",
|
||||
(size_t)i);
|
||||
JS_CallTracer(args->trc, atom, JSTRACE_ATOM);
|
||||
}
|
||||
return HT_ENUMERATE_NEXT;
|
||||
}
|
||||
|
390
js/src/jsgc.c
390
js/src/jsgc.c
@ -1794,378 +1794,6 @@ out:
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include <stdio.h>
|
||||
#include "jsprf.h"
|
||||
|
||||
#ifdef HAVE_XPCONNECT
|
||||
#include "dump_xpc.h"
|
||||
#endif
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
|
||||
void *thing, uint32 kind, JSBool details)
|
||||
{
|
||||
const char *name;
|
||||
size_t n;
|
||||
|
||||
if (bufsize == 0)
|
||||
return;
|
||||
|
||||
switch (kind) {
|
||||
case JSTRACE_OBJECT:
|
||||
{
|
||||
JSObject *obj = (JSObject *)thing;
|
||||
JSClass *clasp = STOBJ_GET_CLASS(obj);
|
||||
|
||||
name = clasp->name;
|
||||
#ifdef HAVE_XPCONNECT
|
||||
if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
|
||||
jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
|
||||
|
||||
JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE);
|
||||
if (!JSVAL_IS_VOID(privateValue)) {
|
||||
void *privateThing = JSVAL_TO_PRIVATE(privateValue);
|
||||
const char *xpcClassName = GetXPCObjectClassName(privateThing);
|
||||
|
||||
if (xpcClassName)
|
||||
name = xpcClassName;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_STRING:
|
||||
name = JSSTRING_IS_DEPENDENT((JSString *)thing)
|
||||
? "substring"
|
||||
: "string";
|
||||
break;
|
||||
|
||||
case JSTRACE_DOUBLE:
|
||||
name = "double";
|
||||
break;
|
||||
|
||||
case JSTRACE_FUNCTION:
|
||||
name = "function";
|
||||
break;
|
||||
|
||||
case JSTRACE_ATOM:
|
||||
name = "atom";
|
||||
break;
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case JSTRACE_NAMESPACE:
|
||||
name = "namespace";
|
||||
break;
|
||||
|
||||
case JSTRACE_QNAME:
|
||||
name = "qname";
|
||||
break;
|
||||
|
||||
case JSTRACE_XML:
|
||||
name = "xml";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
JS_ASSERT(0);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
n = strlen(name);
|
||||
if (n > bufsize - 1)
|
||||
n = bufsize - 1;
|
||||
memcpy(buf, name, n + 1);
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
|
||||
if (details && bufsize > 2) {
|
||||
*buf++ = ' ';
|
||||
bufsize--;
|
||||
|
||||
switch (kind) {
|
||||
case JSTRACE_OBJECT:
|
||||
{
|
||||
JSObject *obj = (JSObject *)thing;
|
||||
jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
|
||||
void *privateThing = JSVAL_IS_VOID(privateValue)
|
||||
? NULL
|
||||
: JSVAL_TO_PRIVATE(privateValue);
|
||||
|
||||
JS_snprintf(buf, bufsize, "%8p", privateThing);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_STRING:
|
||||
js_PutEscapedString(buf, bufsize, (JSString *)thing, 0);
|
||||
break;
|
||||
|
||||
case JSTRACE_DOUBLE:
|
||||
JS_snprintf(buf, bufsize, "%g", *(jsdouble *)thing);
|
||||
break;
|
||||
|
||||
case JSTRACE_FUNCTION:
|
||||
{
|
||||
JSFunction *fun = (JSFunction *)thing;
|
||||
|
||||
if (fun->atom && ATOM_IS_STRING(fun->atom))
|
||||
js_PutEscapedString(buf, bufsize, ATOM_TO_STRING(fun->atom), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case JSTRACE_ATOM:
|
||||
{
|
||||
JSAtom *atom = (JSAtom *)thing;
|
||||
|
||||
if (ATOM_IS_INT(atom))
|
||||
JS_snprintf(buf, bufsize, "%d", ATOM_TO_INT(atom));
|
||||
else if (ATOM_IS_STRING(atom))
|
||||
js_PutEscapedString(buf, bufsize, ATOM_TO_STRING(atom), 0);
|
||||
else
|
||||
JS_snprintf(buf, bufsize, "object");
|
||||
break;
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
case GCX_NAMESPACE:
|
||||
{
|
||||
JSXMLNamespace *ns = (JSXMLNamespace *)thing;
|
||||
|
||||
if (ns->prefix) {
|
||||
n = js_PutEscapedString(buf, bufsize, ns->prefix, 0);
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
}
|
||||
if (bufsize > 2) {
|
||||
*buf++ = ':';
|
||||
bufsize--;
|
||||
js_PutEscapedString(buf, bufsize, ns->uri, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GCX_QNAME:
|
||||
{
|
||||
JSXMLQName *qn = (JSXMLQName *)thing;
|
||||
|
||||
if (qn->prefix) {
|
||||
n = js_PutEscapedString(buf, bufsize, qn->prefix, 0);
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
}
|
||||
if (bufsize > 2) {
|
||||
*buf++ = '(';
|
||||
bufsize--;
|
||||
n = js_PutEscapedString(buf, bufsize, qn->uri, 0);
|
||||
buf += n;
|
||||
bufsize -= n;
|
||||
if (bufsize > 3) {
|
||||
*buf++ = ')';
|
||||
*buf++ = ':';
|
||||
bufsize -= 2;
|
||||
js_PutEscapedString(buf, bufsize, qn->localName, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GCX_XML:
|
||||
{
|
||||
extern const char *js_xml_class_str[];
|
||||
JSXML *xml = (JSXML *)thing;
|
||||
|
||||
JS_snprintf(buf, bufsize, "%s", js_xml_class_str[xml->xml_class]);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
JS_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf[bufsize - 1] = '\0';
|
||||
}
|
||||
|
||||
typedef struct JSGCDumpNode JSGCDumpNode;
|
||||
|
||||
struct JSGCDumpNode {
|
||||
void *thing;
|
||||
uint32 kind;
|
||||
char *name;
|
||||
JSGCDumpNode *next;
|
||||
};
|
||||
|
||||
typedef struct JSDumpingTracer
|
||||
{
|
||||
JSTracer base;
|
||||
JSDHashTable visited;
|
||||
JSBool ok;
|
||||
FILE *file;
|
||||
void *thingToFind;
|
||||
size_t maxRecursionDepth;
|
||||
void *thingToIgnore;
|
||||
size_t recursionDepth;
|
||||
JSGCDumpNode *top, **bottomp;
|
||||
char buffer[200];
|
||||
} JSDumpingTracer;
|
||||
|
||||
static void
|
||||
DumpThing(JSDumpingTracer *dtrc, JSGCDumpNode *node)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = dtrc->file;
|
||||
fprintf(fp, "%p ", node->thing);
|
||||
JS_PrintTraceThingInfo(dtrc->buffer, sizeof(dtrc->buffer),
|
||||
&dtrc->base, node->thing, node->kind, JS_TRUE);
|
||||
fputs(dtrc->buffer, fp);
|
||||
|
||||
/* Print path to this node from a traversal root. */
|
||||
node = dtrc->top;
|
||||
if (node->next) {
|
||||
fputs("\tvia ", fp);
|
||||
do {
|
||||
if (node != dtrc->top)
|
||||
fputc('.', fp);
|
||||
fprintf(fp, "%s(%p ", node->name, node->thing);
|
||||
JS_PrintTraceThingInfo(dtrc->buffer, sizeof(dtrc->buffer),
|
||||
&dtrc->base, node->thing, node->kind,
|
||||
JS_FALSE);
|
||||
fputs(dtrc->buffer, fp);
|
||||
node = node->next;
|
||||
} while (node->next);
|
||||
}
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
static void
|
||||
DumpNotify(JSTracer *trc, void *thing, uint32 kind)
|
||||
{
|
||||
JSDumpingTracer *dtrc;
|
||||
JSContext *cx;
|
||||
JSDHashEntryStub *entry;
|
||||
JSGCDumpNode node, **bottomp;
|
||||
const char *name;
|
||||
|
||||
JS_ASSERT(trc->callback == DumpNotify);
|
||||
dtrc = (JSDumpingTracer *)trc;
|
||||
|
||||
if (!dtrc->ok)
|
||||
return;
|
||||
|
||||
if (dtrc->thingToIgnore == thing)
|
||||
return;
|
||||
|
||||
cx = trc->context;
|
||||
entry = (JSDHashEntryStub *)
|
||||
JS_DHashTableOperate(&dtrc->visited, thing, JS_DHASH_ADD);
|
||||
if (!entry) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
dtrc->ok = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
if (entry->key)
|
||||
return;
|
||||
entry->key = thing;
|
||||
|
||||
node.thing = thing;
|
||||
node.kind = kind;
|
||||
if (dtrc->base.debugPrinter) {
|
||||
dtrc->base.debugPrinter(trc, dtrc->buffer, sizeof(dtrc->buffer));
|
||||
name = dtrc->buffer;
|
||||
} else if (dtrc->base.debugPrintIndex != (size_t)-1) {
|
||||
JS_snprintf(dtrc->buffer, sizeof(dtrc->buffer), "%s[%lu]",
|
||||
(const char *)dtrc->base.debugPrintArg,
|
||||
dtrc->base.debugPrintIndex);
|
||||
name = dtrc->buffer;
|
||||
} else {
|
||||
name = (const char*)dtrc->base.debugPrintArg;
|
||||
}
|
||||
|
||||
node.name = JS_strdup(cx, name);
|
||||
if (!node.name) {
|
||||
dtrc->ok = JS_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
node.next = NULL;
|
||||
bottomp = dtrc->bottomp;
|
||||
JS_ASSERT(!*bottomp);
|
||||
*bottomp = &node;
|
||||
dtrc->bottomp = &node.next;
|
||||
|
||||
/*
|
||||
* Dump thingToFind each time we reach it during the tracing to print all
|
||||
* live references to the thing.
|
||||
*/
|
||||
if (!dtrc->thingToFind || dtrc->thingToFind == thing)
|
||||
DumpThing(dtrc, &node);
|
||||
|
||||
if (dtrc->recursionDepth < dtrc->maxRecursionDepth) {
|
||||
dtrc->recursionDepth++;
|
||||
JS_TraceChildren(&dtrc->base, thing, kind);
|
||||
dtrc->recursionDepth--;
|
||||
}
|
||||
|
||||
*bottomp = NULL;
|
||||
dtrc->bottomp = bottomp;
|
||||
JS_free(cx, node.name);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSTracer *)
|
||||
js_NewGCHeapDumper(JSContext *cx, void *thingToFind, FILE *fp,
|
||||
size_t maxRecursionDepth, void *thingToIgnore)
|
||||
{
|
||||
JSDumpingTracer *dtrc;
|
||||
|
||||
dtrc = (JSDumpingTracer *)JS_malloc(cx, sizeof(*dtrc));
|
||||
if (!dtrc)
|
||||
return NULL;
|
||||
|
||||
JS_TRACER_INIT(&dtrc->base, cx, DumpNotify);
|
||||
if (!JS_DHashTableInit(&dtrc->visited, JS_DHashGetStubOps(),
|
||||
NULL, sizeof(JSDHashEntryStub),
|
||||
JS_DHASH_DEFAULT_CAPACITY(100))) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
JS_free(cx, dtrc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dtrc->ok = JS_TRUE;
|
||||
dtrc->file = fp ? fp : stderr;
|
||||
dtrc->thingToFind = thingToFind;
|
||||
dtrc->maxRecursionDepth = maxRecursionDepth;
|
||||
dtrc->thingToIgnore = thingToIgnore;
|
||||
dtrc->recursionDepth = 0;
|
||||
dtrc->top = NULL;
|
||||
dtrc->bottomp = &dtrc->top;
|
||||
|
||||
return &dtrc->base;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
js_FreeGCHeapDumper(JSTracer *trc)
|
||||
{
|
||||
JSDumpingTracer *dtrc;
|
||||
JSBool ok;
|
||||
JSContext *cx;
|
||||
|
||||
JS_ASSERT(trc->callback == DumpNotify);
|
||||
dtrc = (JSDumpingTracer *)trc;
|
||||
JS_ASSERT(!dtrc->top);
|
||||
JS_ASSERT(dtrc->bottomp == &dtrc->top);
|
||||
|
||||
JS_DHashTableFinish(&dtrc->visited);
|
||||
ok = dtrc->ok;
|
||||
cx = dtrc->base.context;
|
||||
JS_free(cx, dtrc);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
|
||||
{
|
||||
@ -2689,11 +2317,11 @@ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
|
||||
{
|
||||
uintN depth, nslots;
|
||||
if (fp->callobj)
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call object");
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->callobj, "call");
|
||||
if (fp->argsobj)
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->argsobj, "arguments object");
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->argsobj, "arguments");
|
||||
if (fp->varobj)
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables object");
|
||||
JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables");
|
||||
if (fp->script) {
|
||||
js_TraceScript(trc, fp->script);
|
||||
if (fp->spbase) {
|
||||
@ -2862,8 +2490,8 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
|
||||
js_TraceSharpMap(trc, &acx->sharpObjectMap);
|
||||
}
|
||||
|
||||
static void
|
||||
TraceRuntime(JSTracer *trc, JSBool allAtoms)
|
||||
void
|
||||
js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
|
||||
{
|
||||
JSRuntime *rt = trc->context->runtime;
|
||||
JSContext *iter, *acx;
|
||||
@ -2884,12 +2512,6 @@ TraceRuntime(JSTracer *trc, JSBool allAtoms)
|
||||
js_TraceContext(trc, acx);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js_TraceRuntime(JSTracer *trc)
|
||||
{
|
||||
TraceRuntime(trc, JS_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* When gckind is GC_LAST_DITCH, it indicates a call from js_NewGCThing with
|
||||
* rt->gcLock already held and when the lock should be kept on return.
|
||||
@ -3110,7 +2732,7 @@ restart:
|
||||
JS_TRACER_INIT(&trc, cx, NULL);
|
||||
rt->gcMarkingTracer = &trc;
|
||||
JS_ASSERT(IS_GC_MARKING_TRACER(&trc));
|
||||
TraceRuntime(&trc, keepAtoms);
|
||||
js_TraceRuntime(&trc, keepAtoms);
|
||||
js_MarkScriptFilenames(rt, keepAtoms);
|
||||
|
||||
/*
|
||||
|
@ -207,17 +207,6 @@ js_IsAboutToBeFinalized(JSContext *cx, void *thing);
|
||||
*/
|
||||
#define IS_GC_MARKING_TRACER(trc) ((trc)->callback == NULL)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
extern JS_FRIEND_API(JSTracer *)
|
||||
js_NewGCHeapDumper(JSContext *cx, void *thingToFind, FILE *fp,
|
||||
size_t maxRecursionDepth, void *thingToIgnore);
|
||||
|
||||
extern JS_FRIEND_API(JSBool)
|
||||
js_FreeGCHeapDumper(JSTracer *trc);
|
||||
|
||||
#endif
|
||||
|
||||
JS_STATIC_ASSERT(JSTRACE_STRING == 2);
|
||||
|
||||
#define JSTRACE_FUNCTION 3
|
||||
@ -243,8 +232,8 @@ js_CallValueTracerIfGCThing(JSTracer *trc, jsval v);
|
||||
extern void
|
||||
js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp);
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
js_TraceRuntime(JSTracer *trc);
|
||||
extern void
|
||||
js_TraceRuntime(JSTracer *trc, JSBool allAtoms);
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
js_TraceContext(JSTracer *trc, JSContext *acx);
|
||||
|
@ -715,6 +715,18 @@ typedef JSBool
|
||||
typedef JSPrincipals *
|
||||
(* JS_DLL_CALLBACK JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Output formated arguments as specified by format string. See fprintf/sprintf
|
||||
* documentation for specification of format. Return the number of characters
|
||||
* printed or -1 if an error occur.
|
||||
*/
|
||||
typedef int
|
||||
(* JS_DLL_CALLBACK JSPrintfFormater)(void *closure, const char *format, ...)
|
||||
#if defined __GNUC__
|
||||
__attribute__ ((format (printf, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
JS_END_EXTERN_C
|
||||
|
||||
#endif /* jspubtd_h___ */
|
||||
|
@ -294,10 +294,6 @@ DumpXPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
/* XXX needed only by GC() */
|
||||
#include "jscntxt.h"
|
||||
|
||||
#ifdef GC_MARK_DEBUG
|
||||
extern "C" JS_FRIEND_DATA(FILE *) js_DumpGCHeap;
|
||||
#endif
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSBool)
|
||||
GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
@ -306,25 +302,7 @@ GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
|
||||
rt = cx->runtime;
|
||||
preBytes = rt->gcBytes;
|
||||
#ifdef GC_MARK_DEBUG
|
||||
if (argc && JSVAL_IS_STRING(argv[0])) {
|
||||
char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
||||
FILE *file = fopen(name, "w");
|
||||
if (!file) {
|
||||
fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));
|
||||
return JS_FALSE;
|
||||
}
|
||||
js_DumpGCHeap = file;
|
||||
} else {
|
||||
js_DumpGCHeap = stdout;
|
||||
}
|
||||
#endif
|
||||
JS_GC(cx);
|
||||
#ifdef GC_MARK_DEBUG
|
||||
if (js_DumpGCHeap != stdout)
|
||||
fclose(js_DumpGCHeap);
|
||||
js_DumpGCHeap = NULL;
|
||||
#endif
|
||||
fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
|
||||
(unsigned long)preBytes, (unsigned long)rt->gcBytes,
|
||||
#ifdef XP_UNIX
|
||||
@ -339,6 +317,90 @@ GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
static JSBool
|
||||
DumpHeap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
char *fileName = NULL;
|
||||
void* startThing = NULL;
|
||||
uint32 startTraceKind = 0;
|
||||
void *thingToFind = NULL;
|
||||
size_t maxDepth = (size_t)-1;
|
||||
void *thingToIgnore = NULL;
|
||||
jsval *vp;
|
||||
FILE *dumpFile;
|
||||
JSBool ok;
|
||||
|
||||
vp = &argv[0];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
JSString *str;
|
||||
|
||||
str = JS_ValueToString(cx, *vp);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
*vp = STRING_TO_JSVAL(str);
|
||||
fileName = JS_GetStringBytes(str);
|
||||
}
|
||||
|
||||
vp = &argv[1];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
if (!JSVAL_IS_TRACEABLE(*vp))
|
||||
goto not_traceable_arg;
|
||||
startThing = JSVAL_TO_TRACEABLE(*vp);
|
||||
startTraceKind = JSVAL_TRACE_KIND(*vp);
|
||||
}
|
||||
|
||||
vp = &argv[2];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
if (!JSVAL_IS_TRACEABLE(*vp))
|
||||
goto not_traceable_arg;
|
||||
thingToFind = JSVAL_TO_TRACEABLE(*vp);
|
||||
}
|
||||
|
||||
vp = &argv[3];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
uint32 depth;
|
||||
|
||||
if (!JS_ValueToECMAUint32(cx, *vp, &depth))
|
||||
return JS_FALSE;
|
||||
maxDepth = depth;
|
||||
}
|
||||
|
||||
vp = &argv[4];
|
||||
if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) {
|
||||
if (!JSVAL_IS_TRACEABLE(*vp))
|
||||
goto not_traceable_arg;
|
||||
thingToIgnore = JSVAL_TO_TRACEABLE(*vp);
|
||||
}
|
||||
|
||||
if (!fileName) {
|
||||
dumpFile = gOutFile;
|
||||
} else {
|
||||
dumpFile = fopen(fileName, "w");
|
||||
if (!dumpFile) {
|
||||
fprintf(gErrFile, "dumpHeap: can't open %s: %s\n",
|
||||
fileName, strerror(errno));
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ok = JS_DumpHeap(cx, startThing, startTraceKind, thingToFind,
|
||||
maxDepth, thingToIgnore,
|
||||
(JSPrintfFormater)fprintf, dumpFile);
|
||||
if (dumpFile != gOutFile)
|
||||
fclose(dumpFile);
|
||||
return ok;
|
||||
|
||||
not_traceable_arg:
|
||||
fprintf(gErrFile,
|
||||
"dumpHeap: argument %u is not null or a heap-allocated thing\n",
|
||||
(unsigned)(vp - argv));
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSBool)
|
||||
Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
@ -361,6 +423,9 @@ static JSFunctionSpec glob_functions[] = {
|
||||
{"dump", Dump, 1,0,0},
|
||||
{"gc", GC, 0,0,0},
|
||||
{"clear", Clear, 1,0,0},
|
||||
#ifdef DEBUG
|
||||
{"dumpHeap", DumpHeap, 5,0,0},
|
||||
#endif
|
||||
{nsnull,nsnull,0,0,0}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user