зеркало из https://github.com/mozilla/gecko-dev.git
Bug 739681 - Allow DumpHeapComplete to print unreachable objects (r=mccr8)
This commit is contained in:
Родитель
f0a2aa9f36
Коммит
85004a8d19
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче