diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index dc1a19533fc6..611e4f86986b 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -827,32 +827,52 @@ FinalizeCount(JSContext *cx, unsigned argc, jsval *vp) static bool DumpHeapComplete(JSContext *cx, unsigned argc, jsval *vp) { - const char *fileName = nullptr; - JSAutoByteString fileNameBytes; - if (argc > 0) { - Value v = JS_ARGV(cx, vp)[0]; + CallArgs args = CallArgsFromVp(argc, vp); + + DumpHeapNurseryBehaviour nurseryBehaviour = js::IgnoreNurseryObjects; + FILE *dumpFile = NULL; + + unsigned i = 0; + if (argc > i) { + Value v = args[i]; if (v.isString()) { JSString *str = v.toString(); + bool same = false; + if (!JS_StringEqualsAscii(cx, str, "collectNurseryBeforeDump", &same)) + return false; + if (same) { + nurseryBehaviour = js::CollectNurseryBeforeDump; + ++i; + } + } + } + + if (argc > i) { + Value v = args[i]; + if (v.isString()) { + JSString *str = v.toString(); + JSAutoByteString fileNameBytes; if (!fileNameBytes.encodeLatin1(cx, str)) return false; - fileName = fileNameBytes.ptr(); + const char *fileName = fileNameBytes.ptr(); + dumpFile = fopen(fileName, "w"); + if (!dumpFile) { + JS_ReportError(cx, "can't open %s", fileName); + return false; + } + ++i; } } - FILE *dumpFile; - if (!fileName) { - dumpFile = stdout; - } else { - dumpFile = fopen(fileName, "w"); - if (!dumpFile) { - JS_ReportError(cx, "can't open %s", fileName); - return false; - } + if (i != argc) { + JS_ReportError(cx, "bad arguments passed to dumpHeapComplete"); + return false; } - js::DumpHeapComplete(JS_GetRuntime(cx), dumpFile); + js::DumpHeapComplete(JS_GetRuntime(cx), dumpFile ? dumpFile : stdout, nurseryBehaviour); - fclose(dumpFile); + if (dumpFile) + fclose(dumpFile); JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; @@ -1184,8 +1204,10 @@ static const JSFunctionSpecWithHelp TestingFunctions[] = { " 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."), +"dumpHeapComplete(['collectNurseryBeforeDump'], [filename])", +" Dump reachable and unreachable objects to the named file, or to stdout. If\n" +" 'collectNurseryBeforeDump' is specified, a minor GC is performed first,\n" +" otherwise objects in the nursery are ignored."), JS_FN_HELP("terminate", Terminate, 0, 0, "terminate()", diff --git a/js/src/jit-test/tests/gc/bug-913224.js b/js/src/jit-test/tests/gc/bug-913224.js new file mode 100644 index 000000000000..d410e1b9c35a --- /dev/null +++ b/js/src/jit-test/tests/gc/bug-913224.js @@ -0,0 +1 @@ +dumpHeapComplete(); diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index a986da04abf4..9a4cd432abf9 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -753,6 +753,9 @@ DumpHeapVisitCell(JSRuntime *rt, void *data, void *thing, static void DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind) { + if (gc::IsInsideNursery(trc->runtime, *thingp)) + return; + JSDumpHeapTracer *dtrc = static_cast(trc); char buffer[1024]; fprintf(dtrc->output, "> %p %c %s\n", *thingp, MarkDescriptor(*thingp), @@ -762,6 +765,9 @@ DumpHeapVisitChild(JSTracer *trc, void **thingp, JSGCTraceKind kind) static void DumpHeapVisitRoot(JSTracer *trc, void **thingp, JSGCTraceKind kind) { + if (gc::IsInsideNursery(trc->runtime, *thingp)) + return; + JSDumpHeapTracer *dtrc = static_cast(trc); char buffer[1024]; fprintf(dtrc->output, "%p %c %s\n", *thingp, MarkDescriptor(*thingp), @@ -769,10 +775,15 @@ DumpHeapVisitRoot(JSTracer *trc, void **thingp, JSGCTraceKind kind) } void -js::DumpHeapComplete(JSRuntime *rt, FILE *fp) +js::DumpHeapComplete(JSRuntime *rt, FILE *fp, js::DumpHeapNurseryBehaviour nurseryBehaviour) { JSDumpHeapTracer dtrc(fp); +#ifdef JSGC_GENERATIONAL + if (nurseryBehaviour == js::CollectNurseryBeforeDump) + MinorGC(rt, JS::gcreason::API); +#endif + JS_TracerInit(&dtrc, rt, DumpHeapVisitRoot); dtrc.eagerlyTraceWeakMaps = TraceWeakMapKeysValues; TraceRuntime(&dtrc); diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 9565a752e839..bdee8216caa4 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -255,12 +255,17 @@ GetCompartmentZone(JSCompartment *comp); typedef bool (* PreserveWrapperCallback)(JSContext *cx, JSObject *obj); +typedef enum { + CollectNurseryBeforeDump, + IgnoreNurseryObjects +} DumpHeapNurseryBehaviour; + /* * Dump the complete object graph of heap-allocated things. * fp is the file for the dump output. */ extern JS_FRIEND_API(void) -DumpHeapComplete(JSRuntime *rt, FILE *fp); +DumpHeapComplete(JSRuntime *rt, FILE *fp, DumpHeapNurseryBehaviour nurseryBehaviour); #ifdef JS_OLD_GETTER_SETTER_METHODS JS_FRIEND_API(bool) obj_defineGetter(JSContext *cx, unsigned argc, JS::Value *vp); diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp index 1db9dea13253..af2f4dc7c7c2 100644 --- a/xpcom/base/CycleCollectedJSRuntime.cpp +++ b/xpcom/base/CycleCollectedJSRuntime.cpp @@ -968,7 +968,7 @@ CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports) void CycleCollectedJSRuntime::DumpJSHeap(FILE* file) { - js::DumpHeapComplete(Runtime(), file); + js::DumpHeapComplete(Runtime(), file, js::CollectNurseryBeforeDump); }