Coverage Report

Created: 2018-09-25 14:53

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