зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1704500: Add utility process test helpers r=gerard-majax
Also makes the existing utility process test functions a bit more general. Depends on D162943 Differential Revision: https://phabricator.services.mozilla.com/D162944
This commit is contained in:
Родитель
7e4c5e8fba
Коммит
1a3ddd6ba2
|
@ -137,6 +137,14 @@ class UtilityProcessManager final : public UtilityProcessHost::Listener {
|
|||
return {};
|
||||
}
|
||||
|
||||
Span<const UtilityActorName> GetActors(SandboxingKind aSbKind) {
|
||||
auto proc = GetProcess(aSbKind);
|
||||
if (!proc) {
|
||||
return {};
|
||||
}
|
||||
return proc->mActors;
|
||||
}
|
||||
|
||||
// Shutdown the Utility process for that sandbox.
|
||||
void CleanShutdown(SandboxingKind aSandbox);
|
||||
|
||||
|
|
|
@ -44,10 +44,10 @@ add_setup(async function setup() {
|
|||
add_task(async function testKill() {
|
||||
await runTest("small-shot.ogg", "Utility Generic", "vorbis audio decoder");
|
||||
|
||||
const audioDecoderPid = await findGenericAudioDecoder();
|
||||
ok(audioDecoderPid > 0, `Valid PID found: ${audioDecoderPid}`);
|
||||
|
||||
await cleanUtilityProcessShutdown(audioDecoderPid, /* preferKill */ true);
|
||||
await cleanUtilityProcessShutdown(
|
||||
"audioDecoder_Generic",
|
||||
true /* preferKill */
|
||||
);
|
||||
|
||||
info("Waiting 15s to trigger mShutdownBlockers assertions");
|
||||
await new Promise((resolve, reject) => {
|
||||
|
@ -64,7 +64,7 @@ add_task(async function testShutdown() {
|
|||
const audioDecoderPid = await findGenericAudioDecoder();
|
||||
ok(audioDecoderPid > 0, `Valid PID found: ${audioDecoderPid}`);
|
||||
|
||||
await cleanUtilityProcessShutdown(audioDecoderPid);
|
||||
await cleanUtilityProcessShutdown("audioDecoder_Generic");
|
||||
|
||||
info("Waiting 15s to trigger mShutdownBlockers assertions");
|
||||
await new Promise((resolve, reject) => {
|
||||
|
|
|
@ -3,15 +3,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
async function startAndCrashUtility(actors, actorsCheck) {
|
||||
async function startAndCrashUtility(numUnknownActors, actorsCheck) {
|
||||
const actors = Array(numUnknownActors).fill("unknown");
|
||||
const utilityPid = await startUtilityProcess(actors);
|
||||
await crashSomeUtility(utilityPid, actorsCheck);
|
||||
}
|
||||
|
||||
// When running full suite, previous audio decoding tests might have left some
|
||||
// running and this might interfere with our testing
|
||||
// When running full suite, previous tests may have left some utility
|
||||
// processes running and this might interfere with our testing.
|
||||
add_setup(async function ensureNoExistingProcess() {
|
||||
await killPendingUtilityProcess();
|
||||
await killUtilityProcesses();
|
||||
});
|
||||
|
||||
add_task(async function utilityNoActor() {
|
||||
|
|
|
@ -4,27 +4,10 @@
|
|||
"use strict";
|
||||
|
||||
add_task(async () => {
|
||||
const utilityPid = await startUtilityProcess();
|
||||
await startUtilityProcess(["unknown"]);
|
||||
|
||||
SimpleTest.expectChildProcessCrash();
|
||||
|
||||
const utilityProcessGone = TestUtils.topicObserved("ipc:utility-shutdown");
|
||||
|
||||
info("Hard kill Utility Process");
|
||||
const ProcessTools = Cc["@mozilla.org/processtools-service;1"].getService(
|
||||
Ci.nsIProcessToolsService
|
||||
);
|
||||
ProcessTools.kill(utilityPid);
|
||||
|
||||
info(`Waiting for utility process ${utilityPid} to go away.`);
|
||||
let [subject, data] = await utilityProcessGone;
|
||||
ok(
|
||||
subject instanceof Ci.nsIPropertyBag2,
|
||||
"Subject needs to be a nsIPropertyBag2 to clean up properly"
|
||||
);
|
||||
is(
|
||||
parseInt(data, 10),
|
||||
utilityPid,
|
||||
`Should match the crashed PID ${utilityPid} with ${data}`
|
||||
);
|
||||
await cleanUtilityProcessShutdown("unknown", true /* preferKill */);
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// When running full suite, previous audio decoding tests might have left some
|
||||
// running and this might interfere with our testing
|
||||
add_setup(async function ensureNoExistingProcess() {
|
||||
await utilityProcessTest().stopProcess();
|
||||
await killUtilityProcesses();
|
||||
});
|
||||
|
||||
add_task(async () => {
|
||||
|
@ -72,5 +72,5 @@ add_task(async () => {
|
|||
"Collected some explicit/ report"
|
||||
);
|
||||
|
||||
await cleanUtilityProcessShutdown(utilityPid);
|
||||
await cleanUtilityProcessShutdown();
|
||||
});
|
||||
|
|
|
@ -10,10 +10,10 @@ Services.scriptloader.loadSubScript(
|
|||
this
|
||||
);
|
||||
|
||||
// When running full suite, previous audio decoding tests might have left some
|
||||
// running and this might interfere with our testing
|
||||
// When running full suite, previous tests may have left some utility
|
||||
// processes running and this might interfere with our testing.
|
||||
add_setup(async function ensureNoExistingProcess() {
|
||||
await killPendingUtilityProcess();
|
||||
await killUtilityProcesses();
|
||||
});
|
||||
|
||||
add_task(async () => {
|
||||
|
@ -72,5 +72,5 @@ add_task(async () => {
|
|||
|
||||
Services.profiler.StopProfiler();
|
||||
|
||||
await cleanUtilityProcessShutdown(utilityPid);
|
||||
await cleanUtilityProcessShutdown();
|
||||
});
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"use strict";
|
||||
|
||||
add_task(async () => {
|
||||
const pid = await startUtilityProcess();
|
||||
await cleanUtilityProcessShutdown(pid);
|
||||
await startUtilityProcess();
|
||||
await cleanUtilityProcessShutdown();
|
||||
});
|
||||
|
|
|
@ -12,14 +12,51 @@ const utilityProcessTest = () => {
|
|||
const kGenericUtilitySandbox = 0;
|
||||
const kGenericUtilityActor = "unknown";
|
||||
|
||||
async function startUtilityProcess(actors) {
|
||||
// Start a generic utility process with the given array of utility actor names
|
||||
// registered.
|
||||
async function startUtilityProcess(actors = []) {
|
||||
info("Start a UtilityProcess");
|
||||
return utilityProcessTest().startProcess(actors);
|
||||
}
|
||||
|
||||
async function cleanUtilityProcessShutdown(utilityPid, preferKill = false) {
|
||||
info(`CleanShutdown Utility Process ${utilityPid}`);
|
||||
ok(utilityPid !== undefined, "Utility needs to be defined");
|
||||
// Returns an array of process infos for utility processes of the given type
|
||||
// or all utility processes if actor is not defined.
|
||||
async function getUtilityProcesses(actor = undefined) {
|
||||
let procInfos = (await ChromeUtils.requestProcInfo()).children.filter(p => {
|
||||
return (
|
||||
p.type === "utility" &&
|
||||
(actor == undefined ||
|
||||
p.utilityActors.find(a => a.actorName.startsWith(actor)))
|
||||
);
|
||||
});
|
||||
|
||||
info(`Utility process infos = ${JSON.stringify(procInfos)}`);
|
||||
return procInfos;
|
||||
}
|
||||
|
||||
async function getUtilityPid(actor) {
|
||||
let process = await getUtilityProcesses(actor);
|
||||
is(process.length, 1, `exactly one ${actor} process exists`);
|
||||
return process[0].pid;
|
||||
}
|
||||
|
||||
async function checkUtilityExists(actor) {
|
||||
info(`Looking for a running ${actor} utility process`);
|
||||
const utilityPid = await getUtilityPid(actor);
|
||||
ok(utilityPid > 0, `Found ${actor} utility process ${utilityPid}`);
|
||||
return utilityPid;
|
||||
}
|
||||
|
||||
// "Cleanly stop" a utility process. This will never leave a crash dump file.
|
||||
// preferKill will "kill" the process (e.g. SIGABRT) instead of using the
|
||||
// UtilityProcessManager.
|
||||
// To "crash" -- i.e. shutdown and generate a crash dump -- use
|
||||
// crashSomeUtility().
|
||||
async function cleanUtilityProcessShutdown(actor, preferKill = false) {
|
||||
info(`${preferKill ? "Kill" : "Clean shutdown"} Utility Process ${actor}`);
|
||||
|
||||
const utilityPid = await getUtilityPid(actor);
|
||||
ok(utilityPid !== undefined, `Must have PID for ${actor} utility process`);
|
||||
|
||||
const utilityProcessGone = TestUtils.topicObserved(
|
||||
"ipc:utility-shutdown",
|
||||
|
@ -34,7 +71,8 @@ async function cleanUtilityProcessShutdown(utilityPid, preferKill = false) {
|
|||
);
|
||||
ProcessTools.kill(utilityPid);
|
||||
} else {
|
||||
await utilityProcessTest().stopProcess();
|
||||
info(`Stopping Utility Process ${utilityPid}`);
|
||||
await utilityProcessTest().stopProcess(actor);
|
||||
}
|
||||
|
||||
let [subject, data] = await utilityProcessGone;
|
||||
|
@ -51,22 +89,13 @@ async function cleanUtilityProcessShutdown(utilityPid, preferKill = false) {
|
|||
ok(!subject.hasKey("dumpID"), "There should be no dumpID");
|
||||
}
|
||||
|
||||
async function killPendingUtilityProcess() {
|
||||
let audioDecoderProcesses = (
|
||||
await ChromeUtils.requestProcInfo()
|
||||
).children.filter(p => {
|
||||
return (
|
||||
p.type === "utility" &&
|
||||
p.utilityActors.find(a => a.actorName.startsWith("audioDecoder_Generic"))
|
||||
);
|
||||
});
|
||||
info(`audioDecoderProcesses=${JSON.stringify(audioDecoderProcesses)}`);
|
||||
for (let audioDecoderProcess of audioDecoderProcesses) {
|
||||
info(`Stopping audio decoder PID ${audioDecoderProcess.pid}`);
|
||||
await cleanUtilityProcessShutdown(
|
||||
audioDecoderProcess.pid,
|
||||
/* preferKill */ true
|
||||
);
|
||||
async function killUtilityProcesses() {
|
||||
let utilityProcesses = await getUtilityProcesses();
|
||||
for (const utilityProcess of utilityProcesses) {
|
||||
for (const actor of utilityProcess.utilityActors) {
|
||||
info(`Stopping ${actor.actorName} utility process`);
|
||||
await cleanUtilityProcessShutdown(actor.actorName, /* preferKill */ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,3 +395,22 @@ async function crashSomeUtility(utilityPid, actorsCheck) {
|
|||
extrafile.remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Crash a utility process and generate a crash dump. To close a utility
|
||||
// process (forcefully or not) without a generating a crash, use
|
||||
// cleanUtilityProcessShutdown.
|
||||
async function crashSomeUtilityActor(
|
||||
actor,
|
||||
actorsCheck = () => {
|
||||
return true;
|
||||
}
|
||||
) {
|
||||
// Get PID for utility type
|
||||
const procInfos = await getUtilityProcesses(actor);
|
||||
ok(
|
||||
procInfos.length == 1,
|
||||
`exactly one ${actor} utility process should be found`
|
||||
);
|
||||
const utilityPid = procInfos[0].pid;
|
||||
return crashSomeUtility(utilityPid, actorsCheck);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,51 @@
|
|||
|
||||
namespace mozilla::ipc {
|
||||
|
||||
static UtilityActorName UtilityActorNameFromString(
|
||||
const nsACString& aStringName) {
|
||||
using namespace mozilla::dom;
|
||||
|
||||
// We use WebIDLUtilityActorNames because UtilityActorNames is not designed
|
||||
// for iteration.
|
||||
for (size_t i = 0; i < WebIDLUtilityActorNameValues::Count; ++i) {
|
||||
auto idlName = static_cast<UtilityActorName>(i);
|
||||
const nsDependentCSubstring idlNameString(
|
||||
WebIDLUtilityActorNameValues::GetString(idlName));
|
||||
if (idlNameString.Equals(aStringName)) {
|
||||
return idlName;
|
||||
}
|
||||
}
|
||||
MOZ_CRASH("Unknown utility actor name");
|
||||
}
|
||||
|
||||
// Find the utility process with the given actor or any utility process if
|
||||
// the actor is UtilityActorName::EndGuard_.
|
||||
static SandboxingKind FindUtilityProcessWithActor(UtilityActorName aActorName) {
|
||||
RefPtr<UtilityProcessManager> utilityProc =
|
||||
UtilityProcessManager::GetSingleton();
|
||||
MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
|
||||
|
||||
for (size_t i = 0; i < SandboxingKind::COUNT; ++i) {
|
||||
auto sbKind = static_cast<SandboxingKind>(i);
|
||||
if (!utilityProc->Process(sbKind)) {
|
||||
continue;
|
||||
}
|
||||
if (aActorName == UtilityActorName::EndGuard_) {
|
||||
return sbKind;
|
||||
}
|
||||
for (auto actor : utilityProc->GetActors(sbKind)) {
|
||||
if (actor == aActorName) {
|
||||
return sbKind;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SandboxingKind::COUNT;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UtilityProcessTest::StartProcess(int32_t aUnknownActors, JSContext* aCx,
|
||||
UtilityProcessTest::StartProcess(const nsTArray<nsCString>& aActorsToRegister,
|
||||
JSContext* aCx,
|
||||
mozilla::dom::Promise** aOutPromise) {
|
||||
NS_ENSURE_ARG(aOutPromise);
|
||||
*aOutPromise = nullptr;
|
||||
|
@ -32,20 +75,19 @@ UtilityProcessTest::StartProcess(int32_t aUnknownActors, JSContext* aCx,
|
|||
UtilityProcessManager::GetSingleton();
|
||||
MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
|
||||
|
||||
auto actors = aActorsToRegister.Clone();
|
||||
|
||||
utilityProc->LaunchProcess(SandboxingKind::GENERIC_UTILITY)
|
||||
->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[promise, utilityProc, aUnknownActors]() {
|
||||
[promise, utilityProc, actors = std::move(actors)] {
|
||||
RefPtr<UtilityProcessParent> utilityParent =
|
||||
utilityProc->GetProcessParent(SandboxingKind::GENERIC_UTILITY);
|
||||
Maybe<int32_t> utilityPid =
|
||||
utilityProc->ProcessPid(SandboxingKind::GENERIC_UTILITY);
|
||||
if (aUnknownActors > 0) {
|
||||
RefPtr<UtilityProcessParent> utilityParent =
|
||||
utilityProc->GetProcessParent(
|
||||
SandboxingKind::GENERIC_UTILITY);
|
||||
for (int32_t i = 0; i < aUnknownActors; i++) {
|
||||
utilityProc->RegisterActor(utilityParent,
|
||||
UtilityActorName::Unknown);
|
||||
}
|
||||
for (size_t i = 0; i < actors.Length(); ++i) {
|
||||
auto uan = UtilityActorNameFromString(actors[i]);
|
||||
utilityProc->RegisterActor(utilityParent, uan);
|
||||
}
|
||||
if (utilityPid.isSome()) {
|
||||
promise->MaybeResolve(*utilityPid);
|
||||
|
@ -64,14 +106,31 @@ UtilityProcessTest::StartProcess(int32_t aUnknownActors, JSContext* aCx,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UtilityProcessTest::StopProcess() {
|
||||
UtilityProcessTest::StopProcess(const char* aActorName) {
|
||||
using namespace mozilla::dom;
|
||||
|
||||
SandboxingKind sbKind;
|
||||
if (aActorName) {
|
||||
const nsDependentCString actorStringName(aActorName);
|
||||
UtilityActorName actorName = UtilityActorNameFromString(actorStringName);
|
||||
sbKind = FindUtilityProcessWithActor(actorName);
|
||||
} else {
|
||||
sbKind = FindUtilityProcessWithActor(UtilityActorName::EndGuard_);
|
||||
}
|
||||
|
||||
if (sbKind == SandboxingKind::COUNT) {
|
||||
MOZ_ASSERT_UNREACHABLE(
|
||||
"Attempted to stop process for actor when no "
|
||||
"such process exists");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<UtilityProcessManager> utilityProc =
|
||||
UtilityProcessManager::GetSingleton();
|
||||
MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
|
||||
|
||||
utilityProc->CleanShutdown(SandboxingKind::GENERIC_UTILITY);
|
||||
Maybe<int32_t> utilityPid =
|
||||
utilityProc->ProcessPid(SandboxingKind::GENERIC_UTILITY);
|
||||
utilityProc->CleanShutdown(sbKind);
|
||||
Maybe<int32_t> utilityPid = utilityProc->ProcessPid(sbKind);
|
||||
MOZ_RELEASE_ASSERT(utilityPid.isNothing(),
|
||||
"Should not have a utility process PID anymore");
|
||||
|
||||
|
|
|
@ -12,16 +12,21 @@ interface nsIUtilityProcessTest : nsISupports
|
|||
* ** Test-only Method **
|
||||
*
|
||||
* Allowing to start Utility Process from JS code.
|
||||
*
|
||||
* actorsToAdd: An array of actor names, taken from WebIDLUtilityActorName.
|
||||
* Unlike normal utility processes, test processes launched this way do not
|
||||
* have any associated actor names unless specified here. Empty by default.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
Promise startProcess([optional] in int32_t unknownActors);
|
||||
Promise startProcess([optional] in Array<ACString> actorsToAdd);
|
||||
|
||||
/**
|
||||
* ** Test-only Method **
|
||||
*
|
||||
* Allowing to stop Utility Process from JS code.
|
||||
* Default behavior is to stop any utility process.
|
||||
*/
|
||||
void stopProcess();
|
||||
void stopProcess([optional] in string utilityActorName);
|
||||
|
||||
/**
|
||||
* ** Test-only Method **
|
||||
|
|
Загрузка…
Ссылка в новой задаче