ProcessHangMonitor.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/ProcessHangMonitor.h"
  6. #include "mozilla/ProcessHangMonitorIPC.h"
  7. #include "jsapi.h"
  8. #include "js/GCAPI.h"
  9. #include "mozilla/Atomics.h"
  10. #include "mozilla/BackgroundHangMonitor.h"
  11. #include "mozilla/dom/ContentParent.h"
  12. #include "mozilla/dom/Element.h"
  13. #include "mozilla/dom/ScriptSettings.h"
  14. #include "mozilla/dom/TabChild.h"
  15. #include "mozilla/dom/TabParent.h"
  16. #include "mozilla/Monitor.h"
  17. #include "mozilla/plugins/PluginBridge.h"
  18. #include "mozilla/Preferences.h"
  19. #include "mozilla/Unused.h"
  20. #include "nsIFrameLoader.h"
  21. #include "nsIHangReport.h"
  22. #include "nsITabParent.h"
  23. #include "nsPluginHost.h"
  24. #include "nsThreadUtils.h"
  25. #include "base/task.h"
  26. #include "base/thread.h"
  27. #ifdef XP_WIN
  28. // For IsDebuggerPresent()
  29. #include <windows.h>
  30. #endif
  31. using namespace mozilla;
  32. using namespace mozilla::dom;
  33. /*
  34. * Basic architecture:
  35. *
  36. * Each process has its own ProcessHangMonitor singleton. This singleton exists
  37. * as long as there is at least one content process in the system. Each content
  38. * process has a HangMonitorChild and the chrome process has one
  39. * HangMonitorParent per process. Each process (including the chrome process)
  40. * runs a hang monitoring thread. The PHangMonitor actors are bound to this
  41. * thread so that they never block on the main thread.
  42. *
  43. * When the content process detects a hang, it posts a task to its hang thread,
  44. * which sends an IPC message to the hang thread in the parent. The parent
  45. * cancels any ongoing CPOW requests and then posts a runnable to the main
  46. * thread that notifies Firefox frontend code of the hang. The frontend code is
  47. * passed an nsIHangReport, which can be used to terminate the hang.
  48. *
  49. * If the user chooses to terminate a script, a task is posted to the chrome
  50. * process's hang monitoring thread, which sends an IPC message to the hang
  51. * thread in the content process. That thread sets a flag to indicate that JS
  52. * execution should be terminated the next time it hits the interrupt
  53. * callback. A similar scheme is used for debugging slow scripts. If a content
  54. * process or plug-in needs to be terminated, the chrome process does so
  55. * directly, without messaging the content process.
  56. */
  57. namespace {
  58. /* Child process objects */
  59. class HangMonitorChild
  60. : public PProcessHangMonitorChild
  61. {
  62. public:
  63. explicit HangMonitorChild(ProcessHangMonitor* aMonitor);
  64. virtual ~HangMonitorChild();
  65. void Open(Transport* aTransport, ProcessId aOtherPid,
  66. MessageLoop* aIOLoop);
  67. typedef ProcessHangMonitor::SlowScriptAction SlowScriptAction;
  68. SlowScriptAction NotifySlowScript(nsITabChild* aTabChild,
  69. const char* aFileName,
  70. unsigned aLineNo);
  71. void NotifySlowScriptAsync(TabId aTabId,
  72. const nsCString& aFileName,
  73. unsigned aLineNo);
  74. bool IsDebuggerStartupComplete();
  75. void NotifyPluginHang(uint32_t aPluginId);
  76. void NotifyPluginHangAsync(uint32_t aPluginId);
  77. void ClearHang();
  78. void ClearHangAsync();
  79. void ClearForcePaint();
  80. virtual bool RecvTerminateScript() override;
  81. virtual bool RecvBeginStartingDebugger() override;
  82. virtual bool RecvEndStartingDebugger() override;
  83. virtual bool RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch) override;
  84. virtual void ActorDestroy(ActorDestroyReason aWhy) override;
  85. void InterruptCallback();
  86. void Shutdown();
  87. static HangMonitorChild* Get() { return sInstance; }
  88. MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
  89. private:
  90. void ShutdownOnThread();
  91. static Atomic<HangMonitorChild*> sInstance;
  92. UniquePtr<BackgroundHangMonitor> mForcePaintMonitor;
  93. const RefPtr<ProcessHangMonitor> mHangMonitor;
  94. Monitor mMonitor;
  95. // Main thread-only.
  96. bool mSentReport;
  97. // These fields must be accessed with mMonitor held.
  98. bool mTerminateScript;
  99. bool mStartDebugger;
  100. bool mFinishedStartingDebugger;
  101. bool mForcePaint;
  102. TabId mForcePaintTab;
  103. MOZ_INIT_OUTSIDE_CTOR uint64_t mForcePaintEpoch;
  104. JSContext* mContext;
  105. bool mShutdownDone;
  106. // This field is only accessed on the hang thread.
  107. bool mIPCOpen;
  108. };
  109. Atomic<HangMonitorChild*> HangMonitorChild::sInstance;
  110. /* Parent process objects */
  111. class HangMonitorParent;
  112. class HangMonitoredProcess final
  113. : public nsIHangReport
  114. {
  115. public:
  116. NS_DECL_THREADSAFE_ISUPPORTS
  117. HangMonitoredProcess(HangMonitorParent* aActor,
  118. ContentParent* aContentParent)
  119. : mActor(aActor), mContentParent(aContentParent) {}
  120. NS_IMETHOD GetHangType(uint32_t* aHangType) override;
  121. NS_IMETHOD GetScriptBrowser(nsIDOMElement** aBrowser) override;
  122. NS_IMETHOD GetScriptFileName(nsACString& aFileName) override;
  123. NS_IMETHOD GetScriptLineNo(uint32_t* aLineNo) override;
  124. NS_IMETHOD GetPluginName(nsACString& aPluginName) override;
  125. NS_IMETHOD TerminateScript() override;
  126. NS_IMETHOD BeginStartingDebugger() override;
  127. NS_IMETHOD EndStartingDebugger() override;
  128. NS_IMETHOD TerminatePlugin() override;
  129. NS_IMETHOD UserCanceled() override;
  130. NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
  131. // Called when a content process shuts down.
  132. void Clear() {
  133. mContentParent = nullptr;
  134. mActor = nullptr;
  135. }
  136. /**
  137. * Sets the information associated with this hang: this includes the ID of
  138. * the plugin which caused the hang as well as the content PID. The ID of
  139. * a minidump taken during the hang can also be provided.
  140. *
  141. * @param aHangData The hang information
  142. * @param aDumpId The ID of a minidump taken when the hang occurred
  143. */
  144. void SetHangData(const HangData& aHangData, const nsAString& aDumpId) {
  145. mHangData = aHangData;
  146. mDumpId = aDumpId;
  147. }
  148. void ClearHang() {
  149. mHangData = HangData();
  150. mDumpId.Truncate();
  151. }
  152. private:
  153. ~HangMonitoredProcess() {}
  154. // Everything here is main thread-only.
  155. HangMonitorParent* mActor;
  156. ContentParent* mContentParent;
  157. HangData mHangData;
  158. nsAutoString mDumpId;
  159. };
  160. class HangMonitorParent
  161. : public PProcessHangMonitorParent
  162. {
  163. public:
  164. explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
  165. virtual ~HangMonitorParent();
  166. void Open(Transport* aTransport, ProcessId aPid, MessageLoop* aIOLoop);
  167. virtual bool RecvHangEvidence(const HangData& aHangData) override;
  168. virtual bool RecvClearHang() override;
  169. virtual void ActorDestroy(ActorDestroyReason aWhy) override;
  170. void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
  171. void Shutdown();
  172. void ForcePaint(dom::TabParent* aTabParent, uint64_t aLayerObserverEpoch);
  173. void TerminateScript();
  174. void BeginStartingDebugger();
  175. void EndStartingDebugger();
  176. void CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles);
  177. /**
  178. * Update the dump for the specified plugin. This method is thread-safe and
  179. * is used to replace a browser minidump with a full minidump. If aDumpId is
  180. * empty this is a no-op.
  181. */
  182. void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId);
  183. MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
  184. private:
  185. bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
  186. void ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch);
  187. void ShutdownOnThread();
  188. const RefPtr<ProcessHangMonitor> mHangMonitor;
  189. // This field is read-only after construction.
  190. bool mReportHangs;
  191. // This field is only accessed on the hang thread.
  192. bool mIPCOpen;
  193. Monitor mMonitor;
  194. // Must be accessed with mMonitor held.
  195. RefPtr<HangMonitoredProcess> mProcess;
  196. bool mShutdownDone;
  197. // Map from plugin ID to crash dump ID. Protected by mBrowserCrashDumpHashLock.
  198. nsDataHashtable<nsUint32HashKey, nsString> mBrowserCrashDumpIds;
  199. Mutex mBrowserCrashDumpHashLock;
  200. };
  201. } // namespace
  202. /* HangMonitorChild implementation */
  203. HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
  204. : mHangMonitor(aMonitor),
  205. mMonitor("HangMonitorChild lock"),
  206. mSentReport(false),
  207. mTerminateScript(false),
  208. mStartDebugger(false),
  209. mFinishedStartingDebugger(false),
  210. mForcePaint(false),
  211. mShutdownDone(false),
  212. mIPCOpen(true)
  213. {
  214. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  215. mContext = danger::GetJSContext();
  216. mForcePaintMonitor =
  217. MakeUnique<mozilla::BackgroundHangMonitor>("Gecko_Child_ForcePaint",
  218. 128, /* ms timeout for microhangs */
  219. 8192 /* ms timeout for permahangs */,
  220. BackgroundHangMonitor::THREAD_PRIVATE);
  221. }
  222. HangMonitorChild::~HangMonitorChild()
  223. {
  224. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  225. MOZ_ASSERT(sInstance == this);
  226. mForcePaintMonitor = nullptr;
  227. sInstance = nullptr;
  228. }
  229. void
  230. HangMonitorChild::InterruptCallback()
  231. {
  232. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  233. bool forcePaint;
  234. TabId forcePaintTab;
  235. uint64_t forcePaintEpoch;
  236. {
  237. MonitorAutoLock lock(mMonitor);
  238. forcePaint = mForcePaint;
  239. forcePaintTab = mForcePaintTab;
  240. forcePaintEpoch = mForcePaintEpoch;
  241. mForcePaint = false;
  242. }
  243. if (forcePaint) {
  244. RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
  245. if (tabChild) {
  246. tabChild->ForcePaint(forcePaintEpoch);
  247. }
  248. }
  249. }
  250. void
  251. HangMonitorChild::Shutdown()
  252. {
  253. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  254. MonitorAutoLock lock(mMonitor);
  255. while (!mShutdownDone) {
  256. mMonitor.Wait();
  257. }
  258. }
  259. void
  260. HangMonitorChild::ShutdownOnThread()
  261. {
  262. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  263. MonitorAutoLock lock(mMonitor);
  264. mShutdownDone = true;
  265. mMonitor.Notify();
  266. }
  267. void
  268. HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy)
  269. {
  270. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  271. mIPCOpen = false;
  272. // We use a task here to ensure that IPDL is finished with this
  273. // HangMonitorChild before it gets deleted on the main thread.
  274. MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ShutdownOnThread));
  275. }
  276. bool
  277. HangMonitorChild::RecvTerminateScript()
  278. {
  279. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  280. MonitorAutoLock lock(mMonitor);
  281. mTerminateScript = true;
  282. return true;
  283. }
  284. bool
  285. HangMonitorChild::RecvBeginStartingDebugger()
  286. {
  287. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  288. MonitorAutoLock lock(mMonitor);
  289. mStartDebugger = true;
  290. return true;
  291. }
  292. bool
  293. HangMonitorChild::RecvEndStartingDebugger()
  294. {
  295. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  296. MonitorAutoLock lock(mMonitor);
  297. mFinishedStartingDebugger = true;
  298. return true;
  299. }
  300. bool
  301. HangMonitorChild::RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch)
  302. {
  303. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  304. mForcePaintMonitor->NotifyActivity();
  305. {
  306. MonitorAutoLock lock(mMonitor);
  307. mForcePaint = true;
  308. mForcePaintTab = aTabId;
  309. mForcePaintEpoch = aLayerObserverEpoch;
  310. }
  311. JS_RequestInterruptCallback(mContext);
  312. return true;
  313. }
  314. void
  315. HangMonitorChild::ClearForcePaint()
  316. {
  317. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  318. MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
  319. mForcePaintMonitor->NotifyWait();
  320. }
  321. void
  322. HangMonitorChild::Open(Transport* aTransport, ProcessId aPid,
  323. MessageLoop* aIOLoop)
  324. {
  325. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  326. MOZ_ASSERT(!sInstance);
  327. sInstance = this;
  328. DebugOnly<bool> ok = PProcessHangMonitorChild::Open(aTransport, aPid, aIOLoop);
  329. MOZ_ASSERT(ok);
  330. }
  331. void
  332. HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
  333. const nsCString& aFileName,
  334. unsigned aLineNo)
  335. {
  336. if (mIPCOpen) {
  337. Unused << SendHangEvidence(SlowScriptData(aTabId, aFileName, aLineNo));
  338. }
  339. }
  340. HangMonitorChild::SlowScriptAction
  341. HangMonitorChild::NotifySlowScript(nsITabChild* aTabChild,
  342. const char* aFileName,
  343. unsigned aLineNo)
  344. {
  345. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  346. mSentReport = true;
  347. {
  348. MonitorAutoLock lock(mMonitor);
  349. if (mTerminateScript) {
  350. mTerminateScript = false;
  351. return SlowScriptAction::Terminate;
  352. }
  353. if (mStartDebugger) {
  354. mStartDebugger = false;
  355. return SlowScriptAction::StartDebugger;
  356. }
  357. }
  358. TabId id;
  359. if (aTabChild) {
  360. RefPtr<TabChild> tabChild = static_cast<TabChild*>(aTabChild);
  361. id = tabChild->GetTabId();
  362. }
  363. nsAutoCString filename(aFileName);
  364. MonitorLoop()->PostTask(NewNonOwningRunnableMethod
  365. <TabId, nsCString, unsigned>(this,
  366. &HangMonitorChild::NotifySlowScriptAsync,
  367. id, filename, aLineNo));
  368. return SlowScriptAction::Continue;
  369. }
  370. bool
  371. HangMonitorChild::IsDebuggerStartupComplete()
  372. {
  373. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  374. MonitorAutoLock lock(mMonitor);
  375. if (mFinishedStartingDebugger) {
  376. mFinishedStartingDebugger = false;
  377. return true;
  378. }
  379. return false;
  380. }
  381. void
  382. HangMonitorChild::NotifyPluginHang(uint32_t aPluginId)
  383. {
  384. // main thread in the child
  385. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  386. mSentReport = true;
  387. // bounce to background thread
  388. MonitorLoop()->PostTask(NewNonOwningRunnableMethod<uint32_t>(this,
  389. &HangMonitorChild::NotifyPluginHangAsync,
  390. aPluginId));
  391. }
  392. void
  393. HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId)
  394. {
  395. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  396. // bounce back to parent on background thread
  397. if (mIPCOpen) {
  398. Unused << SendHangEvidence(PluginHangData(aPluginId,
  399. base::GetCurrentProcId()));
  400. }
  401. }
  402. void
  403. HangMonitorChild::ClearHang()
  404. {
  405. MOZ_ASSERT(NS_IsMainThread());
  406. if (mSentReport) {
  407. // bounce to background thread
  408. MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ClearHangAsync));
  409. MonitorAutoLock lock(mMonitor);
  410. mSentReport = false;
  411. mTerminateScript = false;
  412. mStartDebugger = false;
  413. mFinishedStartingDebugger = false;
  414. }
  415. }
  416. void
  417. HangMonitorChild::ClearHangAsync()
  418. {
  419. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  420. // bounce back to parent on background thread
  421. if (mIPCOpen) {
  422. Unused << SendClearHang();
  423. }
  424. }
  425. /* HangMonitorParent implementation */
  426. HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor)
  427. : mHangMonitor(aMonitor),
  428. mIPCOpen(true),
  429. mMonitor("HangMonitorParent lock"),
  430. mShutdownDone(false),
  431. mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock")
  432. {
  433. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  434. mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false);
  435. }
  436. HangMonitorParent::~HangMonitorParent()
  437. {
  438. }
  439. void
  440. HangMonitorParent::Shutdown()
  441. {
  442. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  443. MonitorAutoLock lock(mMonitor);
  444. if (mProcess) {
  445. mProcess->Clear();
  446. mProcess = nullptr;
  447. }
  448. MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this,
  449. &HangMonitorParent::ShutdownOnThread));
  450. while (!mShutdownDone) {
  451. mMonitor.Wait();
  452. }
  453. }
  454. void
  455. HangMonitorParent::ShutdownOnThread()
  456. {
  457. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  458. // mIPCOpen is only written from this thread, so need need to take the lock
  459. // here. We'd be shooting ourselves in the foot, because ActorDestroy takes
  460. // it.
  461. if (mIPCOpen) {
  462. Close();
  463. }
  464. MonitorAutoLock lock(mMonitor);
  465. mShutdownDone = true;
  466. mMonitor.Notify();
  467. }
  468. void
  469. HangMonitorParent::ForcePaint(dom::TabParent* aTab, uint64_t aLayerObserverEpoch)
  470. {
  471. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  472. TabId id = aTab->GetTabId();
  473. MonitorLoop()->PostTask(NewNonOwningRunnableMethod<TabId, uint64_t>(
  474. this, &HangMonitorParent::ForcePaintOnThread, id, aLayerObserverEpoch));
  475. }
  476. void
  477. HangMonitorParent::ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch)
  478. {
  479. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  480. if (mIPCOpen) {
  481. Unused << SendForcePaint(aTabId, aLayerObserverEpoch);
  482. }
  483. }
  484. void
  485. HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy)
  486. {
  487. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  488. mIPCOpen = false;
  489. }
  490. void
  491. HangMonitorParent::Open(Transport* aTransport, ProcessId aPid,
  492. MessageLoop* aIOLoop)
  493. {
  494. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  495. DebugOnly<bool> ok = PProcessHangMonitorParent::Open(aTransport, aPid, aIOLoop);
  496. MOZ_ASSERT(ok);
  497. }
  498. class HangObserverNotifier final : public Runnable
  499. {
  500. public:
  501. HangObserverNotifier(HangMonitoredProcess* aProcess,
  502. HangMonitorParent *aParent,
  503. const HangData& aHangData,
  504. const nsString& aBrowserDumpId,
  505. bool aTakeMinidump)
  506. : mProcess(aProcess),
  507. mParent(aParent),
  508. mHangData(aHangData),
  509. mBrowserDumpId(aBrowserDumpId),
  510. mTakeMinidump(aTakeMinidump)
  511. {}
  512. NS_IMETHOD
  513. Run() override
  514. {
  515. // chrome process, main thread
  516. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  517. nsString dumpId;
  518. if ((mHangData.type() == HangData::TPluginHangData) && mTakeMinidump) {
  519. // We've been handed a partial minidump; complete it with plugin and
  520. // content process dumps.
  521. const PluginHangData& phd = mHangData.get_PluginHangData();
  522. plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
  523. mBrowserDumpId, dumpId);
  524. mParent->UpdateMinidump(phd.pluginId(), dumpId);
  525. } else {
  526. // We already have a full minidump; go ahead and use it.
  527. dumpId = mBrowserDumpId;
  528. }
  529. mProcess->SetHangData(mHangData, dumpId);
  530. nsCOMPtr<nsIObserverService> observerService =
  531. mozilla::services::GetObserverService();
  532. observerService->NotifyObservers(mProcess, "process-hang-report", nullptr);
  533. return NS_OK;
  534. }
  535. private:
  536. RefPtr<HangMonitoredProcess> mProcess;
  537. HangMonitorParent* mParent;
  538. HangData mHangData;
  539. nsAutoString mBrowserDumpId;
  540. bool mTakeMinidump;
  541. };
  542. // Take a minidump of the browser process if one wasn't already taken for the
  543. // plugin that caused the hang. Return false if a dump was already available or
  544. // true if new one has been taken.
  545. bool
  546. HangMonitorParent::TakeBrowserMinidump(const PluginHangData& aPhd,
  547. nsString& aCrashId)
  548. {
  549. return false;
  550. }
  551. bool
  552. HangMonitorParent::RecvHangEvidence(const HangData& aHangData)
  553. {
  554. // chrome process, background thread
  555. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  556. if (!mReportHangs) {
  557. return true;
  558. }
  559. #ifdef XP_WIN
  560. // Don't report hangs if we're debugging the process. You can comment this
  561. // line out for testing purposes.
  562. if (IsDebuggerPresent()) {
  563. return true;
  564. }
  565. #endif
  566. // Before we wake up the browser main thread we want to take a
  567. // browser minidump.
  568. nsAutoString crashId;
  569. bool takeMinidump = false;
  570. if (aHangData.type() == HangData::TPluginHangData) {
  571. takeMinidump = TakeBrowserMinidump(aHangData.get_PluginHangData(), crashId);
  572. }
  573. mHangMonitor->InitiateCPOWTimeout();
  574. MonitorAutoLock lock(mMonitor);
  575. nsCOMPtr<nsIRunnable> notifier =
  576. new HangObserverNotifier(mProcess, this, aHangData, crashId, takeMinidump);
  577. NS_DispatchToMainThread(notifier);
  578. return true;
  579. }
  580. class ClearHangNotifier final : public Runnable
  581. {
  582. public:
  583. explicit ClearHangNotifier(HangMonitoredProcess* aProcess)
  584. : mProcess(aProcess)
  585. {}
  586. NS_IMETHOD
  587. Run() override
  588. {
  589. // chrome process, main thread
  590. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  591. mProcess->ClearHang();
  592. nsCOMPtr<nsIObserverService> observerService =
  593. mozilla::services::GetObserverService();
  594. observerService->NotifyObservers(mProcess, "clear-hang-report", nullptr);
  595. return NS_OK;
  596. }
  597. private:
  598. RefPtr<HangMonitoredProcess> mProcess;
  599. };
  600. bool
  601. HangMonitorParent::RecvClearHang()
  602. {
  603. // chrome process, background thread
  604. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  605. if (!mReportHangs) {
  606. return true;
  607. }
  608. mHangMonitor->InitiateCPOWTimeout();
  609. MonitorAutoLock lock(mMonitor);
  610. nsCOMPtr<nsIRunnable> notifier =
  611. new ClearHangNotifier(mProcess);
  612. NS_DispatchToMainThread(notifier);
  613. return true;
  614. }
  615. void
  616. HangMonitorParent::TerminateScript()
  617. {
  618. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  619. if (mIPCOpen) {
  620. Unused << SendTerminateScript();
  621. }
  622. }
  623. void
  624. HangMonitorParent::BeginStartingDebugger()
  625. {
  626. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  627. if (mIPCOpen) {
  628. Unused << SendBeginStartingDebugger();
  629. }
  630. }
  631. void
  632. HangMonitorParent::EndStartingDebugger()
  633. {
  634. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  635. if (mIPCOpen) {
  636. Unused << SendEndStartingDebugger();
  637. }
  638. }
  639. void
  640. HangMonitorParent::CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles)
  641. {
  642. MutexAutoLock lock(mBrowserCrashDumpHashLock);
  643. nsAutoString crashId;
  644. if (!mBrowserCrashDumpIds.Get(aPluginId, &crashId)) {
  645. return;
  646. }
  647. mBrowserCrashDumpIds.Remove(aPluginId);
  648. }
  649. void
  650. HangMonitorParent::UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId)
  651. {
  652. if (aDumpId.IsEmpty()) {
  653. return;
  654. }
  655. MutexAutoLock lock(mBrowserCrashDumpHashLock);
  656. mBrowserCrashDumpIds.Put(aPluginId, aDumpId);
  657. }
  658. /* HangMonitoredProcess implementation */
  659. NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
  660. NS_IMETHODIMP
  661. HangMonitoredProcess::GetHangType(uint32_t* aHangType)
  662. {
  663. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  664. switch (mHangData.type()) {
  665. case HangData::TSlowScriptData:
  666. *aHangType = SLOW_SCRIPT;
  667. break;
  668. case HangData::TPluginHangData:
  669. *aHangType = PLUGIN_HANG;
  670. break;
  671. default:
  672. MOZ_ASSERT_UNREACHABLE("Unexpected HangData type");
  673. return NS_ERROR_UNEXPECTED;
  674. }
  675. return NS_OK;
  676. }
  677. NS_IMETHODIMP
  678. HangMonitoredProcess::GetScriptBrowser(nsIDOMElement** aBrowser)
  679. {
  680. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  681. if (mHangData.type() != HangData::TSlowScriptData) {
  682. return NS_ERROR_NOT_AVAILABLE;
  683. }
  684. TabId tabId = mHangData.get_SlowScriptData().tabId();
  685. if (!mContentParent) {
  686. return NS_ERROR_NOT_AVAILABLE;
  687. }
  688. nsTArray<PBrowserParent*> tabs;
  689. mContentParent->ManagedPBrowserParent(tabs);
  690. for (size_t i = 0; i < tabs.Length(); i++) {
  691. TabParent* tp = TabParent::GetFrom(tabs[i]);
  692. if (tp->GetTabId() == tabId) {
  693. nsCOMPtr<nsIDOMElement> node = do_QueryInterface(tp->GetOwnerElement());
  694. node.forget(aBrowser);
  695. return NS_OK;
  696. }
  697. }
  698. *aBrowser = nullptr;
  699. return NS_OK;
  700. }
  701. NS_IMETHODIMP
  702. HangMonitoredProcess::GetScriptFileName(nsACString& aFileName)
  703. {
  704. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  705. if (mHangData.type() != HangData::TSlowScriptData) {
  706. return NS_ERROR_NOT_AVAILABLE;
  707. }
  708. aFileName = mHangData.get_SlowScriptData().filename();
  709. return NS_OK;
  710. }
  711. NS_IMETHODIMP
  712. HangMonitoredProcess::GetScriptLineNo(uint32_t* aLineNo)
  713. {
  714. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  715. if (mHangData.type() != HangData::TSlowScriptData) {
  716. return NS_ERROR_NOT_AVAILABLE;
  717. }
  718. *aLineNo = mHangData.get_SlowScriptData().lineno();
  719. return NS_OK;
  720. }
  721. NS_IMETHODIMP
  722. HangMonitoredProcess::GetPluginName(nsACString& aPluginName)
  723. {
  724. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  725. if (mHangData.type() != HangData::TPluginHangData) {
  726. return NS_ERROR_NOT_AVAILABLE;
  727. }
  728. uint32_t id = mHangData.get_PluginHangData().pluginId();
  729. RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
  730. nsPluginTag* tag = host->PluginWithId(id);
  731. if (!tag) {
  732. return NS_ERROR_UNEXPECTED;
  733. }
  734. aPluginName = tag->Name();
  735. return NS_OK;
  736. }
  737. NS_IMETHODIMP
  738. HangMonitoredProcess::TerminateScript()
  739. {
  740. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  741. if (mHangData.type() != HangData::TSlowScriptData) {
  742. return NS_ERROR_UNEXPECTED;
  743. }
  744. if (!mActor) {
  745. return NS_ERROR_UNEXPECTED;
  746. }
  747. ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor,
  748. &HangMonitorParent::TerminateScript));
  749. return NS_OK;
  750. }
  751. NS_IMETHODIMP
  752. HangMonitoredProcess::BeginStartingDebugger()
  753. {
  754. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  755. if (mHangData.type() != HangData::TSlowScriptData) {
  756. return NS_ERROR_UNEXPECTED;
  757. }
  758. if (!mActor) {
  759. return NS_ERROR_UNEXPECTED;
  760. }
  761. ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor,
  762. &HangMonitorParent::BeginStartingDebugger));
  763. return NS_OK;
  764. }
  765. NS_IMETHODIMP
  766. HangMonitoredProcess::EndStartingDebugger()
  767. {
  768. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  769. if (mHangData.type() != HangData::TSlowScriptData) {
  770. return NS_ERROR_UNEXPECTED;
  771. }
  772. if (!mActor) {
  773. return NS_ERROR_UNEXPECTED;
  774. }
  775. ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor,
  776. &HangMonitorParent::EndStartingDebugger));
  777. return NS_OK;
  778. }
  779. NS_IMETHODIMP
  780. HangMonitoredProcess::TerminatePlugin()
  781. {
  782. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  783. if (mHangData.type() != HangData::TPluginHangData) {
  784. return NS_ERROR_UNEXPECTED;
  785. }
  786. // Use the multi-process crash report generated earlier.
  787. uint32_t id = mHangData.get_PluginHangData().pluginId();
  788. base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId();
  789. plugins::TerminatePlugin(id, contentPid, NS_LITERAL_CSTRING("HangMonitor"),
  790. mDumpId);
  791. if (mActor) {
  792. mActor->CleanupPluginHang(id, false);
  793. }
  794. return NS_OK;
  795. }
  796. NS_IMETHODIMP
  797. HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult)
  798. {
  799. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  800. if (!mActor) {
  801. *aResult = false;
  802. return NS_OK;
  803. }
  804. TabParent* tp = TabParent::GetFrom(aFrameLoader);
  805. if (!tp) {
  806. *aResult = false;
  807. return NS_OK;
  808. }
  809. *aResult = mContentParent == tp->Manager();
  810. return NS_OK;
  811. }
  812. NS_IMETHODIMP
  813. HangMonitoredProcess::UserCanceled()
  814. {
  815. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  816. if (mHangData.type() != HangData::TPluginHangData) {
  817. return NS_OK;
  818. }
  819. if (mActor) {
  820. uint32_t id = mHangData.get_PluginHangData().pluginId();
  821. mActor->CleanupPluginHang(id, true);
  822. }
  823. return NS_OK;
  824. }
  825. static bool
  826. InterruptCallback(JSContext* cx)
  827. {
  828. if (HangMonitorChild* child = HangMonitorChild::Get()) {
  829. child->InterruptCallback();
  830. }
  831. return true;
  832. }
  833. ProcessHangMonitor* ProcessHangMonitor::sInstance;
  834. ProcessHangMonitor::ProcessHangMonitor()
  835. : mCPOWTimeout(false)
  836. {
  837. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  838. MOZ_COUNT_CTOR(ProcessHangMonitor);
  839. if (XRE_IsContentProcess()) {
  840. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  841. obs->AddObserver(this, "xpcom-shutdown", false);
  842. }
  843. mThread = new base::Thread("ProcessHangMonitor");
  844. if (!mThread->Start()) {
  845. delete mThread;
  846. mThread = nullptr;
  847. }
  848. }
  849. ProcessHangMonitor::~ProcessHangMonitor()
  850. {
  851. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  852. MOZ_COUNT_DTOR(ProcessHangMonitor);
  853. MOZ_ASSERT(sInstance == this);
  854. sInstance = nullptr;
  855. delete mThread;
  856. }
  857. ProcessHangMonitor*
  858. ProcessHangMonitor::GetOrCreate()
  859. {
  860. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  861. if (!sInstance) {
  862. sInstance = new ProcessHangMonitor();
  863. }
  864. return sInstance;
  865. }
  866. NS_IMPL_ISUPPORTS(ProcessHangMonitor, nsIObserver)
  867. NS_IMETHODIMP
  868. ProcessHangMonitor::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
  869. {
  870. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  871. if (!strcmp(aTopic, "xpcom-shutdown")) {
  872. if (HangMonitorChild* child = HangMonitorChild::Get()) {
  873. child->Shutdown();
  874. delete child;
  875. }
  876. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  877. if (obs) {
  878. obs->RemoveObserver(this, "xpcom-shutdown");
  879. }
  880. }
  881. return NS_OK;
  882. }
  883. ProcessHangMonitor::SlowScriptAction
  884. ProcessHangMonitor::NotifySlowScript(nsITabChild* aTabChild,
  885. const char* aFileName,
  886. unsigned aLineNo)
  887. {
  888. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  889. return HangMonitorChild::Get()->NotifySlowScript(aTabChild, aFileName, aLineNo);
  890. }
  891. bool
  892. ProcessHangMonitor::IsDebuggerStartupComplete()
  893. {
  894. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  895. return HangMonitorChild::Get()->IsDebuggerStartupComplete();
  896. }
  897. bool
  898. ProcessHangMonitor::ShouldTimeOutCPOWs()
  899. {
  900. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  901. if (mCPOWTimeout) {
  902. mCPOWTimeout = false;
  903. return true;
  904. }
  905. return false;
  906. }
  907. void
  908. ProcessHangMonitor::InitiateCPOWTimeout()
  909. {
  910. MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
  911. mCPOWTimeout = true;
  912. }
  913. void
  914. ProcessHangMonitor::NotifyPluginHang(uint32_t aPluginId)
  915. {
  916. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  917. return HangMonitorChild::Get()->NotifyPluginHang(aPluginId);
  918. }
  919. PProcessHangMonitorParent*
  920. mozilla::CreateHangMonitorParent(ContentParent* aContentParent,
  921. mozilla::ipc::Transport* aTransport,
  922. base::ProcessId aOtherPid)
  923. {
  924. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  925. ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
  926. HangMonitorParent* parent = new HangMonitorParent(monitor);
  927. HangMonitoredProcess* process = new HangMonitoredProcess(parent, aContentParent);
  928. parent->SetProcess(process);
  929. monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
  930. <mozilla::ipc::Transport*,
  931. base::ProcessId,
  932. MessageLoop*>(parent,
  933. &HangMonitorParent::Open,
  934. aTransport, aOtherPid,
  935. XRE_GetIOMessageLoop()));
  936. return parent;
  937. }
  938. PProcessHangMonitorChild*
  939. mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport,
  940. base::ProcessId aOtherPid)
  941. {
  942. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  943. JSContext* cx = danger::GetJSContext();
  944. JS_AddInterruptCallback(cx, InterruptCallback);
  945. ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
  946. HangMonitorChild* child = new HangMonitorChild(monitor);
  947. monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
  948. <mozilla::ipc::Transport*,
  949. base::ProcessId,
  950. MessageLoop*>(child,
  951. &HangMonitorChild::Open,
  952. aTransport, aOtherPid,
  953. XRE_GetIOMessageLoop()));
  954. return child;
  955. }
  956. MessageLoop*
  957. ProcessHangMonitor::MonitorLoop()
  958. {
  959. return mThread->message_loop();
  960. }
  961. /* static */ void
  962. ProcessHangMonitor::AddProcess(ContentParent* aContentParent)
  963. {
  964. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  965. if (mozilla::Preferences::GetBool("dom.ipc.processHangMonitor", false)) {
  966. DebugOnly<bool> opened = PProcessHangMonitor::Open(aContentParent);
  967. MOZ_ASSERT(opened);
  968. }
  969. }
  970. /* static */ void
  971. ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent* aParent)
  972. {
  973. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  974. auto parent = static_cast<HangMonitorParent*>(aParent);
  975. parent->Shutdown();
  976. delete parent;
  977. }
  978. /* static */ void
  979. ProcessHangMonitor::ClearHang()
  980. {
  981. MOZ_ASSERT(NS_IsMainThread());
  982. if (HangMonitorChild* child = HangMonitorChild::Get()) {
  983. child->ClearHang();
  984. }
  985. }
  986. /* static */ void
  987. ProcessHangMonitor::ForcePaint(PProcessHangMonitorParent* aParent,
  988. dom::TabParent* aTabParent,
  989. uint64_t aLayerObserverEpoch)
  990. {
  991. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  992. auto parent = static_cast<HangMonitorParent*>(aParent);
  993. parent->ForcePaint(aTabParent, aLayerObserverEpoch);
  994. }
  995. /* static */ void
  996. ProcessHangMonitor::ClearForcePaint()
  997. {
  998. MOZ_RELEASE_ASSERT(NS_IsMainThread());
  999. MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
  1000. if (HangMonitorChild* child = HangMonitorChild::Get()) {
  1001. child->ClearForcePaint();
  1002. }
  1003. }