Line data Source code
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 :
7 : #include "mozilla/ProcessHangMonitor.h"
8 : #include "mozilla/ProcessHangMonitorIPC.h"
9 :
10 : #include "jsapi.h"
11 : #include "js/GCAPI.h"
12 :
13 : #include "mozilla/Atomics.h"
14 : #include "mozilla/BackgroundHangMonitor.h"
15 : #include "mozilla/dom/ContentParent.h"
16 : #include "mozilla/dom/Element.h"
17 : #include "mozilla/dom/ScriptSettings.h"
18 : #include "mozilla/dom/TabChild.h"
19 : #include "mozilla/dom/TabParent.h"
20 : #include "mozilla/ipc/TaskFactory.h"
21 : #include "mozilla/Monitor.h"
22 : #include "mozilla/plugins/PluginBridge.h"
23 : #include "mozilla/Preferences.h"
24 : #include "mozilla/Unused.h"
25 :
26 : #include "nsIFrameLoader.h"
27 : #include "nsIHangReport.h"
28 : #include "nsITabParent.h"
29 : #include "nsPluginHost.h"
30 : #include "nsThreadUtils.h"
31 : #ifdef MOZ_CRASHREPORTER
32 : #include "nsExceptionHandler.h"
33 : #endif
34 :
35 : #include "base/task.h"
36 : #include "base/thread.h"
37 :
38 : #ifdef XP_WIN
39 : // For IsDebuggerPresent()
40 : #include <windows.h>
41 : #endif
42 :
43 : using namespace mozilla;
44 : using namespace mozilla::dom;
45 : using namespace mozilla::ipc;
46 :
47 : /*
48 : * Basic architecture:
49 : *
50 : * Each process has its own ProcessHangMonitor singleton. This singleton exists
51 : * as long as there is at least one content process in the system. Each content
52 : * process has a HangMonitorChild and the chrome process has one
53 : * HangMonitorParent per process. Each process (including the chrome process)
54 : * runs a hang monitoring thread. The PHangMonitor actors are bound to this
55 : * thread so that they never block on the main thread.
56 : *
57 : * When the content process detects a hang, it posts a task to its hang thread,
58 : * which sends an IPC message to the hang thread in the parent. The parent
59 : * cancels any ongoing CPOW requests and then posts a runnable to the main
60 : * thread that notifies Firefox frontend code of the hang. The frontend code is
61 : * passed an nsIHangReport, which can be used to terminate the hang.
62 : *
63 : * If the user chooses to terminate a script, a task is posted to the chrome
64 : * process's hang monitoring thread, which sends an IPC message to the hang
65 : * thread in the content process. That thread sets a flag to indicate that JS
66 : * execution should be terminated the next time it hits the interrupt
67 : * callback. A similar scheme is used for debugging slow scripts. If a content
68 : * process or plug-in needs to be terminated, the chrome process does so
69 : * directly, without messaging the content process.
70 : */
71 :
72 : namespace {
73 :
74 : /* Child process objects */
75 :
76 : class HangMonitorChild
77 : : public PProcessHangMonitorChild
78 : {
79 : public:
80 : explicit HangMonitorChild(ProcessHangMonitor* aMonitor);
81 : ~HangMonitorChild() override;
82 :
83 : void Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint);
84 :
85 : typedef ProcessHangMonitor::SlowScriptAction SlowScriptAction;
86 : SlowScriptAction NotifySlowScript(nsITabChild* aTabChild,
87 : const char* aFileName);
88 : void NotifySlowScriptAsync(TabId aTabId,
89 : const nsCString& aFileName);
90 :
91 : bool IsDebuggerStartupComplete();
92 :
93 : void NotifyPluginHang(uint32_t aPluginId);
94 : void NotifyPluginHangAsync(uint32_t aPluginId);
95 :
96 : void ClearHang();
97 : void ClearHangAsync();
98 : void ClearForcePaint();
99 :
100 : mozilla::ipc::IPCResult RecvTerminateScript() override;
101 : mozilla::ipc::IPCResult RecvBeginStartingDebugger() override;
102 : mozilla::ipc::IPCResult RecvEndStartingDebugger() override;
103 :
104 : mozilla::ipc::IPCResult RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch) override;
105 :
106 : void ActorDestroy(ActorDestroyReason aWhy) override;
107 :
108 : void InterruptCallback();
109 : void Shutdown();
110 :
111 1713 : static HangMonitorChild* Get() { return sInstance; }
112 :
113 0 : void Dispatch(already_AddRefed<nsIRunnable> aRunnable)
114 : {
115 0 : mHangMonitor->Dispatch(Move(aRunnable));
116 0 : }
117 4 : bool IsOnThread() { return mHangMonitor->IsOnThread(); }
118 :
119 : private:
120 : void ShutdownOnThread();
121 :
122 : static Atomic<HangMonitorChild*> sInstance;
123 : UniquePtr<BackgroundHangMonitor> mForcePaintMonitor;
124 :
125 : const RefPtr<ProcessHangMonitor> mHangMonitor;
126 : Monitor mMonitor;
127 :
128 : // Main thread-only.
129 : bool mSentReport;
130 :
131 : // These fields must be accessed with mMonitor held.
132 : bool mTerminateScript;
133 : bool mStartDebugger;
134 : bool mFinishedStartingDebugger;
135 : bool mForcePaint;
136 : TabId mForcePaintTab;
137 : MOZ_INIT_OUTSIDE_CTOR uint64_t mForcePaintEpoch;
138 : JSContext* mContext;
139 : bool mShutdownDone;
140 :
141 : // This field is only accessed on the hang thread.
142 : bool mIPCOpen;
143 : };
144 :
145 : Atomic<HangMonitorChild*> HangMonitorChild::sInstance;
146 :
147 : /* Parent process objects */
148 :
149 : class HangMonitorParent;
150 :
151 : class HangMonitoredProcess final
152 : : public nsIHangReport
153 : {
154 : public:
155 : NS_DECL_THREADSAFE_ISUPPORTS
156 :
157 2 : HangMonitoredProcess(HangMonitorParent* aActor,
158 : ContentParent* aContentParent)
159 2 : : mActor(aActor), mContentParent(aContentParent) {}
160 :
161 : NS_IMETHOD GetHangType(uint32_t* aHangType) override;
162 : NS_IMETHOD GetScriptBrowser(nsIDOMElement** aBrowser) override;
163 : NS_IMETHOD GetScriptFileName(nsACString& aFileName) override;
164 :
165 : NS_IMETHOD GetPluginName(nsACString& aPluginName) override;
166 :
167 : NS_IMETHOD TerminateScript() override;
168 : NS_IMETHOD BeginStartingDebugger() override;
169 : NS_IMETHOD EndStartingDebugger() override;
170 : NS_IMETHOD TerminatePlugin() override;
171 : NS_IMETHOD UserCanceled() override;
172 :
173 : NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
174 :
175 : // Called when a content process shuts down.
176 0 : void Clear() {
177 0 : mContentParent = nullptr;
178 0 : mActor = nullptr;
179 0 : }
180 :
181 : /**
182 : * Sets the information associated with this hang: this includes the ID of
183 : * the plugin which caused the hang as well as the content PID. The ID of
184 : * a minidump taken during the hang can also be provided.
185 : *
186 : * @param aHangData The hang information
187 : * @param aDumpId The ID of a minidump taken when the hang occurred
188 : */
189 0 : void SetHangData(const HangData& aHangData, const nsAString& aDumpId) {
190 0 : mHangData = aHangData;
191 0 : mDumpId = aDumpId;
192 0 : }
193 :
194 0 : void ClearHang() {
195 0 : mHangData = HangData();
196 0 : mDumpId.Truncate();
197 0 : }
198 :
199 : private:
200 0 : ~HangMonitoredProcess() = default;
201 :
202 : // Everything here is main thread-only.
203 : HangMonitorParent* mActor;
204 : ContentParent* mContentParent;
205 : HangData mHangData;
206 : nsAutoString mDumpId;
207 : };
208 :
209 : class HangMonitorParent
210 : : public PProcessHangMonitorParent
211 : {
212 : public:
213 : explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
214 : ~HangMonitorParent() override;
215 :
216 : void Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint);
217 :
218 : mozilla::ipc::IPCResult RecvHangEvidence(const HangData& aHangData) override;
219 : mozilla::ipc::IPCResult RecvClearHang() override;
220 :
221 : void ActorDestroy(ActorDestroyReason aWhy) override;
222 :
223 2 : void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
224 :
225 : void Shutdown();
226 :
227 : void ForcePaint(dom::TabParent* aTabParent, uint64_t aLayerObserverEpoch);
228 :
229 : void TerminateScript();
230 : void BeginStartingDebugger();
231 : void EndStartingDebugger();
232 : void CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles);
233 :
234 : /**
235 : * Update the dump for the specified plugin. This method is thread-safe and
236 : * is used to replace a browser minidump with a full minidump. If aDumpId is
237 : * empty this is a no-op.
238 : */
239 : void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId);
240 :
241 2 : void Dispatch(already_AddRefed<nsIRunnable> aRunnable)
242 : {
243 2 : mHangMonitor->Dispatch(Move(aRunnable));
244 2 : }
245 4 : bool IsOnThread() { return mHangMonitor->IsOnThread(); }
246 :
247 : private:
248 : bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
249 :
250 : void SendHangNotification(const HangData& aHangData,
251 : const nsString& aBrowserDumpId,
252 : bool aTakeMinidump);
253 : void OnTakeFullMinidumpComplete(const HangData& aHangData,
254 : const nsString& aDumpId);
255 :
256 : void ClearHangNotification();
257 :
258 : void ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch);
259 :
260 : void ShutdownOnThread();
261 :
262 : const RefPtr<ProcessHangMonitor> mHangMonitor;
263 :
264 : // This field is read-only after construction.
265 : bool mReportHangs;
266 :
267 : // This field is only accessed on the hang thread.
268 : bool mIPCOpen;
269 :
270 : Monitor mMonitor;
271 :
272 : // Must be accessed with mMonitor held.
273 : RefPtr<HangMonitoredProcess> mProcess;
274 : bool mShutdownDone;
275 : // Map from plugin ID to crash dump ID. Protected by mBrowserCrashDumpHashLock.
276 : nsDataHashtable<nsUint32HashKey, nsString> mBrowserCrashDumpIds;
277 : Mutex mBrowserCrashDumpHashLock;
278 : mozilla::ipc::TaskFactory<HangMonitorParent> mMainThreadTaskFactory;
279 :
280 : static bool sShouldForcePaint;
281 : };
282 :
283 : bool HangMonitorParent::sShouldForcePaint = true;
284 :
285 : } // namespace
286 :
287 : /* HangMonitorChild implementation */
288 :
289 2 : HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
290 : : mHangMonitor(aMonitor),
291 : mMonitor("HangMonitorChild lock"),
292 : mSentReport(false),
293 : mTerminateScript(false),
294 : mStartDebugger(false),
295 : mFinishedStartingDebugger(false),
296 : mForcePaint(false),
297 : mShutdownDone(false),
298 2 : mIPCOpen(true)
299 : {
300 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
301 2 : mContext = danger::GetJSContext();
302 : mForcePaintMonitor =
303 4 : MakeUnique<mozilla::BackgroundHangMonitor>("Gecko_Child_ForcePaint",
304 : 128, /* ms timeout for microhangs */
305 : 1024, /* ms timeout for permahangs */
306 2 : BackgroundHangMonitor::THREAD_PRIVATE);
307 2 : }
308 :
309 0 : HangMonitorChild::~HangMonitorChild()
310 : {
311 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
312 0 : MOZ_ASSERT(sInstance == this);
313 0 : mForcePaintMonitor = nullptr;
314 0 : sInstance = nullptr;
315 0 : }
316 :
317 : void
318 2 : HangMonitorChild::InterruptCallback()
319 : {
320 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
321 :
322 : bool forcePaint;
323 2 : TabId forcePaintTab;
324 : uint64_t forcePaintEpoch;
325 :
326 : {
327 4 : MonitorAutoLock lock(mMonitor);
328 2 : forcePaint = mForcePaint;
329 2 : forcePaintTab = mForcePaintTab;
330 2 : forcePaintEpoch = mForcePaintEpoch;
331 :
332 2 : mForcePaint = false;
333 : }
334 :
335 2 : if (forcePaint) {
336 2 : RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
337 1 : if (tabChild) {
338 2 : js::AutoAssertNoContentJS nojs(mContext);
339 1 : tabChild->ForcePaint(forcePaintEpoch);
340 : }
341 : }
342 2 : }
343 :
344 : void
345 0 : HangMonitorChild::Shutdown()
346 : {
347 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
348 :
349 0 : MonitorAutoLock lock(mMonitor);
350 0 : while (!mShutdownDone) {
351 0 : mMonitor.Wait();
352 : }
353 0 : }
354 :
355 : void
356 0 : HangMonitorChild::ShutdownOnThread()
357 : {
358 0 : MOZ_RELEASE_ASSERT(IsOnThread());
359 :
360 0 : MonitorAutoLock lock(mMonitor);
361 0 : mShutdownDone = true;
362 0 : mMonitor.Notify();
363 0 : }
364 :
365 : void
366 0 : HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy)
367 : {
368 0 : MOZ_RELEASE_ASSERT(IsOnThread());
369 :
370 0 : mIPCOpen = false;
371 :
372 : // We use a task here to ensure that IPDL is finished with this
373 : // HangMonitorChild before it gets deleted on the main thread.
374 0 : Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ShutdownOnThread",
375 : this,
376 0 : &HangMonitorChild::ShutdownOnThread));
377 0 : }
378 :
379 : mozilla::ipc::IPCResult
380 0 : HangMonitorChild::RecvTerminateScript()
381 : {
382 0 : MOZ_RELEASE_ASSERT(IsOnThread());
383 :
384 0 : MonitorAutoLock lock(mMonitor);
385 0 : mTerminateScript = true;
386 0 : return IPC_OK();
387 : }
388 :
389 : mozilla::ipc::IPCResult
390 0 : HangMonitorChild::RecvBeginStartingDebugger()
391 : {
392 0 : MOZ_RELEASE_ASSERT(IsOnThread());
393 :
394 0 : MonitorAutoLock lock(mMonitor);
395 0 : mStartDebugger = true;
396 0 : return IPC_OK();
397 : }
398 :
399 : mozilla::ipc::IPCResult
400 0 : HangMonitorChild::RecvEndStartingDebugger()
401 : {
402 0 : MOZ_RELEASE_ASSERT(IsOnThread());
403 :
404 0 : MonitorAutoLock lock(mMonitor);
405 0 : mFinishedStartingDebugger = true;
406 0 : return IPC_OK();
407 : }
408 :
409 : mozilla::ipc::IPCResult
410 2 : HangMonitorChild::RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch)
411 : {
412 2 : MOZ_RELEASE_ASSERT(IsOnThread());
413 :
414 2 : mForcePaintMonitor->NotifyActivity();
415 :
416 : {
417 4 : MonitorAutoLock lock(mMonitor);
418 2 : mForcePaint = true;
419 2 : mForcePaintTab = aTabId;
420 2 : mForcePaintEpoch = aLayerObserverEpoch;
421 : }
422 :
423 2 : JS_RequestInterruptCallback(mContext);
424 :
425 2 : return IPC_OK();
426 : }
427 :
428 : void
429 2 : HangMonitorChild::ClearForcePaint()
430 : {
431 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
432 2 : MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
433 :
434 2 : mForcePaintMonitor->NotifyWait();
435 2 : }
436 :
437 : void
438 2 : HangMonitorChild::Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint)
439 : {
440 2 : MOZ_RELEASE_ASSERT(IsOnThread());
441 :
442 2 : MOZ_ASSERT(!sInstance);
443 2 : sInstance = this;
444 :
445 4 : DebugOnly<bool> ok = aEndpoint.Bind(this);
446 2 : MOZ_ASSERT(ok);
447 2 : }
448 :
449 : void
450 0 : HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
451 : const nsCString& aFileName)
452 : {
453 0 : if (mIPCOpen) {
454 0 : Unused << SendHangEvidence(SlowScriptData(aTabId, aFileName));
455 : }
456 0 : }
457 :
458 : HangMonitorChild::SlowScriptAction
459 0 : HangMonitorChild::NotifySlowScript(nsITabChild* aTabChild,
460 : const char* aFileName)
461 : {
462 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
463 :
464 0 : mSentReport = true;
465 :
466 : {
467 0 : MonitorAutoLock lock(mMonitor);
468 :
469 0 : if (mTerminateScript) {
470 0 : mTerminateScript = false;
471 0 : return SlowScriptAction::Terminate;
472 : }
473 :
474 0 : if (mStartDebugger) {
475 0 : mStartDebugger = false;
476 0 : return SlowScriptAction::StartDebugger;
477 : }
478 : }
479 :
480 0 : TabId id;
481 0 : if (aTabChild) {
482 0 : RefPtr<TabChild> tabChild = static_cast<TabChild*>(aTabChild);
483 0 : id = tabChild->GetTabId();
484 : }
485 0 : nsAutoCString filename(aFileName);
486 :
487 0 : Dispatch(NewNonOwningRunnableMethod<TabId, nsCString>(
488 : "HangMonitorChild::NotifySlowScriptAsync",
489 : this,
490 : &HangMonitorChild::NotifySlowScriptAsync,
491 : id,
492 0 : filename));
493 0 : return SlowScriptAction::Continue;
494 : }
495 :
496 : bool
497 0 : HangMonitorChild::IsDebuggerStartupComplete()
498 : {
499 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
500 :
501 0 : MonitorAutoLock lock(mMonitor);
502 :
503 0 : if (mFinishedStartingDebugger) {
504 0 : mFinishedStartingDebugger = false;
505 0 : return true;
506 : }
507 :
508 0 : return false;
509 : }
510 :
511 : void
512 0 : HangMonitorChild::NotifyPluginHang(uint32_t aPluginId)
513 : {
514 : // main thread in the child
515 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
516 :
517 0 : mSentReport = true;
518 :
519 : // bounce to background thread
520 0 : Dispatch(NewNonOwningRunnableMethod<uint32_t>(
521 : "HangMonitorChild::NotifyPluginHangAsync",
522 : this,
523 : &HangMonitorChild::NotifyPluginHangAsync,
524 0 : aPluginId));
525 0 : }
526 :
527 : void
528 0 : HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId)
529 : {
530 0 : MOZ_RELEASE_ASSERT(IsOnThread());
531 :
532 : // bounce back to parent on background thread
533 0 : if (mIPCOpen) {
534 0 : Unused << SendHangEvidence(PluginHangData(aPluginId,
535 0 : base::GetCurrentProcId()));
536 : }
537 0 : }
538 :
539 : void
540 369 : HangMonitorChild::ClearHang()
541 : {
542 369 : MOZ_ASSERT(NS_IsMainThread());
543 :
544 369 : if (mSentReport) {
545 : // bounce to background thread
546 0 : Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ClearHangAsync",
547 : this,
548 0 : &HangMonitorChild::ClearHangAsync));
549 :
550 0 : MonitorAutoLock lock(mMonitor);
551 0 : mSentReport = false;
552 0 : mTerminateScript = false;
553 0 : mStartDebugger = false;
554 0 : mFinishedStartingDebugger = false;
555 : }
556 369 : }
557 :
558 : void
559 0 : HangMonitorChild::ClearHangAsync()
560 : {
561 0 : MOZ_RELEASE_ASSERT(IsOnThread());
562 :
563 : // bounce back to parent on background thread
564 0 : if (mIPCOpen) {
565 0 : Unused << SendClearHang();
566 : }
567 0 : }
568 :
569 : /* HangMonitorParent implementation */
570 :
571 2 : HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor)
572 : : mHangMonitor(aMonitor),
573 : mIPCOpen(true),
574 : mMonitor("HangMonitorParent lock"),
575 : mShutdownDone(false),
576 : mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock"),
577 2 : mMainThreadTaskFactory(this)
578 : {
579 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
580 2 : mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false);
581 :
582 : static bool sInited = false;
583 2 : if (!sInited) {
584 1 : sInited = true;
585 : Preferences::AddBoolVarCache(&sShouldForcePaint,
586 1 : "browser.tabs.remote.force-paint", true);
587 : }
588 2 : }
589 :
590 0 : HangMonitorParent::~HangMonitorParent()
591 : {
592 : #ifdef MOZ_CRASHREPORTER
593 0 : MutexAutoLock lock(mBrowserCrashDumpHashLock);
594 :
595 0 : for (auto iter = mBrowserCrashDumpIds.Iter(); !iter.Done(); iter.Next()) {
596 0 : nsString crashId = iter.UserData();
597 0 : if (!crashId.IsEmpty()) {
598 0 : CrashReporter::DeleteMinidumpFilesForID(crashId);
599 : }
600 : }
601 : #endif
602 0 : }
603 :
604 : void
605 0 : HangMonitorParent::Shutdown()
606 : {
607 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
608 :
609 0 : MonitorAutoLock lock(mMonitor);
610 :
611 0 : if (mProcess) {
612 0 : mProcess->Clear();
613 0 : mProcess = nullptr;
614 : }
615 :
616 0 : Dispatch(NewNonOwningRunnableMethod("HangMonitorParent::ShutdownOnThread",
617 : this,
618 0 : &HangMonitorParent::ShutdownOnThread));
619 :
620 0 : while (!mShutdownDone) {
621 0 : mMonitor.Wait();
622 : }
623 0 : }
624 :
625 : void
626 0 : HangMonitorParent::ShutdownOnThread()
627 : {
628 0 : MOZ_RELEASE_ASSERT(IsOnThread());
629 :
630 : // mIPCOpen is only written from this thread, so need need to take the lock
631 : // here. We'd be shooting ourselves in the foot, because ActorDestroy takes
632 : // it.
633 0 : if (mIPCOpen) {
634 0 : Close();
635 : }
636 :
637 0 : MonitorAutoLock lock(mMonitor);
638 0 : mShutdownDone = true;
639 0 : mMonitor.Notify();
640 0 : }
641 :
642 : void
643 2 : HangMonitorParent::ForcePaint(dom::TabParent* aTab, uint64_t aLayerObserverEpoch)
644 : {
645 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
646 2 : if (sShouldForcePaint) {
647 2 : TabId id = aTab->GetTabId();
648 4 : Dispatch(NewNonOwningRunnableMethod<TabId, uint64_t>(
649 : "HangMonitorParent::ForcePaintOnThread",
650 : this,
651 : &HangMonitorParent::ForcePaintOnThread,
652 : id,
653 2 : aLayerObserverEpoch));
654 : }
655 2 : }
656 :
657 : void
658 2 : HangMonitorParent::ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch)
659 : {
660 2 : MOZ_RELEASE_ASSERT(IsOnThread());
661 :
662 2 : if (mIPCOpen) {
663 2 : Unused << SendForcePaint(aTabId, aLayerObserverEpoch);
664 : }
665 2 : }
666 :
667 : void
668 0 : HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy)
669 : {
670 0 : MOZ_RELEASE_ASSERT(IsOnThread());
671 0 : mIPCOpen = false;
672 0 : }
673 :
674 : void
675 2 : HangMonitorParent::Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint)
676 : {
677 2 : MOZ_RELEASE_ASSERT(IsOnThread());
678 :
679 4 : DebugOnly<bool> ok = aEndpoint.Bind(this);
680 2 : MOZ_ASSERT(ok);
681 2 : }
682 :
683 : void
684 0 : HangMonitorParent::SendHangNotification(const HangData& aHangData,
685 : const nsString& aBrowserDumpId,
686 : bool aTakeMinidump)
687 : {
688 : // chrome process, main thread
689 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
690 :
691 0 : if ((aHangData.type() == HangData::TPluginHangData) && aTakeMinidump) {
692 : // We've been handed a partial minidump; complete it with plugin and
693 : // content process dumps.
694 0 : const PluginHangData& phd = aHangData.get_PluginHangData();
695 :
696 : std::function<void(nsString)> callback =
697 0 : [this, aHangData](nsString aResult) {
698 0 : this->UpdateMinidump(aHangData.get_PluginHangData().pluginId(),
699 0 : aResult);
700 0 : this->OnTakeFullMinidumpComplete(aHangData, aResult);
701 0 : };
702 :
703 0 : plugins::TakeFullMinidump(phd.pluginId(),
704 0 : phd.contentProcessId(),
705 : aBrowserDumpId,
706 0 : Move(callback),
707 0 : true);
708 : } else {
709 : // We already have a full minidump; go ahead and use it.
710 0 : OnTakeFullMinidumpComplete(aHangData, aBrowserDumpId);
711 : }
712 0 : }
713 :
714 : void
715 0 : HangMonitorParent::OnTakeFullMinidumpComplete(const HangData& aHangData,
716 : const nsString& aDumpId)
717 : {
718 0 : mProcess->SetHangData(aHangData, aDumpId);
719 :
720 : nsCOMPtr<nsIObserverService> observerService =
721 0 : mozilla::services::GetObserverService();
722 0 : observerService->NotifyObservers(mProcess,
723 : "process-hang-report",
724 0 : nullptr);
725 0 : }
726 :
727 : void
728 0 : HangMonitorParent::ClearHangNotification()
729 : {
730 : // chrome process, main thread
731 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
732 0 : mProcess->ClearHang();
733 :
734 : nsCOMPtr<nsIObserverService> observerService =
735 0 : mozilla::services::GetObserverService();
736 0 : observerService->NotifyObservers(mProcess, "clear-hang-report", nullptr);
737 0 : }
738 :
739 : // Take a minidump of the browser process if one wasn't already taken for the
740 : // plugin that caused the hang. Return false if a dump was already available or
741 : // true if new one has been taken.
742 : bool
743 0 : HangMonitorParent::TakeBrowserMinidump(const PluginHangData& aPhd,
744 : nsString& aCrashId)
745 : {
746 : #ifdef MOZ_CRASHREPORTER
747 0 : MutexAutoLock lock(mBrowserCrashDumpHashLock);
748 0 : if (!mBrowserCrashDumpIds.Get(aPhd.pluginId(), &aCrashId)) {
749 0 : nsCOMPtr<nsIFile> browserDump;
750 0 : if (CrashReporter::TakeMinidump(getter_AddRefs(browserDump), true)) {
751 0 : if (!CrashReporter::GetIDFromMinidump(browserDump, aCrashId)
752 0 : || aCrashId.IsEmpty()) {
753 0 : browserDump->Remove(false);
754 : NS_WARNING("Failed to generate timely browser stack, "
755 0 : "this is bad for plugin hang analysis!");
756 : } else {
757 0 : mBrowserCrashDumpIds.Put(aPhd.pluginId(), aCrashId);
758 0 : return true;
759 : }
760 : }
761 : }
762 : #endif // MOZ_CRASHREPORTER
763 :
764 0 : return false;
765 : }
766 :
767 : mozilla::ipc::IPCResult
768 0 : HangMonitorParent::RecvHangEvidence(const HangData& aHangData)
769 : {
770 : // chrome process, background thread
771 0 : MOZ_RELEASE_ASSERT(IsOnThread());
772 :
773 0 : if (!mReportHangs) {
774 0 : return IPC_OK();
775 : }
776 :
777 : #ifdef XP_WIN
778 : // Don't report hangs if we're debugging the process. You can comment this
779 : // line out for testing purposes.
780 : if (IsDebuggerPresent()) {
781 : return IPC_OK();
782 : }
783 : #endif
784 :
785 : // Before we wake up the browser main thread we want to take a
786 : // browser minidump.
787 0 : nsAutoString crashId;
788 0 : bool takeMinidump = false;
789 0 : if (aHangData.type() == HangData::TPluginHangData) {
790 0 : takeMinidump = TakeBrowserMinidump(aHangData.get_PluginHangData(), crashId);
791 : }
792 :
793 0 : mHangMonitor->InitiateCPOWTimeout();
794 :
795 0 : MonitorAutoLock lock(mMonitor);
796 :
797 0 : NS_DispatchToMainThread(
798 0 : mMainThreadTaskFactory.NewRunnableMethod(
799 : &HangMonitorParent::SendHangNotification, aHangData, crashId,
800 0 : takeMinidump));
801 :
802 0 : return IPC_OK();
803 : }
804 :
805 : mozilla::ipc::IPCResult
806 0 : HangMonitorParent::RecvClearHang()
807 : {
808 : // chrome process, background thread
809 0 : MOZ_RELEASE_ASSERT(IsOnThread());
810 :
811 0 : if (!mReportHangs) {
812 0 : return IPC_OK();
813 : }
814 :
815 0 : mHangMonitor->InitiateCPOWTimeout();
816 :
817 0 : MonitorAutoLock lock(mMonitor);
818 :
819 0 : NS_DispatchToMainThread(
820 0 : mMainThreadTaskFactory.NewRunnableMethod(
821 0 : &HangMonitorParent::ClearHangNotification));
822 :
823 0 : return IPC_OK();
824 : }
825 :
826 : void
827 0 : HangMonitorParent::TerminateScript()
828 : {
829 0 : MOZ_RELEASE_ASSERT(IsOnThread());
830 :
831 0 : if (mIPCOpen) {
832 0 : Unused << SendTerminateScript();
833 : }
834 0 : }
835 :
836 : void
837 0 : HangMonitorParent::BeginStartingDebugger()
838 : {
839 0 : MOZ_RELEASE_ASSERT(IsOnThread());
840 :
841 0 : if (mIPCOpen) {
842 0 : Unused << SendBeginStartingDebugger();
843 : }
844 0 : }
845 :
846 : void
847 0 : HangMonitorParent::EndStartingDebugger()
848 : {
849 0 : MOZ_RELEASE_ASSERT(IsOnThread());
850 :
851 0 : if (mIPCOpen) {
852 0 : Unused << SendEndStartingDebugger();
853 : }
854 0 : }
855 :
856 : void
857 0 : HangMonitorParent::CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles)
858 : {
859 0 : MutexAutoLock lock(mBrowserCrashDumpHashLock);
860 0 : nsAutoString crashId;
861 0 : if (!mBrowserCrashDumpIds.Get(aPluginId, &crashId)) {
862 0 : return;
863 : }
864 0 : mBrowserCrashDumpIds.Remove(aPluginId);
865 : #ifdef MOZ_CRASHREPORTER
866 0 : if (aRemoveFiles && !crashId.IsEmpty()) {
867 0 : CrashReporter::DeleteMinidumpFilesForID(crashId);
868 : }
869 : #endif
870 : }
871 :
872 : void
873 0 : HangMonitorParent::UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId)
874 : {
875 0 : if (aDumpId.IsEmpty()) {
876 0 : return;
877 : }
878 :
879 0 : MutexAutoLock lock(mBrowserCrashDumpHashLock);
880 0 : mBrowserCrashDumpIds.Put(aPluginId, aDumpId);
881 : }
882 :
883 : /* HangMonitoredProcess implementation */
884 :
885 2 : NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
886 :
887 : NS_IMETHODIMP
888 0 : HangMonitoredProcess::GetHangType(uint32_t* aHangType)
889 : {
890 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
891 0 : switch (mHangData.type()) {
892 : case HangData::TSlowScriptData:
893 0 : *aHangType = SLOW_SCRIPT;
894 0 : break;
895 : case HangData::TPluginHangData:
896 0 : *aHangType = PLUGIN_HANG;
897 0 : break;
898 : default:
899 0 : MOZ_ASSERT_UNREACHABLE("Unexpected HangData type");
900 : return NS_ERROR_UNEXPECTED;
901 : }
902 :
903 0 : return NS_OK;
904 : }
905 :
906 : NS_IMETHODIMP
907 0 : HangMonitoredProcess::GetScriptBrowser(nsIDOMElement** aBrowser)
908 : {
909 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
910 0 : if (mHangData.type() != HangData::TSlowScriptData) {
911 0 : return NS_ERROR_NOT_AVAILABLE;
912 : }
913 :
914 0 : TabId tabId = mHangData.get_SlowScriptData().tabId();
915 0 : if (!mContentParent) {
916 0 : return NS_ERROR_NOT_AVAILABLE;
917 : }
918 :
919 0 : nsTArray<PBrowserParent*> tabs;
920 0 : mContentParent->ManagedPBrowserParent(tabs);
921 0 : for (size_t i = 0; i < tabs.Length(); i++) {
922 0 : TabParent* tp = TabParent::GetFrom(tabs[i]);
923 0 : if (tp->GetTabId() == tabId) {
924 0 : nsCOMPtr<nsIDOMElement> node = do_QueryInterface(tp->GetOwnerElement());
925 0 : node.forget(aBrowser);
926 0 : return NS_OK;
927 : }
928 : }
929 :
930 0 : *aBrowser = nullptr;
931 0 : return NS_OK;
932 : }
933 :
934 : NS_IMETHODIMP
935 0 : HangMonitoredProcess::GetScriptFileName(nsACString& aFileName)
936 : {
937 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
938 0 : if (mHangData.type() != HangData::TSlowScriptData) {
939 0 : return NS_ERROR_NOT_AVAILABLE;
940 : }
941 :
942 0 : aFileName = mHangData.get_SlowScriptData().filename();
943 0 : return NS_OK;
944 : }
945 :
946 : NS_IMETHODIMP
947 0 : HangMonitoredProcess::GetPluginName(nsACString& aPluginName)
948 : {
949 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
950 0 : if (mHangData.type() != HangData::TPluginHangData) {
951 0 : return NS_ERROR_NOT_AVAILABLE;
952 : }
953 :
954 0 : uint32_t id = mHangData.get_PluginHangData().pluginId();
955 :
956 0 : RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
957 0 : nsPluginTag* tag = host->PluginWithId(id);
958 0 : if (!tag) {
959 0 : return NS_ERROR_UNEXPECTED;
960 : }
961 :
962 0 : aPluginName = tag->Name();
963 0 : return NS_OK;
964 : }
965 :
966 : NS_IMETHODIMP
967 0 : HangMonitoredProcess::TerminateScript()
968 : {
969 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
970 0 : if (mHangData.type() != HangData::TSlowScriptData) {
971 0 : return NS_ERROR_UNEXPECTED;
972 : }
973 :
974 0 : if (!mActor) {
975 0 : return NS_ERROR_UNEXPECTED;
976 : }
977 :
978 0 : ProcessHangMonitor::Get()->Dispatch(
979 0 : NewNonOwningRunnableMethod("HangMonitorParent::TerminateScript",
980 : mActor,
981 0 : &HangMonitorParent::TerminateScript));
982 0 : return NS_OK;
983 : }
984 :
985 : NS_IMETHODIMP
986 0 : HangMonitoredProcess::BeginStartingDebugger()
987 : {
988 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
989 0 : if (mHangData.type() != HangData::TSlowScriptData) {
990 0 : return NS_ERROR_UNEXPECTED;
991 : }
992 :
993 0 : if (!mActor) {
994 0 : return NS_ERROR_UNEXPECTED;
995 : }
996 :
997 0 : ProcessHangMonitor::Get()->Dispatch(
998 0 : NewNonOwningRunnableMethod("HangMonitorParent::BeginStartingDebugger",
999 : mActor,
1000 0 : &HangMonitorParent::BeginStartingDebugger));
1001 0 : return NS_OK;
1002 : }
1003 :
1004 : NS_IMETHODIMP
1005 0 : HangMonitoredProcess::EndStartingDebugger()
1006 : {
1007 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1008 0 : if (mHangData.type() != HangData::TSlowScriptData) {
1009 0 : return NS_ERROR_UNEXPECTED;
1010 : }
1011 :
1012 0 : if (!mActor) {
1013 0 : return NS_ERROR_UNEXPECTED;
1014 : }
1015 :
1016 0 : ProcessHangMonitor::Get()->Dispatch(
1017 0 : NewNonOwningRunnableMethod("HangMonitorParent::EndStartingDebugger",
1018 : mActor,
1019 0 : &HangMonitorParent::EndStartingDebugger));
1020 0 : return NS_OK;
1021 : }
1022 :
1023 : NS_IMETHODIMP
1024 0 : HangMonitoredProcess::TerminatePlugin()
1025 : {
1026 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1027 0 : if (mHangData.type() != HangData::TPluginHangData) {
1028 0 : return NS_ERROR_UNEXPECTED;
1029 : }
1030 :
1031 : // Use the multi-process crash report generated earlier.
1032 0 : uint32_t id = mHangData.get_PluginHangData().pluginId();
1033 0 : base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId();
1034 :
1035 0 : RefPtr<HangMonitoredProcess> self{this};
1036 : std::function<void(bool)> callback =
1037 0 : [self, id](bool aResult) {
1038 0 : if (self->mActor) {
1039 0 : self->mActor->CleanupPluginHang(id, false);
1040 : }
1041 0 : };
1042 :
1043 0 : plugins::TerminatePlugin(id,
1044 : contentPid,
1045 0 : NS_LITERAL_CSTRING("HangMonitor"),
1046 : mDumpId,
1047 0 : Move(callback));
1048 0 : return NS_OK;
1049 : }
1050 :
1051 : NS_IMETHODIMP
1052 0 : HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult)
1053 : {
1054 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1055 :
1056 0 : if (!mActor) {
1057 0 : *aResult = false;
1058 0 : return NS_OK;
1059 : }
1060 :
1061 0 : TabParent* tp = TabParent::GetFrom(aFrameLoader);
1062 0 : if (!tp) {
1063 0 : *aResult = false;
1064 0 : return NS_OK;
1065 : }
1066 :
1067 0 : *aResult = mContentParent == tp->Manager();
1068 0 : return NS_OK;
1069 : }
1070 :
1071 : NS_IMETHODIMP
1072 0 : HangMonitoredProcess::UserCanceled()
1073 : {
1074 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1075 0 : if (mHangData.type() != HangData::TPluginHangData) {
1076 0 : return NS_OK;
1077 : }
1078 :
1079 0 : if (mActor) {
1080 0 : uint32_t id = mHangData.get_PluginHangData().pluginId();
1081 0 : mActor->CleanupPluginHang(id, true);
1082 : }
1083 0 : return NS_OK;
1084 : }
1085 :
1086 : static bool
1087 2 : InterruptCallback(JSContext* cx)
1088 : {
1089 2 : if (HangMonitorChild* child = HangMonitorChild::Get()) {
1090 2 : child->InterruptCallback();
1091 : }
1092 :
1093 2 : return true;
1094 : }
1095 :
1096 : ProcessHangMonitor* ProcessHangMonitor::sInstance;
1097 :
1098 3 : ProcessHangMonitor::ProcessHangMonitor()
1099 3 : : mCPOWTimeout(false)
1100 : {
1101 3 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1102 :
1103 3 : if (XRE_IsContentProcess()) {
1104 4 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1105 2 : obs->AddObserver(this, "xpcom-shutdown", false);
1106 : }
1107 :
1108 3 : if (NS_FAILED(NS_NewNamedThread("ProcessHangMon", getter_AddRefs(mThread)))) {
1109 0 : mThread = nullptr;
1110 : }
1111 3 : }
1112 :
1113 0 : ProcessHangMonitor::~ProcessHangMonitor()
1114 : {
1115 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1116 :
1117 0 : MOZ_ASSERT(sInstance == this);
1118 0 : sInstance = nullptr;
1119 :
1120 0 : mThread->Shutdown();
1121 0 : mThread = nullptr;
1122 0 : }
1123 :
1124 : ProcessHangMonitor*
1125 4 : ProcessHangMonitor::GetOrCreate()
1126 : {
1127 4 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1128 4 : if (!sInstance) {
1129 3 : sInstance = new ProcessHangMonitor();
1130 : }
1131 4 : return sInstance;
1132 : }
1133 :
1134 6 : NS_IMPL_ISUPPORTS(ProcessHangMonitor, nsIObserver)
1135 :
1136 : NS_IMETHODIMP
1137 0 : ProcessHangMonitor::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
1138 : {
1139 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1140 0 : if (!strcmp(aTopic, "xpcom-shutdown")) {
1141 0 : if (HangMonitorChild* child = HangMonitorChild::Get()) {
1142 0 : child->Shutdown();
1143 0 : delete child;
1144 : }
1145 :
1146 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1147 0 : if (obs) {
1148 0 : obs->RemoveObserver(this, "xpcom-shutdown");
1149 : }
1150 : }
1151 0 : return NS_OK;
1152 : }
1153 :
1154 : ProcessHangMonitor::SlowScriptAction
1155 0 : ProcessHangMonitor::NotifySlowScript(nsITabChild* aTabChild,
1156 : const char* aFileName)
1157 : {
1158 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1159 0 : return HangMonitorChild::Get()->NotifySlowScript(aTabChild, aFileName);
1160 : }
1161 :
1162 : bool
1163 0 : ProcessHangMonitor::IsDebuggerStartupComplete()
1164 : {
1165 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1166 0 : return HangMonitorChild::Get()->IsDebuggerStartupComplete();
1167 : }
1168 :
1169 : bool
1170 0 : ProcessHangMonitor::ShouldTimeOutCPOWs()
1171 : {
1172 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1173 :
1174 0 : if (mCPOWTimeout) {
1175 0 : mCPOWTimeout = false;
1176 0 : return true;
1177 : }
1178 0 : return false;
1179 : }
1180 :
1181 : void
1182 0 : ProcessHangMonitor::InitiateCPOWTimeout()
1183 : {
1184 0 : MOZ_RELEASE_ASSERT(IsOnThread());
1185 0 : mCPOWTimeout = true;
1186 0 : }
1187 :
1188 : void
1189 0 : ProcessHangMonitor::NotifyPluginHang(uint32_t aPluginId)
1190 : {
1191 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1192 0 : return HangMonitorChild::Get()->NotifyPluginHang(aPluginId);
1193 : }
1194 :
1195 : static PProcessHangMonitorParent*
1196 2 : CreateHangMonitorParent(ContentParent* aContentParent,
1197 : Endpoint<PProcessHangMonitorParent>&& aEndpoint)
1198 : {
1199 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1200 :
1201 2 : ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
1202 2 : auto* parent = new HangMonitorParent(monitor);
1203 :
1204 2 : auto* process = new HangMonitoredProcess(parent, aContentParent);
1205 2 : parent->SetProcess(process);
1206 :
1207 4 : monitor->Dispatch(
1208 4 : NewNonOwningRunnableMethod<Endpoint<PProcessHangMonitorParent>&&>(
1209 : "HangMonitorParent::Bind",
1210 : parent,
1211 : &HangMonitorParent::Bind,
1212 4 : Move(aEndpoint)));
1213 :
1214 2 : return parent;
1215 : }
1216 :
1217 : void
1218 2 : mozilla::CreateHangMonitorChild(Endpoint<PProcessHangMonitorChild>&& aEndpoint)
1219 : {
1220 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1221 :
1222 2 : JSContext* cx = danger::GetJSContext();
1223 2 : JS_AddInterruptCallback(cx, InterruptCallback);
1224 :
1225 2 : ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
1226 2 : auto* child = new HangMonitorChild(monitor);
1227 :
1228 4 : monitor->Dispatch(
1229 4 : NewNonOwningRunnableMethod<Endpoint<PProcessHangMonitorChild>&&>(
1230 : "HangMonitorChild::Bind",
1231 : child,
1232 : &HangMonitorChild::Bind,
1233 4 : Move(aEndpoint)));
1234 2 : }
1235 :
1236 : void
1237 6 : ProcessHangMonitor::Dispatch(already_AddRefed<nsIRunnable> aRunnable)
1238 : {
1239 6 : mThread->Dispatch(Move(aRunnable), nsIEventTarget::NS_DISPATCH_NORMAL);
1240 6 : }
1241 :
1242 : bool
1243 8 : ProcessHangMonitor::IsOnThread()
1244 : {
1245 : bool on;
1246 8 : return NS_SUCCEEDED(mThread->IsOnCurrentThread(&on)) && on;
1247 : }
1248 :
1249 : /* static */ PProcessHangMonitorParent*
1250 2 : ProcessHangMonitor::AddProcess(ContentParent* aContentParent)
1251 : {
1252 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1253 :
1254 2 : if (!mozilla::Preferences::GetBool("dom.ipc.processHangMonitor", false)) {
1255 0 : return nullptr;
1256 : }
1257 :
1258 4 : Endpoint<PProcessHangMonitorParent> parent;
1259 4 : Endpoint<PProcessHangMonitorChild> child;
1260 : nsresult rv;
1261 4 : rv = PProcessHangMonitor::CreateEndpoints(base::GetCurrentProcId(),
1262 2 : aContentParent->OtherPid(),
1263 2 : &parent, &child);
1264 2 : if (NS_FAILED(rv)) {
1265 0 : MOZ_ASSERT(false, "PProcessHangMonitor::CreateEndpoints failed");
1266 : return nullptr;
1267 : }
1268 :
1269 2 : if (!aContentParent->SendInitProcessHangMonitor(Move(child))) {
1270 0 : MOZ_ASSERT(false);
1271 : return nullptr;
1272 : }
1273 :
1274 2 : return CreateHangMonitorParent(aContentParent, Move(parent));
1275 : }
1276 :
1277 : /* static */ void
1278 0 : ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent* aParent)
1279 : {
1280 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1281 0 : auto parent = static_cast<HangMonitorParent*>(aParent);
1282 0 : parent->Shutdown();
1283 0 : delete parent;
1284 0 : }
1285 :
1286 : /* static */ void
1287 1709 : ProcessHangMonitor::ClearHang()
1288 : {
1289 1709 : MOZ_ASSERT(NS_IsMainThread());
1290 1709 : if (HangMonitorChild* child = HangMonitorChild::Get()) {
1291 369 : child->ClearHang();
1292 : }
1293 1709 : }
1294 :
1295 : /* static */ void
1296 2 : ProcessHangMonitor::ForcePaint(PProcessHangMonitorParent* aParent,
1297 : dom::TabParent* aTabParent,
1298 : uint64_t aLayerObserverEpoch)
1299 : {
1300 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1301 2 : auto parent = static_cast<HangMonitorParent*>(aParent);
1302 2 : parent->ForcePaint(aTabParent, aLayerObserverEpoch);
1303 2 : }
1304 :
1305 : /* static */ void
1306 2 : ProcessHangMonitor::ClearForcePaint()
1307 : {
1308 2 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
1309 2 : MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
1310 :
1311 2 : if (HangMonitorChild* child = HangMonitorChild::Get()) {
1312 2 : child->ClearForcePaint();
1313 : }
1314 2 : }
|