Bug 1834537 - Part 6: Simplify GCRuntime::checkAllocatorState a little r=sfink
[gecko.git] / ipc / glue / UtilityProcessManager.cpp
blob0b1fb2dd86acf996081842f40552d82dbe21fd04
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "UtilityProcessManager.h"
8 #include "JSOracleParent.h"
9 #include "mozilla/ipc/UtilityProcessHost.h"
10 #include "mozilla/MemoryReportingProcess.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/ProfilerMarkers.h"
13 #include "mozilla/StaticPrefs_media.h"
14 #include "mozilla/SyncRunnable.h" // for LaunchUtilityProcess
15 #include "mozilla/ipc/UtilityProcessParent.h"
16 #include "mozilla/ipc/UtilityAudioDecoderChild.h"
17 #include "mozilla/ipc/UtilityAudioDecoderParent.h"
18 #include "mozilla/dom/ContentParent.h"
19 #include "mozilla/ipc/Endpoint.h"
20 #include "mozilla/ipc/UtilityProcessSandboxing.h"
21 #include "mozilla/ipc/ProcessChild.h"
22 #include "nsAppRunner.h"
23 #include "nsContentUtils.h"
25 #ifdef XP_WIN
26 # include "mozilla/dom/WindowsUtilsParent.h"
27 #endif
29 #include "mozilla/GeckoArgs.h"
31 namespace mozilla::ipc {
33 extern LazyLogModule gUtilityProcessLog;
34 #define LOGD(...) MOZ_LOG(gUtilityProcessLog, LogLevel::Debug, (__VA_ARGS__))
36 static StaticRefPtr<UtilityProcessManager> sSingleton;
38 static bool sXPCOMShutdown = false;
40 bool UtilityProcessManager::IsShutdown() const {
41 MOZ_ASSERT(NS_IsMainThread());
42 return sXPCOMShutdown || !sSingleton;
45 RefPtr<UtilityProcessManager> UtilityProcessManager::GetSingleton() {
46 MOZ_ASSERT(XRE_IsParentProcess());
47 MOZ_ASSERT(NS_IsMainThread());
49 if (!sXPCOMShutdown && sSingleton == nullptr) {
50 sSingleton = new UtilityProcessManager();
52 return sSingleton;
55 RefPtr<UtilityProcessManager> UtilityProcessManager::GetIfExists() {
56 MOZ_ASSERT(NS_IsMainThread());
57 return sSingleton;
60 UtilityProcessManager::UtilityProcessManager() : mObserver(new Observer(this)) {
61 LOGD("[%p] UtilityProcessManager::UtilityProcessManager", this);
63 // Start listening for pref changes so we can
64 // forward them to the process once it is running.
65 nsContentUtils::RegisterShutdownObserver(mObserver);
66 Preferences::AddStrongObserver(mObserver, "");
69 UtilityProcessManager::~UtilityProcessManager() {
70 LOGD("[%p] UtilityProcessManager::~UtilityProcessManager", this);
72 // The Utility process should ALL have already been shut down.
73 MOZ_ASSERT(NoMoreProcesses());
76 NS_IMPL_ISUPPORTS(UtilityProcessManager::Observer, nsIObserver);
78 UtilityProcessManager::Observer::Observer(
79 RefPtr<UtilityProcessManager> aManager)
80 : mManager(std::move(aManager)) {}
82 NS_IMETHODIMP
83 UtilityProcessManager::Observer::Observe(nsISupports* aSubject,
84 const char* aTopic,
85 const char16_t* aData) {
86 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
87 mManager->OnXPCOMShutdown();
88 } else if (!strcmp(aTopic, "nsPref:changed")) {
89 mManager->OnPreferenceChange(aData);
91 return NS_OK;
94 void UtilityProcessManager::OnXPCOMShutdown() {
95 LOGD("[%p] UtilityProcessManager::OnXPCOMShutdown", this);
97 MOZ_ASSERT(NS_IsMainThread());
98 sXPCOMShutdown = true;
99 nsContentUtils::UnregisterShutdownObserver(mObserver);
100 CleanShutdownAllProcesses();
103 void UtilityProcessManager::OnPreferenceChange(const char16_t* aData) {
104 MOZ_ASSERT(NS_IsMainThread());
105 if (NoMoreProcesses()) {
106 // Process hasn't been launched yet
107 return;
109 // We know prefs are ASCII here.
110 NS_LossyConvertUTF16toASCII strData(aData);
112 mozilla::dom::Pref pref(strData, /* isLocked */ false,
113 /* isSanitized */ false, Nothing(), Nothing());
114 Preferences::GetPreference(&pref, GeckoProcessType_Utility,
115 /* remoteType */ ""_ns);
117 for (auto& p : mProcesses) {
118 if (!p) {
119 continue;
122 if (p->mProcessParent) {
123 Unused << p->mProcessParent->SendPreferenceUpdate(pref);
124 } else if (IsProcessLaunching(p->mSandbox)) {
125 p->mQueuedPrefs.AppendElement(pref);
130 RefPtr<UtilityProcessManager::ProcessFields> UtilityProcessManager::GetProcess(
131 SandboxingKind aSandbox) {
132 if (!mProcesses[aSandbox]) {
133 return nullptr;
136 return mProcesses[aSandbox];
139 RefPtr<GenericNonExclusivePromise> UtilityProcessManager::LaunchProcess(
140 SandboxingKind aSandbox) {
141 LOGD("[%p] UtilityProcessManager::LaunchProcess SandboxingKind=%" PRIu64,
142 this, aSandbox);
144 MOZ_ASSERT(NS_IsMainThread());
146 if (IsShutdown()) {
147 NS_WARNING("Reject early LaunchProcess() for Shutdown");
148 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
149 __func__);
152 RefPtr<ProcessFields> p = GetProcess(aSandbox);
153 if (p && p->mNumProcessAttempts) {
154 // We failed to start the Utility process earlier, abort now.
155 NS_WARNING("Reject LaunchProcess() for earlier mNumProcessAttempts");
156 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
157 __func__);
160 if (p && p->mLaunchPromise && p->mProcess) {
161 return p->mLaunchPromise;
164 if (!p) {
165 p = new ProcessFields(aSandbox);
166 mProcesses[aSandbox] = p;
169 std::vector<std::string> extraArgs;
170 ProcessChild::AddPlatformBuildID(extraArgs);
171 geckoargs::sSandboxingKind.Put(aSandbox, extraArgs);
173 // The subprocess is launched asynchronously, so we
174 // wait for the promise to be resolved to acquire the IPDL actor.
175 p->mProcess = new UtilityProcessHost(aSandbox, this);
176 if (!p->mProcess->Launch(extraArgs)) {
177 p->mNumProcessAttempts++;
178 DestroyProcess(aSandbox);
179 NS_WARNING("Reject LaunchProcess() for mNumProcessAttempts++");
180 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE,
181 __func__);
184 RefPtr<UtilityProcessManager> self = this;
185 p->mLaunchPromise = p->mProcess->LaunchPromise()->Then(
186 GetMainThreadSerialEventTarget(), __func__,
187 [self, p, aSandbox](bool) {
188 if (self->IsShutdown()) {
189 NS_WARNING(
190 "Reject LaunchProcess() after LaunchPromise() for Shutdown");
191 return GenericNonExclusivePromise::CreateAndReject(
192 NS_ERROR_NOT_AVAILABLE, __func__);
195 if (self->IsProcessDestroyed(aSandbox)) {
196 NS_WARNING(
197 "Reject LaunchProcess() after LaunchPromise() for destroyed "
198 "process");
199 return GenericNonExclusivePromise::CreateAndReject(
200 NS_ERROR_NOT_AVAILABLE, __func__);
203 p->mProcessParent = p->mProcess->GetActor();
205 // Flush any pref updates that happened during
206 // launch and weren't included in the blobs set
207 // up in LaunchUtilityProcess.
208 for (const mozilla::dom::Pref& pref : p->mQueuedPrefs) {
209 Unused << NS_WARN_IF(!p->mProcessParent->SendPreferenceUpdate(pref));
211 p->mQueuedPrefs.Clear();
213 CrashReporter::AnnotateCrashReport(
214 CrashReporter::Annotation::UtilityProcessStatus, "Running"_ns);
216 return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
218 [self, p, aSandbox](nsresult aError) {
219 if (GetSingleton()) {
220 p->mNumProcessAttempts++;
221 self->DestroyProcess(aSandbox);
223 NS_WARNING("Reject LaunchProcess() for LaunchPromise() rejection");
224 return GenericNonExclusivePromise::CreateAndReject(aError, __func__);
227 return p->mLaunchPromise;
230 template <typename Actor>
231 RefPtr<GenericNonExclusivePromise> UtilityProcessManager::StartUtility(
232 RefPtr<Actor> aActor, SandboxingKind aSandbox) {
233 LOGD(
234 "[%p] UtilityProcessManager::StartUtility actor=%p "
235 "SandboxingKind=%" PRIu64,
236 this, aActor.get(), aSandbox);
238 TimeStamp utilityStart = TimeStamp::Now();
240 if (!aActor) {
241 MOZ_ASSERT(false, "Actor singleton failure");
242 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
243 __func__);
246 if (aActor->CanSend()) {
247 // Actor has already been setup, so we:
248 // - know the process has been launched
249 // - the ipc actors are ready
250 PROFILER_MARKER_TEXT(
251 "UtilityProcessManager::StartUtility", IPC,
252 MarkerOptions(MarkerTiming::InstantNow()),
253 nsPrintfCString("SandboxingKind=%" PRIu64 " aActor->CanSend()",
254 aSandbox));
255 return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
258 RefPtr<UtilityProcessManager> self = this;
259 return LaunchProcess(aSandbox)->Then(
260 GetMainThreadSerialEventTarget(), __func__,
261 [self, aActor, aSandbox, utilityStart]() {
262 RefPtr<UtilityProcessParent> utilityParent =
263 self->GetProcessParent(aSandbox);
264 if (!utilityParent) {
265 NS_WARNING("Missing parent in StartUtility");
266 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
267 __func__);
270 // It is possible if multiple processes concurrently request a utility
271 // actor that the previous CanSend() check returned false for both but
272 // that by the time we have started our process for real, one of them
273 // has already been able to establish the IPC connection and thus we
274 // would perform more than one Open() call.
276 // The tests within browser_utility_multipleAudio.js should be able to
277 // catch that behavior.
278 if (!aActor->CanSend()) {
279 nsresult rv = aActor->BindToUtilityProcess(utilityParent);
280 if (NS_FAILED(rv)) {
281 MOZ_ASSERT(false, "Protocol endpoints failure");
282 return GenericNonExclusivePromise::CreateAndReject(rv, __func__);
285 MOZ_DIAGNOSTIC_ASSERT(aActor->CanSend(), "IPC established for actor");
286 self->RegisterActor(utilityParent, aActor->GetActorName());
289 PROFILER_MARKER_TEXT(
290 "UtilityProcessManager::StartUtility", IPC,
291 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(utilityStart)),
292 nsPrintfCString("SandboxingKind=%" PRIu64 " Resolve", aSandbox));
293 return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
295 [self, aSandbox, utilityStart](nsresult aError) {
296 NS_WARNING("Reject StartUtility() for LaunchProcess() rejection");
297 if (!self->IsShutdown()) {
298 NS_WARNING("Reject StartUtility() when !IsShutdown()");
300 PROFILER_MARKER_TEXT(
301 "UtilityProcessManager::StartUtility", IPC,
302 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(utilityStart)),
303 nsPrintfCString("SandboxingKind=%" PRIu64 " Reject", aSandbox));
304 return GenericNonExclusivePromise::CreateAndReject(aError, __func__);
308 RefPtr<UtilityProcessManager::StartRemoteDecodingUtilityPromise>
309 UtilityProcessManager::StartProcessForRemoteMediaDecoding(
310 base::ProcessId aOtherProcess, SandboxingKind aSandbox) {
311 // Not supported kinds.
312 if (aSandbox != SandboxingKind::GENERIC_UTILITY
313 #ifdef MOZ_APPLEMEDIA
314 && aSandbox != SandboxingKind::UTILITY_AUDIO_DECODING_APPLE_MEDIA
315 #endif
316 #ifdef XP_WIN
317 && aSandbox != SandboxingKind::UTILITY_AUDIO_DECODING_WMF
318 #endif
319 #ifdef MOZ_WMF_MEDIA_ENGINE
320 && aSandbox != SandboxingKind::MF_MEDIA_ENGINE_CDM
321 #endif
323 return StartRemoteDecodingUtilityPromise::CreateAndReject(NS_ERROR_FAILURE,
324 __func__);
326 TimeStamp remoteDecodingStart = TimeStamp::Now();
328 RefPtr<UtilityProcessManager> self = this;
329 RefPtr<UtilityAudioDecoderChild> uadc =
330 UtilityAudioDecoderChild::GetSingleton(aSandbox);
331 MOZ_ASSERT(uadc, "Unable to get a singleton for UtilityAudioDecoderChild");
332 return StartUtility(uadc, aSandbox)
333 ->Then(
334 GetMainThreadSerialEventTarget(), __func__,
335 [self, uadc, aOtherProcess, aSandbox, remoteDecodingStart]() {
336 RefPtr<UtilityProcessParent> parent =
337 self->GetProcessParent(aSandbox);
338 if (!parent) {
339 NS_WARNING("UtilityAudioDecoderParent lost in the middle");
340 return StartRemoteDecodingUtilityPromise::CreateAndReject(
341 NS_ERROR_FAILURE, __func__);
344 if (!uadc->CanSend()) {
345 NS_WARNING("UtilityAudioDecoderChild lost in the middle");
346 return StartRemoteDecodingUtilityPromise::CreateAndReject(
347 NS_ERROR_FAILURE, __func__);
350 base::ProcessId process = parent->OtherPid();
352 Endpoint<PRemoteDecoderManagerChild> childPipe;
353 Endpoint<PRemoteDecoderManagerParent> parentPipe;
354 nsresult rv = PRemoteDecoderManager::CreateEndpoints(
355 process, aOtherProcess, &parentPipe, &childPipe);
356 if (NS_FAILED(rv)) {
357 MOZ_ASSERT(false, "Could not create content remote decoder");
358 return StartRemoteDecodingUtilityPromise::CreateAndReject(
359 rv, __func__);
362 if (!uadc->SendNewContentRemoteDecoderManager(
363 std::move(parentPipe))) {
364 MOZ_ASSERT(false, "SendNewContentRemoteDecoderManager failure");
365 return StartRemoteDecodingUtilityPromise::CreateAndReject(
366 NS_ERROR_FAILURE, __func__);
369 #ifdef MOZ_WMF_MEDIA_ENGINE
370 if (aSandbox == SandboxingKind::MF_MEDIA_ENGINE_CDM &&
371 !uadc->CreateVideoBridge()) {
372 MOZ_ASSERT(false, "Failed to create video bridge");
373 return StartRemoteDecodingUtilityPromise::CreateAndReject(
374 NS_ERROR_FAILURE, __func__);
376 #endif
377 PROFILER_MARKER_TEXT(
378 "UtilityProcessManager::StartProcessForRemoteMediaDecoding",
379 MEDIA,
380 MarkerOptions(
381 MarkerTiming::IntervalUntilNowFrom(remoteDecodingStart)),
382 "Resolve"_ns);
383 return StartRemoteDecodingUtilityPromise::CreateAndResolve(
384 std::move(childPipe), __func__);
386 [self, remoteDecodingStart](nsresult aError) {
387 NS_WARNING(
388 "Reject StartProcessForRemoteMediaDecoding() for "
389 "StartUtility() rejection");
390 PROFILER_MARKER_TEXT(
391 "UtilityProcessManager::StartProcessForRemoteMediaDecoding",
392 MEDIA,
393 MarkerOptions(
394 MarkerTiming::IntervalUntilNowFrom(remoteDecodingStart)),
395 "Reject"_ns);
396 return StartRemoteDecodingUtilityPromise::CreateAndReject(aError,
397 __func__);
401 RefPtr<UtilityProcessManager::JSOraclePromise>
402 UtilityProcessManager::StartJSOracle(dom::JSOracleParent* aParent) {
403 return StartUtility(RefPtr{aParent}, SandboxingKind::GENERIC_UTILITY);
406 #ifdef XP_WIN
408 // Windows Utils
410 RefPtr<UtilityProcessManager::WindowsUtilsPromise>
411 UtilityProcessManager::GetWindowsUtilsPromise() {
412 TimeStamp windowsUtilsStart = TimeStamp::Now();
413 RefPtr<UtilityProcessManager> self = this;
414 if (!mWindowsUtils) {
415 mWindowsUtils = new dom::WindowsUtilsParent();
418 RefPtr<dom::WindowsUtilsParent> wup = mWindowsUtils;
419 MOZ_ASSERT(wup, "Unable to get a singleton for WindowsUtils");
420 return StartUtility(wup, SandboxingKind::WINDOWS_UTILS)
421 ->Then(
422 GetMainThreadSerialEventTarget(), __func__,
423 [self, wup, windowsUtilsStart]() {
424 if (!wup->CanSend()) {
425 MOZ_ASSERT(false, "WindowsUtilsParent can't send");
426 return WindowsUtilsPromise::CreateAndReject(NS_ERROR_FAILURE,
427 __func__);
429 PROFILER_MARKER_TEXT(
430 "UtilityProcessManager::GetWindowsUtilsPromise", OTHER,
431 MarkerOptions(
432 MarkerTiming::IntervalUntilNowFrom(windowsUtilsStart)),
433 "Resolve"_ns);
434 return WindowsUtilsPromise::CreateAndResolve(wup, __func__);
436 [self, windowsUtilsStart](nsresult aError) {
437 NS_WARNING("StartUtility rejected promise for PWindowsUtils");
438 PROFILER_MARKER_TEXT(
439 "UtilityProcessManager::GetWindowsUtilsPromise", OTHER,
440 MarkerOptions(
441 MarkerTiming::IntervalUntilNowFrom(windowsUtilsStart)),
442 "Reject"_ns);
443 return WindowsUtilsPromise::CreateAndReject(aError, __func__);
447 void UtilityProcessManager::ReleaseWindowsUtils() { mWindowsUtils = nullptr; }
449 #endif // XP_WIN
451 bool UtilityProcessManager::IsProcessLaunching(SandboxingKind aSandbox) {
452 MOZ_ASSERT(NS_IsMainThread());
454 RefPtr<ProcessFields> p = GetProcess(aSandbox);
455 if (!p) {
456 MOZ_CRASH("Cannot check process launching with no process");
457 return false;
460 return p->mProcess && !(p->mProcessParent);
463 bool UtilityProcessManager::IsProcessDestroyed(SandboxingKind aSandbox) {
464 MOZ_ASSERT(NS_IsMainThread());
465 RefPtr<ProcessFields> p = GetProcess(aSandbox);
466 if (!p) {
467 MOZ_CRASH("Cannot check process destroyed with no process");
468 return false;
470 return !p->mProcess && !p->mProcessParent;
473 void UtilityProcessManager::OnProcessUnexpectedShutdown(
474 UtilityProcessHost* aHost) {
475 MOZ_ASSERT(NS_IsMainThread());
477 for (auto& it : mProcesses) {
478 if (it && it->mProcess && it->mProcess == aHost) {
479 it->mNumUnexpectedCrashes++;
480 DestroyProcess(it->mSandbox);
481 return;
485 MOZ_CRASH(
486 "Called UtilityProcessManager::OnProcessUnexpectedShutdown with invalid "
487 "aHost");
490 void UtilityProcessManager::CleanShutdownAllProcesses() {
491 LOGD("[%p] UtilityProcessManager::CleanShutdownAllProcesses", this);
493 for (auto& it : mProcesses) {
494 if (it) {
495 DestroyProcess(it->mSandbox);
500 void UtilityProcessManager::CleanShutdown(SandboxingKind aSandbox) {
501 LOGD("[%p] UtilityProcessManager::CleanShutdown SandboxingKind=%" PRIu64,
502 this, aSandbox);
504 DestroyProcess(aSandbox);
507 uint16_t UtilityProcessManager::AliveProcesses() {
508 uint16_t alive = 0;
509 for (auto& p : mProcesses) {
510 if (p != nullptr) {
511 alive++;
514 return alive;
517 bool UtilityProcessManager::NoMoreProcesses() { return AliveProcesses() == 0; }
519 void UtilityProcessManager::DestroyProcess(SandboxingKind aSandbox) {
520 LOGD("[%p] UtilityProcessManager::DestroyProcess SandboxingKind=%" PRIu64,
521 this, aSandbox);
523 MOZ_RELEASE_ASSERT(NS_IsMainThread());
525 if (AliveProcesses() <= 1) {
526 if (mObserver) {
527 Preferences::RemoveObserver(mObserver, "");
530 mObserver = nullptr;
533 RefPtr<ProcessFields> p = GetProcess(aSandbox);
534 if (!p) {
535 return;
538 p->mQueuedPrefs.Clear();
539 p->mProcessParent = nullptr;
541 if (!p->mProcess) {
542 return;
545 p->mProcess->Shutdown();
546 p->mProcess = nullptr;
548 mProcesses[aSandbox] = nullptr;
550 CrashReporter::AnnotateCrashReport(
551 CrashReporter::Annotation::UtilityProcessStatus, "Destroyed"_ns);
553 if (NoMoreProcesses()) {
554 sSingleton = nullptr;
558 Maybe<base::ProcessId> UtilityProcessManager::ProcessPid(
559 SandboxingKind aSandbox) {
560 MOZ_ASSERT(NS_IsMainThread());
561 RefPtr<ProcessFields> p = GetProcess(aSandbox);
562 if (!p) {
563 return Nothing();
565 if (p->mProcessParent) {
566 return Some(p->mProcessParent->OtherPid());
568 return Nothing();
571 class UtilityMemoryReporter : public MemoryReportingProcess {
572 public:
573 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UtilityMemoryReporter, override)
575 explicit UtilityMemoryReporter(UtilityProcessParent* aParent) {
576 mParent = aParent;
579 bool IsAlive() const override { return bool(GetParent()); }
581 bool SendRequestMemoryReport(
582 const uint32_t& aGeneration, const bool& aAnonymize,
583 const bool& aMinimizeMemoryUsage,
584 const Maybe<ipc::FileDescriptor>& aDMDFile) override {
585 RefPtr<UtilityProcessParent> parent = GetParent();
586 if (!parent) {
587 return false;
590 return parent->SendRequestMemoryReport(aGeneration, aAnonymize,
591 aMinimizeMemoryUsage, aDMDFile);
594 int32_t Pid() const override {
595 if (RefPtr<UtilityProcessParent> parent = GetParent()) {
596 return (int32_t)parent->OtherPid();
598 return 0;
601 private:
602 RefPtr<UtilityProcessParent> GetParent() const { return mParent; }
604 RefPtr<UtilityProcessParent> mParent = nullptr;
606 protected:
607 ~UtilityMemoryReporter() = default;
610 RefPtr<MemoryReportingProcess> UtilityProcessManager::GetProcessMemoryReporter(
611 UtilityProcessParent* parent) {
612 return new UtilityMemoryReporter(parent);
615 } // namespace mozilla::ipc