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 296708 1bb479eb334cf745759e76090b7e4817110168f3
parent 296707 a07fa49c4b0f7e92b1a832655414fe4581479e82
child 296709 8c0d3d862f1410236d7abbbe388a4a1d5868fcc9
push id19157
push usercbook@mozilla.com
push dateTue, 10 May 2016 10:16:09 +0000
treeherderfx-team@8b4e5ab50377 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmilan
bugs1270894
milestone49.0a1
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();
 };