diff --git a/ipc/glue/UtilityProcessParent.cpp b/ipc/glue/UtilityProcessParent.cpp index 2c64bc8854bf..8fa80c1086d6 100644 --- a/ipc/glue/UtilityProcessParent.cpp +++ b/ipc/glue/UtilityProcessParent.cpp @@ -15,6 +15,10 @@ #include "mozilla/ipc/ProcessChild.h" #include "mozilla/FOGIPC.h" +#include "nsHashPropertyBag.h" +#include "mozilla/Services.h" +#include "nsIObserverService.h" + namespace mozilla::ipc { static std::atomic sUtilityProcessParent; @@ -79,7 +83,24 @@ mozilla::ipc::IPCResult UtilityProcessParent::RecvFOGData(ByteBuf&& aBuf) { void UtilityProcessParent::ActorDestroy(ActorDestroyReason aWhy) { if (aWhy == AbnormalShutdown) { - GenerateCrashReport(OtherPid()); + nsAutoString dumpID; + GenerateCrashReport(OtherPid(), &dumpID); + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + RefPtr props = new nsHashPropertyBag(); + // It's okay for dumpID to be empty if there was no minidump generated + // tests like ipc/glue/test/browser/browser_utility_crashReporter.js are + // there to verify this + if (!dumpID.IsEmpty()) { + props->SetPropertyAsAString(u"dumpID"_ns, dumpID); + } + + nsAutoString pid; + pid.AppendInt(static_cast(OtherPid())); + obs->NotifyObservers((nsIPropertyBag2*)props, "ipc:utility-shutdown", + pid.get()); + } } mHost->OnChannelClosed(); diff --git a/ipc/glue/test/browser/browser.ini b/ipc/glue/test/browser/browser.ini index a239ce05a447..73defab8aa13 100644 --- a/ipc/glue/test/browser/browser.ini +++ b/ipc/glue/test/browser/browser.ini @@ -2,9 +2,12 @@ support-files = ../../../../tools/profiler/tests/shared-head.js +[browser_utility_crashReporter.js] +skip-if = !crashreporter [browser_utility_clean_shutdown.js] [browser_utility_hard_kill.js] [browser_utility_memoryReport.js] +skip-if = tsan # bug 1754554 [browser_utility_profiler.js] skip-if = tsan # from tools/profiler/tests/browser/browser.ini, timing out on profiler tests? [browser_utility_start.js] diff --git a/ipc/glue/test/browser/browser_utility_crashReporter.js b/ipc/glue/test/browser/browser_utility_crashReporter.js new file mode 100644 index 000000000000..283b17c9854b --- /dev/null +++ b/ipc/glue/test/browser/browser_utility_crashReporter.js @@ -0,0 +1,83 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var utilityPid = undefined; +const utilityProcessTest = Cc[ + "@mozilla.org/utility-process-test;1" +].createInstance(Ci.nsIUtilityProcessTest); + +add_task(async () => { + await utilityProcessTest + .startProcess() + .then(async pid => { + utilityPid = pid; + ok(true, "Could start Utility process: " + pid); + }) + .catch(async () => { + ok(false, "Cannot start Utility process?"); + }); +}); + +add_task(async () => { + SimpleTest.expectChildProcessCrash(); + + const crashMan = Services.crashmanager; + const utilityProcessGone = TestUtils.topicObserved("ipc:utility-shutdown"); + + info("prune any previous crashes"); + const future = new Date(Date.now() + 1000 * 60 * 60 * 24); + await crashMan.pruneOldCrashes(future); + + info("crash Utility Process"); + const ProcessTools = Cc["@mozilla.org/processtools-service;1"].getService( + Ci.nsIProcessToolsService + ); + ProcessTools.crash(utilityPid); + + info("Waiting for utility process to go away."); + let [subject, data] = await utilityProcessGone; + ok( + parseInt(data, 10) === utilityPid, + `Should match the crashed PID ${utilityPid} with ${data}` + ); + ok( + subject instanceof Ci.nsIPropertyBag2, + "Subject needs to be a nsIPropertyBag2 to clean up properly" + ); + + const dumpID = subject.getPropertyAsAString("dumpID"); + ok(dumpID, "There should be a dumpID"); + + await crashMan.ensureCrashIsPresent(dumpID); + await crashMan.getCrashes().then(crashes => { + is(crashes.length, 1, "There should be only one record"); + const crash = crashes[0]; + ok( + crash.isOfType( + crashMan.processTypes[Ci.nsIXULRuntime.PROCESS_TYPE_UTILITY], + crashMan.CRASH_TYPE_CRASH + ), + "Record should be a utility process crash" + ); + ok(crash.id === dumpID, "Record should have an ID"); + }); + + let minidumpDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile); + minidumpDirectory.append("minidumps"); + + let dumpfile = minidumpDirectory.clone(); + dumpfile.append(dumpID + ".dmp"); + if (dumpfile.exists()) { + info(`Removal of ${dumpfile.path}`); + dumpfile.remove(false); + } + + let extrafile = minidumpDirectory.clone(); + extrafile.append(dumpID + ".extra"); + info(`Removal of ${extrafile.path}`); + if (extrafile.exists()) { + extrafile.remove(false); + } +});