Add an nsIGfxInfo for describing active crash guards. (bug 1270894 part 1, r=milan)
☠☠ backed out by ff2c2c132be9 ☠ ☠
authorDavid Anderson <danderson@mozilla.com>
Mon, 09 May 2016 13:15:58 -0700
changeset 337703 1bb479eb334cf745759e76090b7e4817110168f3
parent 337702 a07fa49c4b0f7e92b1a832655414fe4581479e82
child 337704 8c0d3d862f1410236d7abbbe388a4a1d5868fcc9
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmilan
bugs1270894
milestone49.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
Add an nsIGfxInfo for describing active crash guards. (bug 1270894 part 1, r=milan)
gfx/src/DriverCrashGuard.cpp
gfx/src/DriverCrashGuard.h
widget/GfxInfoBase.cpp
widget/GfxInfoBase.h
widget/nsIGfxInfo.idl
--- a/gfx/src/DriverCrashGuard.cpp
+++ b/gfx/src/DriverCrashGuard.cpp
@@ -18,69 +18,88 @@
 #include "mozilla/Services.h"
 #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[NUM_CRASH_GUARD_TYPES] = {
+static const char* sCrashGuardNames[] = {
   "d3d11layers",
   "d3d9video",
   "glcontext",
+  "d3d11video",
 };
+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(mType < CrashGuardType::NUM_TYPES);
+  MOZ_ASSERT(sCrashGuardNames[size_t(mType)]);
+
+  aOutPrefName.Assign("gfx.crash-guard.status.");
+  aOutPrefName.Append(sCrashGuardNames[size_t(aType)]);
+}
 
 DriverCrashGuard::DriverCrashGuard(CrashGuardType aType, dom::ContentParent* aContentParent)
  : mType(aType)
  , mMode(aContentParent ? Mode::Proxy : Mode::Normal)
  , mInitialized(false)
  , mGuardActivated(false)
  , mCrashDetected(false)
 {
-  MOZ_ASSERT(mType < CrashGuardType::NUM_TYPES);
-
-  mStatusPref.Assign("gfx.crash-guard.status.");
-  mStatusPref.Append(sCrashGuardNames[size_t(mType)]);
+  BuildCrashGuardPrefName(aType, mStatusPref);
 }
 
 void
 DriverCrashGuard::InitializeIfNeeded()
 {
   if (mInitialized) {
     return;
   }
 
   mInitialized = true;
   Initialize();
 }
 
-void
-DriverCrashGuard::Initialize()
+static inline bool
+AreCrashGuardsEnabled()
 {
 #ifdef NIGHTLY_BUILD
   // We only use the crash guard on non-nightly channels, since the nightly
   // channel is for development and having graphics features perma-disabled
   // is rather annoying.
-  return;
+  return false;
+#else
+  // Check to see if all guards have been disabled through the environment.
+  if (gfxEnv::DisableCrashGuard()) {
+    return false;
+  }
+  return true;
 #endif
+}
+
+void
+DriverCrashGuard::Initialize()
+{
+  if (!AreCrashGuardsEnabled()) {
+    return;
+  }
 
   // Using DriverCrashGuard off the main thread currently does not work. Under
   // e10s it could conceivably work by dispatching the IPC calls via the main
   // thread. In the parent process this would be harder. For now, we simply
   // exit early instead.
   if (!NS_IsMainThread()) {
     return;
   }
 
-  // Check to see if all guards have been disabled through the environment.
-  if (gfxEnv::DisableCrashGuard()) {
-    return;
-  }
-
   mGfxInfo = services::GetGfxInfo();
 
   if (XRE_IsContentProcess()) {
     // Ask the parent whether or not activating the guard is okay. The parent
     // won't bother if it detected a crash.
     dom::ContentChild* cc = dom::ContentChild::GetSingleton();
     cc->SendBeginDriverCrashGuard(uint32_t(mType), &mCrashDetected);
     if (mCrashDetected) {
@@ -361,16 +380,41 @@ DriverCrashGuard::FlushPreferences()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   if (nsIPrefService* prefService = Preferences::GetService()) {
     prefService->SavePrefFile(nullptr);
   }
 }
 
+void
+DriverCrashGuard::ForEachActiveCrashGuard(const CrashGuardCallback& aCallback)
+{
+  if (!AreCrashGuardsEnabled()) {
+    // Even if guards look active (via prefs), they can be ignored if globally
+    // disabled.
+    return;
+  }
+
+  for (size_t i = 0; i < NUM_CRASH_GUARD_TYPES; i++) {
+    CrashGuardType type = static_cast<CrashGuardType>(i);
+
+    nsCString prefName;
+    BuildCrashGuardPrefName(type, prefName);
+
+    auto status =
+      static_cast<DriverInitStatus>(Preferences::GetInt(prefName.get(), 0));
+    if (status != DriverInitStatus::Crashed) {
+      continue;
+    }
+
+    aCallback(sCrashGuardNames[i], prefName.get());
+  }
+}
+
 D3D11LayersCrashGuard::D3D11LayersCrashGuard(dom::ContentParent* aContentParent)
  : DriverCrashGuard(CrashGuardType::D3D11Layers, aContentParent)
 {
 }
 
 void
 D3D11LayersCrashGuard::Initialize()
 {
--- a/gfx/src/DriverCrashGuard.h
+++ b/gfx/src/DriverCrashGuard.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef gfx_src_DriverCrashGuard_h__
 #define gfx_src_DriverCrashGuard_h__
 
 #include "nsCOMPtr.h"
 #include "nsIGfxInfo.h"
 #include "nsIFile.h"
 #include "nsString.h"
+#include "mozilla/Function.h"
 #include <string>
 
 namespace mozilla {
 
 namespace dom {
 class ContentParent;
 } // namespace dom
 
@@ -35,16 +36,20 @@ enum class DriverInitStatus
 };
 
 enum class CrashGuardType : uint32_t
 {
   D3D11Layers,
   D3D9Video,
   GLContext,
   D3D11Video,
+  // 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.
 // 
 // If the graphics environment is unrecognized or has changed since the last
 // session, the crash guard will activate and will detect any crashes within
 // the scope of the guard object.
@@ -73,16 +78,20 @@ public:
   enum class Mode {
     // Normal operation.
     Normal,
 
     // Acting as a proxy between the parent and child process.
     Proxy
   };
 
+  typedef mozilla::function<void(const char* aName, const char* aPrefName)>
+    CrashGuardCallback;
+  static void ForEachActiveCrashGuard(const CrashGuardCallback& aCallback);
+
 protected:
   virtual void Initialize();
   virtual bool UpdateEnvironment() = 0;
   virtual void LogCrashRecovery() = 0;
   virtual void LogFeatureDisabled() = 0;
 
   // Helper functions.
   bool FeatureEnabled(int aFeature, bool aDefault=true);
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -29,16 +29,17 @@
 #include "nsIXULAppInfo.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"
 #include "gfxPrefs.h"
 #include "gfxPlatform.h"
 #include "gfxConfig.h"
+#include "DriverCrashGuard.h"
 
 #if defined(MOZ_CRASHREPORTER)
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla::widget;
 using namespace mozilla;
 using mozilla::MutexAutoLock;
@@ -1332,16 +1333,45 @@ GfxInfoBase::InitFeatureObject(JSContext
     JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
     JS_SetProperty(aCx, aContainer, aName, val);
   }
 
   aOutObj.set(obj);
   return true;
 }
 
+nsresult
+GfxInfoBase::GetActiveCrashGuards(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
+{
+  JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
+  if (!array) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  aOut.setObject(*array);
+
+  DriverCrashGuard::ForEachActiveCrashGuard([&](const char* aName,
+                                                const char* aPrefName) -> void {
+    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
+    if (!obj) {
+      return;
+    }
+    if (!SetJSPropertyString(aCx, obj, "type", aName)) {
+      return;
+    }
+    if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
+      return;
+    }
+    if (!AppendJSElement(aCx, array, obj)) {
+      return;
+    }
+  });
+
+  return NS_OK;
+}
+
 GfxInfoCollectorBase::GfxInfoCollectorBase()
 {
   GfxInfoBase::AddCollector(this);
 }
 
 GfxInfoCollectorBase::~GfxInfoCollectorBase()
 {
   GfxInfoBase::RemoveCollector(this);
--- a/widget/GfxInfoBase.h
+++ b/widget/GfxInfoBase.h
@@ -52,16 +52,17 @@ public:
   NS_IMETHOD GetWebGLParameter(const nsAString & aParam, nsAString & _retval) override;
 
   NS_IMETHOD GetMonitors(JSContext* cx, JS::MutableHandleValue _retval) override;
   NS_IMETHOD GetFailures(uint32_t *failureCount, int32_t** indices, char ***failures) override;
   NS_IMETHOD_(void) LogFailure(const nsACString &failure) override;
   NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle<JS::Value>) override;
   NS_IMETHOD GetFeatureLog(JSContext*, JS::MutableHandle<JS::Value>) override;
+  NS_IMETHOD GetActiveCrashGuards(JSContext*, JS::MutableHandle<JS::Value>) override;
 
   // Initialization function. If you override this, you must call this class's
   // version of Init first.
   // We need Init to be called separately from the constructor so we can
   // register as an observer after all derived classes have been constructed
   // and we know we have a non-zero refcount.
   // Ideally, Init() would be void-return, but the rules of
   // NS_GENERIC_FACTORY_CONSTRUCTOR_INIT require it be nsresult return.
--- a/widget/nsIGfxInfo.idl
+++ b/widget/nsIGfxInfo.idl
@@ -219,10 +219,25 @@ interface nsIGfxInfo : nsISupports
   //       "status": "<status>",
   //       "version": "<version>",
   //       "warp": true|false,           // D3D11 only.
   //       "textureSharing": true|false, // D3D11 only.
   //     }
   //   }
   [implicit_jscontext]
   jsval getFeatures();
+
+  // Returns an array listing any active crash guards.
+  //
+  //   [
+  //     {
+  //       // Type is one of "d3d11layers", "d3d9video", "glcontext",
+  //       // or "d3d11video".
+  //       "type":        "<identifier>",
+  //
+  //       // Preference that must be deleted/reset to retrigger the guard.
+  //       "prefName":    "<preference>",
+  //     }
+  //   ]
+  [implicit_jscontext]
+  jsval getActiveCrashGuards();
 };