Bug 1521370 - Add crash guard around VPX decoder creation. r=mattwoodrow
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 13 Mar 2019 02:36:08 +0000
changeset 524656 a6724ca6cb915b86bd84fdf2c3ec3bfb0072302b
parent 524655 383f32cdb53bf9d3ce638dfaff490794758bbf0a
child 524657 6ee4b21eb0e0fef64130e6377d1d54c3b7721fb4
push id2032
push userffxbld-merge
push dateMon, 13 May 2019 09:36:57 +0000
treeherdermozilla-release@455c1065dcbe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1521370
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1521370 - Add crash guard around VPX decoder creation. r=mattwoodrow Differential Revision: https://phabricator.services.mozilla.com/D21477
dom/ipc/ContentParent.cpp
dom/media/platforms/wmf/WMFDecoderModule.cpp
gfx/src/DriverCrashGuard.cpp
gfx/src/DriverCrashGuard.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1120,36 +1120,37 @@ TabParent* ContentParent::CreateBrowser(
   bool isPreloadBrowser = false;
   nsAutoString isPreloadBrowserStr;
   if (aFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::preloadedState,
                              isPreloadBrowserStr)) {
     isPreloadBrowser = isPreloadBrowserStr.EqualsLiteral("preloaded");
   }
 
   RefPtr<ContentParent> constructorSender;
-  MOZ_RELEASE_ASSERT(XRE_IsParentProcess(), "Cannot allocate TabParent in content process");
+  MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
+                     "Cannot allocate TabParent in content process");
   if (aOpenerContentParent) {
     constructorSender = aOpenerContentParent;
   } else {
     if (aContext.IsJSPlugin()) {
       constructorSender =
           GetNewOrUsedJSPluginProcess(aContext.JSPluginId(), initialPriority);
     } else {
-      constructorSender = GetNewOrUsedBrowserProcess(
-          aFrameElement, remoteType, initialPriority, nullptr,
-          isPreloadBrowser);
+      constructorSender =
+          GetNewOrUsedBrowserProcess(aFrameElement, remoteType, initialPriority,
+                                     nullptr, isPreloadBrowser);
     }
     if (!constructorSender) {
       return nullptr;
     }
   }
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   cpm->RegisterRemoteFrame(tabId, ContentParentId(0), openerTabId,
-                            aContext.AsIPCTabContext(),
-                            constructorSender->ChildID());
+                           aContext.AsIPCTabContext(),
+                           constructorSender->ChildID());
 
   if (constructorSender) {
     nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
     docShell->GetTreeOwner(getter_AddRefs(treeOwner));
     if (!treeOwner) {
       return nullptr;
     }
 
@@ -3602,17 +3603,18 @@ PChildToParentStreamParent* ContentParen
 
 bool ContentParent::DeallocPChildToParentStreamParent(
     PChildToParentStreamParent* aActor) {
   delete aActor;
   return true;
 }
 
 PParentToChildStreamParent* ContentParent::AllocPParentToChildStreamParent() {
-  MOZ_CRASH("PParentToChildStreamParent actors should be manually constructed!");
+  MOZ_CRASH(
+      "PParentToChildStreamParent actors should be manually constructed!");
 }
 
 bool ContentParent::DeallocPParentToChildStreamParent(
     PParentToChildStreamParent* aActor) {
   delete aActor;
   return true;
 }
 
@@ -5022,16 +5024,19 @@ mozilla::ipc::IPCResult ContentParent::R
       guard = MakeUnique<gfx::D3D9VideoCrashGuard>(this);
       break;
     case gfx::CrashGuardType::GLContext:
       guard = MakeUnique<gfx::GLContextCrashGuard>(this);
       break;
     case gfx::CrashGuardType::D3D11Video:
       guard = MakeUnique<gfx::D3D11VideoCrashGuard>(this);
       break;
+    case gfx::CrashGuardType::WMFVPXVideo:
+      guard = MakeUnique<gfx::WMFVPXVideoCrashGuard>(this);
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("unknown crash guard type");
       return IPC_FAIL_NO_REASON(this);
   }
 
   if (guard->Crashed()) {
     *aOutCrashed = true;
     return IPC_OK();
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp
@@ -1,73 +1,106 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "WMFDecoderModule.h"
+#include <algorithm>
+#include <vector>
+#include "DriverCrashGuard.h"
 #include "GfxDriverInfo.h"
 #include "MFTDecoder.h"
 #include "MP4Decoder.h"
 #include "MediaInfo.h"
 #include "VPXDecoder.h"
 #include "WMF.h"
 #include "WMFAudioMFTManager.h"
 #include "WMFMediaDataDecoder.h"
 #include "WMFVideoMFTManager.h"
 #include "gfxPrefs.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/WindowsVersion.h"
 #include "mozilla/gfx/gfxVars.h"
+#include "mozilla/mscom/EnsureMTA.h"
 #include "nsAutoPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIGfxInfo.h"
 #include "nsIWindowsRegKey.h"
+#include "nsIXULRuntime.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWindowsHelpers.h"
 #include "prsystem.h"
-#include "nsIXULRuntime.h"
-#include "mozilla/mscom/EnsureMTA.h"
-#include <algorithm>
-#include <vector>
 
 extern const GUID CLSID_WebmMfVpxDec;
-extern const GUID CLSID_AMDWebmMfVp9Dec;
 
 namespace mozilla {
 
 static Atomic<bool> sDXVAEnabled(false);
+static Atomic<bool> sUsableVPXMFT(false);
 
 WMFDecoderModule::~WMFDecoderModule() {
   if (mWMFInitialized) {
     DebugOnly<HRESULT> hr = wmf::MFShutdown();
     NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
   }
 }
 
+static bool CanCreateMFTDecoder(const GUID& aGuid) {
+  // The IMFTransform interface used by MFTDecoder is documented to require to
+  // run on an MTA thread.
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/ee892371(v=vs.85).aspx#components
+  // Note: our normal SharedThreadPool task queues are initialized to MTA, but
+  // the main thread (which calls in here from our CanPlayType implementation)
+  // is not.
+  bool canCreateDecoder = false;
+  mozilla::mscom::EnsureMTA([&]() -> void {
+    if (FAILED(wmf::MFStartup())) {
+      return;
+    }
+    RefPtr<MFTDecoder> decoder(new MFTDecoder());
+    canCreateDecoder = SUCCEEDED(decoder->Create(aGuid));
+    wmf::MFShutdown();
+  });
+  return canCreateDecoder;
+}
+
 /* static */
 void WMFDecoderModule::Init() {
+  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+  bool testForVPx;
   if (XRE_IsContentProcess()) {
     // If we're in the content process and the UseGPUDecoder pref is set, it
     // means that we've given up on the GPU process (it's been crashing) so we
     // should disable DXVA
     sDXVAEnabled = !StaticPrefs::MediaGpuProcessDecoder();
+    // We need to test for VPX in the content process as the GPUDecoderModule
+    // directly calls WMFDecoderModule::Supports in the content process.
+    // This unnecessary requirement will be fixed in bug 1534815.
+    testForVPx = true;
   } else if (XRE_IsGPUProcess()) {
     // Always allow DXVA in the GPU process.
-    sDXVAEnabled = true;
+    testForVPx = sDXVAEnabled = true;
   } else {
     // Only allow DXVA in the UI process if we aren't in e10s Firefox
-    sDXVAEnabled = !mozilla::BrowserTabsRemoteAutostart();
+    testForVPx = sDXVAEnabled = !mozilla::BrowserTabsRemoteAutostart();
   }
 
   sDXVAEnabled = sDXVAEnabled && gfx::gfxVars::CanUseHardwareVideoDecoding();
+  testForVPx = testForVPx && gfx::gfxVars::CanUseHardwareVideoDecoding();
+  if (testForVPx && StaticPrefs::MediaWmfVp9Enabled()) {
+    gfx::WMFVPXVideoCrashGuard guard;
+    if (!guard.Crashed()) {
+      sUsableVPXMFT = CanCreateMFTDecoder(CLSID_WebmMfVpxDec);
+    }
+  }
 }
 
 /* static */
 int WMFDecoderModule::GetNumDecoderThreads() {
   int32_t numCores = PR_GetNumberOfProcessors();
 
   // If we have more than 4 cores, let the decoder decide how many threads.
   // On an 8 core machine, WMF chooses 4 decoder threads.
@@ -80,17 +113,16 @@ int WMFDecoderModule::GetNumDecoderThrea
 
 nsresult WMFDecoderModule::Startup() {
   mWMFInitialized = SUCCEEDED(wmf::MFStartup());
   return mWMFInitialized ? NS_OK : NS_ERROR_FAILURE;
 }
 
 already_AddRefed<MediaDataDecoder> WMFDecoderModule::CreateVideoDecoder(
     const CreateDecoderParams& aParams) {
-
   // Temporary - forces use of VPXDecoder when alpha is present.
   // Bug 1263836 will handle alpha scenario once implemented. It will shift
   // the check for alpha to PDMFactory but not itself remove the need for a
   // check.
   if (aParams.VideoConfig().HasAlpha()) {
     return nullptr;
   }
 
@@ -121,35 +153,16 @@ already_AddRefed<MediaDataDecoder> WMFDe
     return nullptr;
   }
 
   RefPtr<MediaDataDecoder> decoder =
       new WMFMediaDataDecoder(manager.forget(), aParams.mTaskQueue);
   return decoder.forget();
 }
 
-static bool CanCreateMFTDecoder(const GUID& aGuid) {
-  // The IMFTransform interface used by MFTDecoder is documented to require to
-  // run on an MTA thread.
-  // https://msdn.microsoft.com/en-us/library/windows/desktop/ee892371(v=vs.85).aspx#components
-  // Note: our normal SharedThreadPool task queues are initialized to MTA, but
-  // the main thread (which calls in here from our CanPlayType implementation)
-  // is not.
-  bool canCreateDecoder = false;
-  mozilla::mscom::EnsureMTA([&]() -> void {
-    if (FAILED(wmf::MFStartup())) {
-      return;
-    }
-    RefPtr<MFTDecoder> decoder(new MFTDecoder());
-    canCreateDecoder = SUCCEEDED(decoder->Create(aGuid));
-    wmf::MFShutdown();
-  });
-  return canCreateDecoder;
-}
-
 template <const GUID& aGuid>
 static bool CanCreateWMFDecoder() {
   static StaticMutex sMutex;
   StaticMutexAutoLock lock(sMutex);
   static Maybe<bool> result;
   if (result.isNothing()) {
     result.emplace(CanCreateMFTDecoder(aGuid));
   }
@@ -199,26 +212,22 @@ bool WMFDecoderModule::Supports(const Tr
   }
   if (MP4Decoder::IsH264(aTrackInfo.mMimeType) && WMFDecoderModule::HasH264()) {
     return true;
   }
   if (aTrackInfo.mMimeType.EqualsLiteral("audio/mpeg") &&
       CanCreateWMFDecoder<CLSID_CMP3DecMediaObject>()) {
     return true;
   }
-  if (StaticPrefs::MediaWmfVp9Enabled()) {
+  if (sUsableVPXMFT) {
     static const uint32_t VP8_USABLE_BUILD = 16287;
-    if (VPXDecoder::IsVP8(aTrackInfo.mMimeType) &&
-        IsWindowsBuildOrLater(VP8_USABLE_BUILD) &&
-        CanCreateWMFDecoder<CLSID_WebmMfVpxDec>()) {
-      return true;
-    }
-    if (VPXDecoder::IsVP9(aTrackInfo.mMimeType) &&
-        CanCreateWMFDecoder<CLSID_WebmMfVpxDec>()) {
-      return true;
+    if ((VPXDecoder::IsVP8(aTrackInfo.mMimeType) &&
+         IsWindowsBuildOrLater(VP8_USABLE_BUILD)) ||
+        VPXDecoder::IsVP9(aTrackInfo.mMimeType)) {
+      return CanCreateWMFDecoder<CLSID_WebmMfVpxDec>();
     }
   }
 
   // Some unsupported codec.
   return false;
 }
 
 }  // namespace mozilla
--- a/gfx/src/DriverCrashGuard.cpp
+++ b/gfx/src/DriverCrashGuard.cpp
@@ -19,20 +19,17 @@
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/dom/ContentChild.h"
 
 namespace mozilla {
 namespace gfx {
 
 static const size_t NUM_CRASH_GUARD_TYPES = size_t(CrashGuardType::NUM_TYPES);
 static const char* sCrashGuardNames[] = {
-    "d3d11layers",
-    "d3d9video",
-    "glcontext",
-    "d3d11video",
+    "d3d11layers", "d3d9video", "glcontext", "d3d11video", "wmfvpxvideo",
 };
 static_assert(MOZ_ARRAY_LENGTH(sCrashGuardNames) == NUM_CRASH_GUARD_TYPES,
               "CrashGuardType updated without a name string");
 
 static inline void BuildCrashGuardPrefName(CrashGuardType aType,
                                            nsCString& aOutPrefName) {
   MOZ_ASSERT(aType < CrashGuardType::NUM_TYPES);
   MOZ_ASSERT(sCrashGuardNames[size_t(aType)]);
@@ -535,10 +532,28 @@ bool GLContextCrashGuard::UpdateEnvironm
 void GLContextCrashGuard::LogCrashRecovery() {
   gfxCriticalNote << "GLContext just crashed.";
 }
 
 void GLContextCrashGuard::LogFeatureDisabled() {
   gfxCriticalNote << "GLContext remains enabled despite a previous crash.";
 }
 
+WMFVPXVideoCrashGuard::WMFVPXVideoCrashGuard(dom::ContentParent* aContentParent)
+    : DriverCrashGuard(CrashGuardType::WMFVPXVideo, aContentParent) {}
+
+bool WMFVPXVideoCrashGuard::UpdateEnvironment() {
+  // We don't care about any extra preferences here.
+  return false;
+}
+
+void WMFVPXVideoCrashGuard::LogCrashRecovery() {
+  gfxCriticalNote
+      << "WMF VPX decoder just crashed; hardware video will be disabled.";
+}
+
+void WMFVPXVideoCrashGuard::LogFeatureDisabled() {
+  gfxCriticalNote
+      << "WMF VPX video decoding is disabled due to a previous crash.";
+}
+
 }  // namespace gfx
 }  // namespace mozilla
--- a/gfx/src/DriverCrashGuard.h
+++ b/gfx/src/DriverCrashGuard.h
@@ -35,16 +35,17 @@ enum class DriverInitStatus {
   Crashed
 };
 
 enum class CrashGuardType : uint32_t {
   D3D11Layers,
   D3D9Video,
   GLContext,
   D3D11Video,
+  WMFVPXVideo,
   // Add new entries above this line, update the name array in
   // DriverCrashGuard.cpp, and make sure to add an entry in
   // ContentParent.cpp.
 
   NUM_TYPES
 };
 
 // DriverCrashGuard is used to detect crashes at graphics driver callsites.
@@ -165,12 +166,22 @@ class GLContextCrashGuard final : public
   void Initialize() override;
 
  protected:
   bool UpdateEnvironment() override;
   void LogCrashRecovery() override;
   void LogFeatureDisabled() override;
 };
 
+class WMFVPXVideoCrashGuard final : public DriverCrashGuard {
+ public:
+  explicit WMFVPXVideoCrashGuard(dom::ContentParent* aContentParent = nullptr);
+
+ protected:
+  bool UpdateEnvironment() override;
+  void LogCrashRecovery() override;
+  void LogFeatureDisabled() override;
+};
+
 }  // namespace gfx
 }  // namespace mozilla
 
 #endif  // gfx_src_DriverCrashGuard_h__