Bug 739681 - Allow DumpHeapComplete to print unreachable objects (r=mccr8)

This commit is contained in:
Bill McCloskey 2012-11-02 12:24:19 -07:00
Родитель f0a2aa9f36
Коммит 85004a8d19
2 изменённых файлов: 85 добавлений и 57 удалений

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

@ -687,6 +687,40 @@ FinalizeCount(JSContext *cx, unsigned argc, jsval *vp)
return true;
}
static JSBool
DumpHeapComplete(JSContext *cx, unsigned argc, jsval *vp)
{
const char *fileName = NULL;
JSAutoByteString fileNameBytes;
if (argc > 0) {
Value v = JS_ARGV(cx, vp)[0];
if (v.isString()) {
JSString *str = v.toString();
if (!fileNameBytes.encode(cx, str))
return false;
fileName = fileNameBytes.ptr();
}
}
FILE *dumpFile;
if (!fileName) {
dumpFile = stdout;
} else {
dumpFile = fopen(fileName, "w");
if (!dumpFile) {
JS_ReportError(cx, "can't open %s", fileName);
return false;
}
}
js::DumpHeapComplete(JS_GetRuntime(cx), dumpFile);
fclose(dumpFile);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return true;
}
JSBool
MJitChunkLimit(JSContext *cx, unsigned argc, jsval *vp)
{
@ -860,6 +894,10 @@ static JSFunctionSpecWithHelp TestingFunctions[] = {
"isProxy(obj)",
" If true, obj is a proxy of some sort"),
JS_FN_HELP("dumpHeapComplete", DumpHeapComplete, 1, 0,
"dumpHeapComplete([filename])",
" Dump reachable and unreachable objects to a file."),
JS_FN_HELP("mjitChunkLimit", MJitChunkLimit, 1, 0,
"mjitChunkLimit(N)",
" Specify limit on compiled chunk size during mjit compilation."),

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

@ -597,32 +597,15 @@ js_DumpObject(JSObject *obj)
#endif
struct DumpingChildInfo {
void *node;
JSGCTraceKind kind;
DumpingChildInfo (void *n, JSGCTraceKind k)
: node(n), kind(k)
{}
};
typedef HashSet<void *, DefaultHasher<void *>, SystemAllocPolicy> PtrSet;
struct JSDumpHeapTracer : public JSTracer {
PtrSet visited;
struct JSDumpHeapTracer : public JSTracer
{
FILE *output;
Vector<DumpingChildInfo, 0, SystemAllocPolicy> nodes;
char buffer[200];
bool rootTracing;
JSDumpHeapTracer(FILE *fp)
: output(fp)
{}
};
static void
DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind);
static char
MarkDescriptor(void *thing)
{
@ -634,65 +617,72 @@ MarkDescriptor(void *thing)
}
static void
DumpHeapPushIfNew(JSTracer *trc, void **thingp, JSGCTraceKind kind)
DumpHeapVisitCompartment(JSRuntime *rt, void *data, JSCompartment *comp)
{
JS_ASSERT(trc->callback == DumpHeapPushIfNew ||
trc->callback == DumpHeapVisitChild);
void *thing = *thingp;
JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(trc);
char name[1024];
if (rt->compartmentNameCallback)
(*rt->compartmentNameCallback)(rt, comp, name, sizeof(name));
else
strcpy(name, "<unknown>");
/*
* If we're tracing roots, print root information. Do this even if we've
* already seen thing, for complete root information.
*/
if (dtrc->rootTracing) {
fprintf(dtrc->output, "%p %c %s\n", thing, MarkDescriptor(thing),
JS_GetTraceEdgeName(dtrc, dtrc->buffer, sizeof(dtrc->buffer)));
}
JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(data);
fprintf(dtrc->output, "# compartment %s\n", name);
}
PtrSet::AddPtr ptrEntry = dtrc->visited.lookupForAdd(thing);
if (ptrEntry || !dtrc->visited.add(ptrEntry, thing))
return;
static void
DumpHeapVisitArena(JSRuntime *rt, void *data, gc::Arena *arena,
JSGCTraceKind traceKind, size_t thingSize)
{
JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(data);
fprintf(dtrc->output, "# arena allockind=%u size=%u\n",
unsigned(arena->aheader.getAllocKind()), unsigned(thingSize));
}
dtrc->nodes.append(DumpingChildInfo(thing, kind));
static void
DumpHeapVisitCell(JSRuntime *rt, void *data, void *thing,
JSGCTraceKind traceKind, size_t thingSize)
{
JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(data);
char cellDesc[1024];
JS_GetTraceThingInfo(cellDesc, sizeof(cellDesc), dtrc, thing, traceKind, true);
fprintf(dtrc->output, "%p %c %s\n", thing, MarkDescriptor(thing), cellDesc);
JS_TraceChildren(dtrc, thing, traceKind);
}
static void
DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind)
{
JS_ASSERT(trc->callback == DumpHeapVisitChild);
JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(trc);
const char *edgeName = JS_GetTraceEdgeName(dtrc, dtrc->buffer, sizeof(dtrc->buffer));
fprintf(dtrc->output, "> %p %c %s\n", *thingp, MarkDescriptor(*thingp), edgeName);
DumpHeapPushIfNew(dtrc, thingp, kind);
char buffer[1024];
fprintf(dtrc->output, "> %p %c %s\n", *thingp, MarkDescriptor(*thingp),
JS_GetTraceEdgeName(dtrc, buffer, sizeof(buffer)));
}
static void
DumpHeapVisitRoot(JSTracer *trc, void **thingp, JSGCTraceKind kind)
{
JSDumpHeapTracer *dtrc = static_cast<JSDumpHeapTracer *>(trc);
char buffer[1024];
fprintf(dtrc->output, "%p %c %s\n", *thingp, MarkDescriptor(*thingp),
JS_GetTraceEdgeName(dtrc, buffer, sizeof(buffer)));
}
void
js::DumpHeapComplete(JSRuntime *rt, FILE *fp)
{
JSDumpHeapTracer dtrc(fp);
JS_TracerInit(&dtrc, rt, DumpHeapPushIfNew);
if (!dtrc.visited.init(10000))
return;
/* Store and log the root information. */
dtrc.rootTracing = true;
JS_TracerInit(&dtrc, rt, DumpHeapVisitRoot);
TraceRuntime(&dtrc);
fprintf(dtrc.output, "==========\n");
/* Log the graph. */
dtrc.rootTracing = false;
dtrc.callback = DumpHeapVisitChild;
JS_TracerInit(&dtrc, rt, DumpHeapVisitChild);
IterateCompartmentsArenasCells(rt, &dtrc,
DumpHeapVisitCompartment,
DumpHeapVisitArena,
DumpHeapVisitCell);
while (!dtrc.nodes.empty()) {
DumpingChildInfo dci = dtrc.nodes.popCopy();
JS_GetTraceThingInfo(dtrc.buffer, sizeof(dtrc.buffer),
&dtrc, dci.node, dci.kind, JS_TRUE);
fprintf(fp, "%p %c %s\n", dci.node, MarkDescriptor(dci.node), dtrc.buffer);
JS_TraceChildren(&dtrc, dci.node, dci.kind);
}
dtrc.visited.finish();
fflush(dtrc.output);
}