123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "mozilla/ProcessHangMonitor.h"
- #include "mozilla/ProcessHangMonitorIPC.h"
- #include "jsapi.h"
- #include "js/GCAPI.h"
- #include "mozilla/Atomics.h"
- #include "mozilla/BackgroundHangMonitor.h"
- #include "mozilla/dom/ContentParent.h"
- #include "mozilla/dom/Element.h"
- #include "mozilla/dom/ScriptSettings.h"
- #include "mozilla/dom/TabChild.h"
- #include "mozilla/dom/TabParent.h"
- #include "mozilla/Monitor.h"
- #include "mozilla/plugins/PluginBridge.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/Unused.h"
- #include "nsIFrameLoader.h"
- #include "nsIHangReport.h"
- #include "nsITabParent.h"
- #include "nsPluginHost.h"
- #include "nsThreadUtils.h"
- #include "base/task.h"
- #include "base/thread.h"
- #ifdef XP_WIN
- // For IsDebuggerPresent()
- #include <windows.h>
- #endif
- using namespace mozilla;
- using namespace mozilla::dom;
- /*
- * Basic architecture:
- *
- * Each process has its own ProcessHangMonitor singleton. This singleton exists
- * as long as there is at least one content process in the system. Each content
- * process has a HangMonitorChild and the chrome process has one
- * HangMonitorParent per process. Each process (including the chrome process)
- * runs a hang monitoring thread. The PHangMonitor actors are bound to this
- * thread so that they never block on the main thread.
- *
- * When the content process detects a hang, it posts a task to its hang thread,
- * which sends an IPC message to the hang thread in the parent. The parent
- * cancels any ongoing CPOW requests and then posts a runnable to the main
- * thread that notifies Firefox frontend code of the hang. The frontend code is
- * passed an nsIHangReport, which can be used to terminate the hang.
- *
- * If the user chooses to terminate a script, a task is posted to the chrome
- * process's hang monitoring thread, which sends an IPC message to the hang
- * thread in the content process. That thread sets a flag to indicate that JS
- * execution should be terminated the next time it hits the interrupt
- * callback. A similar scheme is used for debugging slow scripts. If a content
- * process or plug-in needs to be terminated, the chrome process does so
- * directly, without messaging the content process.
- */
- namespace {
- /* Child process objects */
- class HangMonitorChild
- : public PProcessHangMonitorChild
- {
- public:
- explicit HangMonitorChild(ProcessHangMonitor* aMonitor);
- virtual ~HangMonitorChild();
- void Open(Transport* aTransport, ProcessId aOtherPid,
- MessageLoop* aIOLoop);
- typedef ProcessHangMonitor::SlowScriptAction SlowScriptAction;
- SlowScriptAction NotifySlowScript(nsITabChild* aTabChild,
- const char* aFileName,
- unsigned aLineNo);
- void NotifySlowScriptAsync(TabId aTabId,
- const nsCString& aFileName,
- unsigned aLineNo);
- bool IsDebuggerStartupComplete();
- void NotifyPluginHang(uint32_t aPluginId);
- void NotifyPluginHangAsync(uint32_t aPluginId);
- void ClearHang();
- void ClearHangAsync();
- void ClearForcePaint();
- virtual bool RecvTerminateScript() override;
- virtual bool RecvBeginStartingDebugger() override;
- virtual bool RecvEndStartingDebugger() override;
- virtual bool RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch) override;
- virtual void ActorDestroy(ActorDestroyReason aWhy) override;
- void InterruptCallback();
- void Shutdown();
- static HangMonitorChild* Get() { return sInstance; }
- MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
- private:
- void ShutdownOnThread();
- static Atomic<HangMonitorChild*> sInstance;
- UniquePtr<BackgroundHangMonitor> mForcePaintMonitor;
- const RefPtr<ProcessHangMonitor> mHangMonitor;
- Monitor mMonitor;
- // Main thread-only.
- bool mSentReport;
- // These fields must be accessed with mMonitor held.
- bool mTerminateScript;
- bool mStartDebugger;
- bool mFinishedStartingDebugger;
- bool mForcePaint;
- TabId mForcePaintTab;
- MOZ_INIT_OUTSIDE_CTOR uint64_t mForcePaintEpoch;
- JSContext* mContext;
- bool mShutdownDone;
- // This field is only accessed on the hang thread.
- bool mIPCOpen;
- };
- Atomic<HangMonitorChild*> HangMonitorChild::sInstance;
- /* Parent process objects */
- class HangMonitorParent;
- class HangMonitoredProcess final
- : public nsIHangReport
- {
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- HangMonitoredProcess(HangMonitorParent* aActor,
- ContentParent* aContentParent)
- : mActor(aActor), mContentParent(aContentParent) {}
- NS_IMETHOD GetHangType(uint32_t* aHangType) override;
- NS_IMETHOD GetScriptBrowser(nsIDOMElement** aBrowser) override;
- NS_IMETHOD GetScriptFileName(nsACString& aFileName) override;
- NS_IMETHOD GetScriptLineNo(uint32_t* aLineNo) override;
- NS_IMETHOD GetPluginName(nsACString& aPluginName) override;
- NS_IMETHOD TerminateScript() override;
- NS_IMETHOD BeginStartingDebugger() override;
- NS_IMETHOD EndStartingDebugger() override;
- NS_IMETHOD TerminatePlugin() override;
- NS_IMETHOD UserCanceled() override;
- NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
- // Called when a content process shuts down.
- void Clear() {
- mContentParent = nullptr;
- mActor = nullptr;
- }
- /**
- * Sets the information associated with this hang: this includes the ID of
- * the plugin which caused the hang as well as the content PID. The ID of
- * a minidump taken during the hang can also be provided.
- *
- * @param aHangData The hang information
- * @param aDumpId The ID of a minidump taken when the hang occurred
- */
- void SetHangData(const HangData& aHangData, const nsAString& aDumpId) {
- mHangData = aHangData;
- mDumpId = aDumpId;
- }
- void ClearHang() {
- mHangData = HangData();
- mDumpId.Truncate();
- }
- private:
- ~HangMonitoredProcess() {}
- // Everything here is main thread-only.
- HangMonitorParent* mActor;
- ContentParent* mContentParent;
- HangData mHangData;
- nsAutoString mDumpId;
- };
- class HangMonitorParent
- : public PProcessHangMonitorParent
- {
- public:
- explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
- virtual ~HangMonitorParent();
- void Open(Transport* aTransport, ProcessId aPid, MessageLoop* aIOLoop);
- virtual bool RecvHangEvidence(const HangData& aHangData) override;
- virtual bool RecvClearHang() override;
- virtual void ActorDestroy(ActorDestroyReason aWhy) override;
- void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
- void Shutdown();
- void ForcePaint(dom::TabParent* aTabParent, uint64_t aLayerObserverEpoch);
- void TerminateScript();
- void BeginStartingDebugger();
- void EndStartingDebugger();
- void CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles);
- /**
- * Update the dump for the specified plugin. This method is thread-safe and
- * is used to replace a browser minidump with a full minidump. If aDumpId is
- * empty this is a no-op.
- */
- void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId);
- MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
- private:
- bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
- void ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch);
- void ShutdownOnThread();
- const RefPtr<ProcessHangMonitor> mHangMonitor;
- // This field is read-only after construction.
- bool mReportHangs;
- // This field is only accessed on the hang thread.
- bool mIPCOpen;
- Monitor mMonitor;
- // Must be accessed with mMonitor held.
- RefPtr<HangMonitoredProcess> mProcess;
- bool mShutdownDone;
- // Map from plugin ID to crash dump ID. Protected by mBrowserCrashDumpHashLock.
- nsDataHashtable<nsUint32HashKey, nsString> mBrowserCrashDumpIds;
- Mutex mBrowserCrashDumpHashLock;
- };
- } // namespace
- /* HangMonitorChild implementation */
- HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
- : mHangMonitor(aMonitor),
- mMonitor("HangMonitorChild lock"),
- mSentReport(false),
- mTerminateScript(false),
- mStartDebugger(false),
- mFinishedStartingDebugger(false),
- mForcePaint(false),
- mShutdownDone(false),
- mIPCOpen(true)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- mContext = danger::GetJSContext();
- mForcePaintMonitor =
- MakeUnique<mozilla::BackgroundHangMonitor>("Gecko_Child_ForcePaint",
- 128, /* ms timeout for microhangs */
- 8192 /* ms timeout for permahangs */,
- BackgroundHangMonitor::THREAD_PRIVATE);
- }
- HangMonitorChild::~HangMonitorChild()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(sInstance == this);
- mForcePaintMonitor = nullptr;
- sInstance = nullptr;
- }
- void
- HangMonitorChild::InterruptCallback()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- bool forcePaint;
- TabId forcePaintTab;
- uint64_t forcePaintEpoch;
- {
- MonitorAutoLock lock(mMonitor);
- forcePaint = mForcePaint;
- forcePaintTab = mForcePaintTab;
- forcePaintEpoch = mForcePaintEpoch;
- mForcePaint = false;
- }
- if (forcePaint) {
- RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
- if (tabChild) {
- tabChild->ForcePaint(forcePaintEpoch);
- }
- }
- }
- void
- HangMonitorChild::Shutdown()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- MonitorAutoLock lock(mMonitor);
- while (!mShutdownDone) {
- mMonitor.Wait();
- }
- }
- void
- HangMonitorChild::ShutdownOnThread()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- MonitorAutoLock lock(mMonitor);
- mShutdownDone = true;
- mMonitor.Notify();
- }
- void
- HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy)
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- mIPCOpen = false;
- // We use a task here to ensure that IPDL is finished with this
- // HangMonitorChild before it gets deleted on the main thread.
- MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ShutdownOnThread));
- }
- bool
- HangMonitorChild::RecvTerminateScript()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- MonitorAutoLock lock(mMonitor);
- mTerminateScript = true;
- return true;
- }
- bool
- HangMonitorChild::RecvBeginStartingDebugger()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- MonitorAutoLock lock(mMonitor);
- mStartDebugger = true;
- return true;
- }
- bool
- HangMonitorChild::RecvEndStartingDebugger()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- MonitorAutoLock lock(mMonitor);
- mFinishedStartingDebugger = true;
- return true;
- }
- bool
- HangMonitorChild::RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch)
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- mForcePaintMonitor->NotifyActivity();
- {
- MonitorAutoLock lock(mMonitor);
- mForcePaint = true;
- mForcePaintTab = aTabId;
- mForcePaintEpoch = aLayerObserverEpoch;
- }
- JS_RequestInterruptCallback(mContext);
- return true;
- }
- void
- HangMonitorChild::ClearForcePaint()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
- mForcePaintMonitor->NotifyWait();
- }
- void
- HangMonitorChild::Open(Transport* aTransport, ProcessId aPid,
- MessageLoop* aIOLoop)
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- MOZ_ASSERT(!sInstance);
- sInstance = this;
- DebugOnly<bool> ok = PProcessHangMonitorChild::Open(aTransport, aPid, aIOLoop);
- MOZ_ASSERT(ok);
- }
- void
- HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
- const nsCString& aFileName,
- unsigned aLineNo)
- {
- if (mIPCOpen) {
- Unused << SendHangEvidence(SlowScriptData(aTabId, aFileName, aLineNo));
- }
- }
- HangMonitorChild::SlowScriptAction
- HangMonitorChild::NotifySlowScript(nsITabChild* aTabChild,
- const char* aFileName,
- unsigned aLineNo)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- mSentReport = true;
- {
- MonitorAutoLock lock(mMonitor);
- if (mTerminateScript) {
- mTerminateScript = false;
- return SlowScriptAction::Terminate;
- }
- if (mStartDebugger) {
- mStartDebugger = false;
- return SlowScriptAction::StartDebugger;
- }
- }
- TabId id;
- if (aTabChild) {
- RefPtr<TabChild> tabChild = static_cast<TabChild*>(aTabChild);
- id = tabChild->GetTabId();
- }
- nsAutoCString filename(aFileName);
- MonitorLoop()->PostTask(NewNonOwningRunnableMethod
- <TabId, nsCString, unsigned>(this,
- &HangMonitorChild::NotifySlowScriptAsync,
- id, filename, aLineNo));
- return SlowScriptAction::Continue;
- }
- bool
- HangMonitorChild::IsDebuggerStartupComplete()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- MonitorAutoLock lock(mMonitor);
- if (mFinishedStartingDebugger) {
- mFinishedStartingDebugger = false;
- return true;
- }
- return false;
- }
- void
- HangMonitorChild::NotifyPluginHang(uint32_t aPluginId)
- {
- // main thread in the child
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- mSentReport = true;
- // bounce to background thread
- MonitorLoop()->PostTask(NewNonOwningRunnableMethod<uint32_t>(this,
- &HangMonitorChild::NotifyPluginHangAsync,
- aPluginId));
- }
- void
- HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId)
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- // bounce back to parent on background thread
- if (mIPCOpen) {
- Unused << SendHangEvidence(PluginHangData(aPluginId,
- base::GetCurrentProcId()));
- }
- }
- void
- HangMonitorChild::ClearHang()
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (mSentReport) {
- // bounce to background thread
- MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ClearHangAsync));
- MonitorAutoLock lock(mMonitor);
- mSentReport = false;
- mTerminateScript = false;
- mStartDebugger = false;
- mFinishedStartingDebugger = false;
- }
- }
- void
- HangMonitorChild::ClearHangAsync()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- // bounce back to parent on background thread
- if (mIPCOpen) {
- Unused << SendClearHang();
- }
- }
- /* HangMonitorParent implementation */
- HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor)
- : mHangMonitor(aMonitor),
- mIPCOpen(true),
- mMonitor("HangMonitorParent lock"),
- mShutdownDone(false),
- mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock")
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false);
- }
- HangMonitorParent::~HangMonitorParent()
- {
- }
- void
- HangMonitorParent::Shutdown()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- MonitorAutoLock lock(mMonitor);
- if (mProcess) {
- mProcess->Clear();
- mProcess = nullptr;
- }
- MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this,
- &HangMonitorParent::ShutdownOnThread));
- while (!mShutdownDone) {
- mMonitor.Wait();
- }
- }
- void
- HangMonitorParent::ShutdownOnThread()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- // mIPCOpen is only written from this thread, so need need to take the lock
- // here. We'd be shooting ourselves in the foot, because ActorDestroy takes
- // it.
- if (mIPCOpen) {
- Close();
- }
- MonitorAutoLock lock(mMonitor);
- mShutdownDone = true;
- mMonitor.Notify();
- }
- void
- HangMonitorParent::ForcePaint(dom::TabParent* aTab, uint64_t aLayerObserverEpoch)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- TabId id = aTab->GetTabId();
- MonitorLoop()->PostTask(NewNonOwningRunnableMethod<TabId, uint64_t>(
- this, &HangMonitorParent::ForcePaintOnThread, id, aLayerObserverEpoch));
- }
- void
- HangMonitorParent::ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch)
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- if (mIPCOpen) {
- Unused << SendForcePaint(aTabId, aLayerObserverEpoch);
- }
- }
- void
- HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy)
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- mIPCOpen = false;
- }
- void
- HangMonitorParent::Open(Transport* aTransport, ProcessId aPid,
- MessageLoop* aIOLoop)
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- DebugOnly<bool> ok = PProcessHangMonitorParent::Open(aTransport, aPid, aIOLoop);
- MOZ_ASSERT(ok);
- }
- class HangObserverNotifier final : public Runnable
- {
- public:
- HangObserverNotifier(HangMonitoredProcess* aProcess,
- HangMonitorParent *aParent,
- const HangData& aHangData,
- const nsString& aBrowserDumpId,
- bool aTakeMinidump)
- : mProcess(aProcess),
- mParent(aParent),
- mHangData(aHangData),
- mBrowserDumpId(aBrowserDumpId),
- mTakeMinidump(aTakeMinidump)
- {}
- NS_IMETHOD
- Run() override
- {
- // chrome process, main thread
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- nsString dumpId;
- if ((mHangData.type() == HangData::TPluginHangData) && mTakeMinidump) {
- // We've been handed a partial minidump; complete it with plugin and
- // content process dumps.
- const PluginHangData& phd = mHangData.get_PluginHangData();
- plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
- mBrowserDumpId, dumpId);
- mParent->UpdateMinidump(phd.pluginId(), dumpId);
- } else {
- // We already have a full minidump; go ahead and use it.
- dumpId = mBrowserDumpId;
- }
- mProcess->SetHangData(mHangData, dumpId);
- nsCOMPtr<nsIObserverService> observerService =
- mozilla::services::GetObserverService();
- observerService->NotifyObservers(mProcess, "process-hang-report", nullptr);
- return NS_OK;
- }
- private:
- RefPtr<HangMonitoredProcess> mProcess;
- HangMonitorParent* mParent;
- HangData mHangData;
- nsAutoString mBrowserDumpId;
- bool mTakeMinidump;
- };
- // Take a minidump of the browser process if one wasn't already taken for the
- // plugin that caused the hang. Return false if a dump was already available or
- // true if new one has been taken.
- bool
- HangMonitorParent::TakeBrowserMinidump(const PluginHangData& aPhd,
- nsString& aCrashId)
- {
- return false;
- }
- bool
- HangMonitorParent::RecvHangEvidence(const HangData& aHangData)
- {
- // chrome process, background thread
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- if (!mReportHangs) {
- return true;
- }
- #ifdef XP_WIN
- // Don't report hangs if we're debugging the process. You can comment this
- // line out for testing purposes.
- if (IsDebuggerPresent()) {
- return true;
- }
- #endif
- // Before we wake up the browser main thread we want to take a
- // browser minidump.
- nsAutoString crashId;
- bool takeMinidump = false;
- if (aHangData.type() == HangData::TPluginHangData) {
- takeMinidump = TakeBrowserMinidump(aHangData.get_PluginHangData(), crashId);
- }
- mHangMonitor->InitiateCPOWTimeout();
- MonitorAutoLock lock(mMonitor);
- nsCOMPtr<nsIRunnable> notifier =
- new HangObserverNotifier(mProcess, this, aHangData, crashId, takeMinidump);
- NS_DispatchToMainThread(notifier);
- return true;
- }
- class ClearHangNotifier final : public Runnable
- {
- public:
- explicit ClearHangNotifier(HangMonitoredProcess* aProcess)
- : mProcess(aProcess)
- {}
- NS_IMETHOD
- Run() override
- {
- // chrome process, main thread
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- mProcess->ClearHang();
- nsCOMPtr<nsIObserverService> observerService =
- mozilla::services::GetObserverService();
- observerService->NotifyObservers(mProcess, "clear-hang-report", nullptr);
- return NS_OK;
- }
- private:
- RefPtr<HangMonitoredProcess> mProcess;
- };
- bool
- HangMonitorParent::RecvClearHang()
- {
- // chrome process, background thread
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- if (!mReportHangs) {
- return true;
- }
- mHangMonitor->InitiateCPOWTimeout();
- MonitorAutoLock lock(mMonitor);
- nsCOMPtr<nsIRunnable> notifier =
- new ClearHangNotifier(mProcess);
- NS_DispatchToMainThread(notifier);
- return true;
- }
- void
- HangMonitorParent::TerminateScript()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- if (mIPCOpen) {
- Unused << SendTerminateScript();
- }
- }
- void
- HangMonitorParent::BeginStartingDebugger()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- if (mIPCOpen) {
- Unused << SendBeginStartingDebugger();
- }
- }
- void
- HangMonitorParent::EndStartingDebugger()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- if (mIPCOpen) {
- Unused << SendEndStartingDebugger();
- }
- }
- void
- HangMonitorParent::CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles)
- {
- MutexAutoLock lock(mBrowserCrashDumpHashLock);
- nsAutoString crashId;
- if (!mBrowserCrashDumpIds.Get(aPluginId, &crashId)) {
- return;
- }
- mBrowserCrashDumpIds.Remove(aPluginId);
- }
- void
- HangMonitorParent::UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId)
- {
- if (aDumpId.IsEmpty()) {
- return;
- }
- MutexAutoLock lock(mBrowserCrashDumpHashLock);
- mBrowserCrashDumpIds.Put(aPluginId, aDumpId);
- }
- /* HangMonitoredProcess implementation */
- NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
- NS_IMETHODIMP
- HangMonitoredProcess::GetHangType(uint32_t* aHangType)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- switch (mHangData.type()) {
- case HangData::TSlowScriptData:
- *aHangType = SLOW_SCRIPT;
- break;
- case HangData::TPluginHangData:
- *aHangType = PLUGIN_HANG;
- break;
- default:
- MOZ_ASSERT_UNREACHABLE("Unexpected HangData type");
- return NS_ERROR_UNEXPECTED;
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::GetScriptBrowser(nsIDOMElement** aBrowser)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TSlowScriptData) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- TabId tabId = mHangData.get_SlowScriptData().tabId();
- if (!mContentParent) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- nsTArray<PBrowserParent*> tabs;
- mContentParent->ManagedPBrowserParent(tabs);
- for (size_t i = 0; i < tabs.Length(); i++) {
- TabParent* tp = TabParent::GetFrom(tabs[i]);
- if (tp->GetTabId() == tabId) {
- nsCOMPtr<nsIDOMElement> node = do_QueryInterface(tp->GetOwnerElement());
- node.forget(aBrowser);
- return NS_OK;
- }
- }
- *aBrowser = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::GetScriptFileName(nsACString& aFileName)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TSlowScriptData) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- aFileName = mHangData.get_SlowScriptData().filename();
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::GetScriptLineNo(uint32_t* aLineNo)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TSlowScriptData) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- *aLineNo = mHangData.get_SlowScriptData().lineno();
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::GetPluginName(nsACString& aPluginName)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TPluginHangData) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- uint32_t id = mHangData.get_PluginHangData().pluginId();
- RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
- nsPluginTag* tag = host->PluginWithId(id);
- if (!tag) {
- return NS_ERROR_UNEXPECTED;
- }
- aPluginName = tag->Name();
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::TerminateScript()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TSlowScriptData) {
- return NS_ERROR_UNEXPECTED;
- }
- if (!mActor) {
- return NS_ERROR_UNEXPECTED;
- }
- ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor,
- &HangMonitorParent::TerminateScript));
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::BeginStartingDebugger()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TSlowScriptData) {
- return NS_ERROR_UNEXPECTED;
- }
- if (!mActor) {
- return NS_ERROR_UNEXPECTED;
- }
- ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor,
- &HangMonitorParent::BeginStartingDebugger));
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::EndStartingDebugger()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TSlowScriptData) {
- return NS_ERROR_UNEXPECTED;
- }
- if (!mActor) {
- return NS_ERROR_UNEXPECTED;
- }
- ProcessHangMonitor::Get()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor,
- &HangMonitorParent::EndStartingDebugger));
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::TerminatePlugin()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TPluginHangData) {
- return NS_ERROR_UNEXPECTED;
- }
- // Use the multi-process crash report generated earlier.
- uint32_t id = mHangData.get_PluginHangData().pluginId();
- base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId();
- plugins::TerminatePlugin(id, contentPid, NS_LITERAL_CSTRING("HangMonitor"),
- mDumpId);
- if (mActor) {
- mActor->CleanupPluginHang(id, false);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (!mActor) {
- *aResult = false;
- return NS_OK;
- }
- TabParent* tp = TabParent::GetFrom(aFrameLoader);
- if (!tp) {
- *aResult = false;
- return NS_OK;
- }
- *aResult = mContentParent == tp->Manager();
- return NS_OK;
- }
- NS_IMETHODIMP
- HangMonitoredProcess::UserCanceled()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mHangData.type() != HangData::TPluginHangData) {
- return NS_OK;
- }
- if (mActor) {
- uint32_t id = mHangData.get_PluginHangData().pluginId();
- mActor->CleanupPluginHang(id, true);
- }
- return NS_OK;
- }
- static bool
- InterruptCallback(JSContext* cx)
- {
- if (HangMonitorChild* child = HangMonitorChild::Get()) {
- child->InterruptCallback();
- }
- return true;
- }
- ProcessHangMonitor* ProcessHangMonitor::sInstance;
- ProcessHangMonitor::ProcessHangMonitor()
- : mCPOWTimeout(false)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- MOZ_COUNT_CTOR(ProcessHangMonitor);
- if (XRE_IsContentProcess()) {
- nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
- obs->AddObserver(this, "xpcom-shutdown", false);
- }
- mThread = new base::Thread("ProcessHangMonitor");
- if (!mThread->Start()) {
- delete mThread;
- mThread = nullptr;
- }
- }
- ProcessHangMonitor::~ProcessHangMonitor()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- MOZ_COUNT_DTOR(ProcessHangMonitor);
- MOZ_ASSERT(sInstance == this);
- sInstance = nullptr;
- delete mThread;
- }
- ProcessHangMonitor*
- ProcessHangMonitor::GetOrCreate()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (!sInstance) {
- sInstance = new ProcessHangMonitor();
- }
- return sInstance;
- }
- NS_IMPL_ISUPPORTS(ProcessHangMonitor, nsIObserver)
- NS_IMETHODIMP
- ProcessHangMonitor::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (!strcmp(aTopic, "xpcom-shutdown")) {
- if (HangMonitorChild* child = HangMonitorChild::Get()) {
- child->Shutdown();
- delete child;
- }
- nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
- if (obs) {
- obs->RemoveObserver(this, "xpcom-shutdown");
- }
- }
- return NS_OK;
- }
- ProcessHangMonitor::SlowScriptAction
- ProcessHangMonitor::NotifySlowScript(nsITabChild* aTabChild,
- const char* aFileName,
- unsigned aLineNo)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- return HangMonitorChild::Get()->NotifySlowScript(aTabChild, aFileName, aLineNo);
- }
- bool
- ProcessHangMonitor::IsDebuggerStartupComplete()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- return HangMonitorChild::Get()->IsDebuggerStartupComplete();
- }
- bool
- ProcessHangMonitor::ShouldTimeOutCPOWs()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mCPOWTimeout) {
- mCPOWTimeout = false;
- return true;
- }
- return false;
- }
- void
- ProcessHangMonitor::InitiateCPOWTimeout()
- {
- MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
- mCPOWTimeout = true;
- }
- void
- ProcessHangMonitor::NotifyPluginHang(uint32_t aPluginId)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- return HangMonitorChild::Get()->NotifyPluginHang(aPluginId);
- }
- PProcessHangMonitorParent*
- mozilla::CreateHangMonitorParent(ContentParent* aContentParent,
- mozilla::ipc::Transport* aTransport,
- base::ProcessId aOtherPid)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
- HangMonitorParent* parent = new HangMonitorParent(monitor);
- HangMonitoredProcess* process = new HangMonitoredProcess(parent, aContentParent);
- parent->SetProcess(process);
- monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
- <mozilla::ipc::Transport*,
- base::ProcessId,
- MessageLoop*>(parent,
- &HangMonitorParent::Open,
- aTransport, aOtherPid,
- XRE_GetIOMessageLoop()));
- return parent;
- }
- PProcessHangMonitorChild*
- mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport,
- base::ProcessId aOtherPid)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- JSContext* cx = danger::GetJSContext();
- JS_AddInterruptCallback(cx, InterruptCallback);
- ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
- HangMonitorChild* child = new HangMonitorChild(monitor);
- monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
- <mozilla::ipc::Transport*,
- base::ProcessId,
- MessageLoop*>(child,
- &HangMonitorChild::Open,
- aTransport, aOtherPid,
- XRE_GetIOMessageLoop()));
- return child;
- }
- MessageLoop*
- ProcessHangMonitor::MonitorLoop()
- {
- return mThread->message_loop();
- }
- /* static */ void
- ProcessHangMonitor::AddProcess(ContentParent* aContentParent)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- if (mozilla::Preferences::GetBool("dom.ipc.processHangMonitor", false)) {
- DebugOnly<bool> opened = PProcessHangMonitor::Open(aContentParent);
- MOZ_ASSERT(opened);
- }
- }
- /* static */ void
- ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent* aParent)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- auto parent = static_cast<HangMonitorParent*>(aParent);
- parent->Shutdown();
- delete parent;
- }
- /* static */ void
- ProcessHangMonitor::ClearHang()
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (HangMonitorChild* child = HangMonitorChild::Get()) {
- child->ClearHang();
- }
- }
- /* static */ void
- ProcessHangMonitor::ForcePaint(PProcessHangMonitorParent* aParent,
- dom::TabParent* aTabParent,
- uint64_t aLayerObserverEpoch)
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- auto parent = static_cast<HangMonitorParent*>(aParent);
- parent->ForcePaint(aTabParent, aLayerObserverEpoch);
- }
- /* static */ void
- ProcessHangMonitor::ClearForcePaint()
- {
- MOZ_RELEASE_ASSERT(NS_IsMainThread());
- MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
- if (HangMonitorChild* child = HangMonitorChild::Get()) {
- child->ClearForcePaint();
- }
- }
|