diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 1bef008771ad..79309ad2d37e 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2081,10 +2081,6 @@ ContentParent::ActorDestroy(ActorDestroyReason why) SEND_SHUTDOWN_MESSAGE)); } cpm->RemoveContentProcess(this->ChildID()); - - if (mDriverCrashGuard) { - mDriverCrashGuard->NotifyCrashed(); - } } void @@ -5182,42 +5178,6 @@ ContentParent::RecvGetGraphicsDeviceInitData(DeviceInitData* aOut) return true; } -bool -ContentParent::RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed) -{ - // Only one driver crash guard should be active at a time, per-process. - MOZ_ASSERT(!mDriverCrashGuard); - - UniquePtr guard; - switch (gfx::CrashGuardType(aGuardType)) { - case gfx::CrashGuardType::D3D11Layers: - guard = MakeUnique(this); - break; - case gfx::CrashGuardType::D3D9Video: - guard = MakeUnique(this); - break; - default: - MOZ_ASSERT_UNREACHABLE("unknown crash guard type"); - return false; - } - - if (guard->Crashed()) { - *aOutCrashed = true; - return true; - } - - *aOutCrashed = false; - mDriverCrashGuard = Move(guard); - return true; -} - -bool -ContentParent::RecvEndDriverCrashGuard(const uint32_t& aGuardType) -{ - mDriverCrashGuard = nullptr; - return true; -} - } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 3472462357e4..332bfd042783 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -25,7 +25,6 @@ #include "nsIDOMGeoPositionCallback.h" #include "nsIDOMGeoPositionErrorCallback.h" #include "PermissionMessageUtils.h" -#include "DriverCrashGuard.h" #define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown") @@ -815,8 +814,6 @@ private: virtual bool RecvGetGraphicsFeatureStatus(const int32_t& aFeature, int32_t* aStatus, bool* aSuccess) override; - virtual bool RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed) override; - virtual bool RecvEndDriverCrashGuard(const uint32_t& aGuardType) override; virtual bool RecvAddIdleObserver(const uint64_t& observerId, const uint32_t& aIdleTimeInS) override; @@ -957,8 +954,6 @@ private: nsRefPtr mGatherer; #endif nsCString mProfile; - - UniquePtr mDriverCrashGuard; }; } // namespace dom diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 33f8762693b1..29ab5cedda2e 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -933,10 +933,6 @@ parent: sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, bool aSuccess); - // Driver crash guards. aGuardType must be a member of CrashGuardType. - sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected); - sync EndDriverCrashGuard(uint32_t aGuardType); - AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS); RemoveIdleObserver(uint64_t observerId, uint32_t idleTimeInS); diff --git a/dom/media/Intervals.h b/dom/media/Intervals.h index 135a865d4fca..9e0ef0af34af 100644 --- a/dom/media/Intervals.h +++ b/dom/media/Intervals.h @@ -429,8 +429,8 @@ public: } T firstEnd = std::max(mIntervals[0].mStart, aInterval.mStart); T secondStart = std::min(mIntervals.LastElement().mEnd, aInterval.mEnd); - ElemType startInterval(mIntervals[0].mStart, firstEnd, aInterval.mFuzz); - ElemType endInterval(secondStart, mIntervals.LastElement().mEnd, aInterval.mFuzz); + ElemType startInterval(mIntervals[0].mStart, firstEnd); + ElemType endInterval(secondStart, mIntervals.LastElement().mEnd); SelfType intervals(Move(startInterval)); intervals += Move(endInterval); return Intersection(intervals); diff --git a/dom/media/gtest/TestIntervalSet.cpp b/dom/media/gtest/TestIntervalSet.cpp index f2198320f534..321a16a409de 100644 --- a/dom/media/gtest/TestIntervalSet.cpp +++ b/dom/media/gtest/TestIntervalSet.cpp @@ -776,4 +776,46 @@ TEST(IntervalSet, Substraction) EXPECT_EQ(1u, i0.Length()); EXPECT_EQ(5, i0[0].mStart); EXPECT_EQ(8, i0[0].mEnd); + + i0 = IntIntervals(); + i0 += IntInterval(0, 10); + IntIntervals i2; + i2 += IntInterval(4, 6); + i0 -= i2; + EXPECT_EQ(2u, i0.Length()); + EXPECT_EQ(0, i0[0].mStart); + EXPECT_EQ(4, i0[0].mEnd); + EXPECT_EQ(6, i0[1].mStart); + EXPECT_EQ(10, i0[1].mEnd); + + i0 = IntIntervals(); + i0 += IntInterval(0, 1); + i0 += IntInterval(3, 10); + EXPECT_EQ(2u, i0.Length()); + // This fuzz should collapse i0 into [0,10). + i0.SetFuzz(1); + EXPECT_EQ(1u, i0.Length()); + EXPECT_EQ(1, i0[0].mFuzz); + i2 = IntInterval(4, 6); + i0 -= i2; + EXPECT_EQ(2u, i0.Length()); + EXPECT_EQ(0, i0[0].mStart); + EXPECT_EQ(4, i0[0].mEnd); + EXPECT_EQ(6, i0[1].mStart); + EXPECT_EQ(10, i0[1].mEnd); + EXPECT_EQ(1, i0[0].mFuzz); + EXPECT_EQ(1, i0[1].mFuzz); + + i0 = IntIntervals(); + i0 += IntInterval(0, 10); + // [4,6) with fuzz 1 used to fail because the complementary interval set + // [0,4)+[6,10) would collapse into [0,10). + i2 = IntInterval(4, 6); + i2.SetFuzz(1); + i0 -= i2; + EXPECT_EQ(2u, i0.Length()); + EXPECT_EQ(0, i0[0].mStart); + EXPECT_EQ(4, i0[0].mEnd); + EXPECT_EQ(6, i0[1].mStart); + EXPECT_EQ(10, i0[1].mEnd); } diff --git a/dom/media/platforms/wmf/DXVA2Manager.cpp b/dom/media/platforms/wmf/DXVA2Manager.cpp index a9bbcfcb79f0..05abc6aa256a 100644 --- a/dom/media/platforms/wmf/DXVA2Manager.cpp +++ b/dom/media/platforms/wmf/DXVA2Manager.cpp @@ -14,7 +14,6 @@ #include "mozilla/Preferences.h" #include "mfapi.h" #include "MFTDecoder.h" -#include "DriverCrashGuard.h" const CLSID CLSID_VideoProcessorMFT = { @@ -32,7 +31,7 @@ const GUID MF_XVP_PLAYBACK_MODE = { 0xaf, 0x12, 0xcf, 0x3e, 0x23, 0x8a, 0xcc, 0xe9 } }; -DEFINE_GUID(MF_LOW_LATENCY, +DEFINE_GUID(MF_LOW_LATENCY, 0x9c27891a, 0xed7a, 0x40e1, 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee); namespace mozilla { @@ -91,12 +90,6 @@ D3D9DXVA2Manager::Init() { MOZ_ASSERT(NS_IsMainThread()); - gfx::D3D9VideoCrashGuard crashGuard; - if (crashGuard.Crashed()) { - NS_WARNING("DXVA2D3D9 crash detected"); - return E_FAIL; - } - // Create D3D9Ex. HMODULE d3d9lib = LoadLibraryW(L"d3d9.dll"); NS_ENSURE_TRUE(d3d9lib, E_FAIL); diff --git a/gfx/src/DriverCrashGuard.cpp b/gfx/src/DriverCrashGuard.cpp deleted file mode 100644 index b4dbc829e4ba..000000000000 --- a/gfx/src/DriverCrashGuard.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; 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 "DriverCrashGuard.h" -#include "gfxPrefs.h" -#include "nsAppDirectoryServiceDefs.h" -#include "nsDirectoryServiceUtils.h" -#ifdef MOZ_CRASHREPORTER -#include "nsExceptionHandler.h" -#endif -#include "nsServiceManagerUtils.h" -#include "nsString.h" -#include "nsXULAppAPI.h" -#include "mozilla/Preferences.h" -#include "mozilla/Telemetry.h" -#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] = { - "d3d11layers", - "d3d9video", -}; - -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)]); -} - -void -DriverCrashGuard::InitializeIfNeeded() -{ - if (mInitialized) { - return; - } - - mInitialized = true; - Initialize(); -} - -void -DriverCrashGuard::Initialize() -{ - 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) { - LogFeatureDisabled(); - return; - } - - ActivateGuard(); - return; - } - - // Always check whether or not the lock file exists. For example, we could - // have crashed creating a D3D9 device in the parent process, and on restart - // are now requesting one in the child process. We catch everything here. - if (RecoverFromCrash()) { - mCrashDetected = true; - return; - } - - // If the environment has changed, we always activate the guard. In the - // parent process this performs main-thread disk I/O. Child process guards - // only incur an IPC cost, so if we're proxying for a child process, we - // play it safe and activate the guard as long as we don't expect it to - // crash. - if (CheckOrRefreshEnvironment() || - (mMode == Mode::Proxy && GetStatus() != DriverInitStatus::Crashed)) - { - ActivateGuard(); - return; - } - - // If we got here and our status is "crashed", then the environment has not - // updated and we do not want to attempt to use the driver again. - if (GetStatus() == DriverInitStatus::Crashed) { - mCrashDetected = true; - LogFeatureDisabled(); - } -} - -DriverCrashGuard::~DriverCrashGuard() -{ - if (!mGuardActivated) { - return; - } - - if (XRE_IsParentProcess()) { - if (mGuardFile) { - mGuardFile->Remove(false); - } - - // If during our initialization, no other process encountered a crash, we - // proceed to mark the status as okay. - if (GetStatus() != DriverInitStatus::Crashed) { - SetStatus(DriverInitStatus::Okay); - } - } else { - dom::ContentChild::GetSingleton()->SendEndDriverCrashGuard(uint32_t(mType)); - } - -#ifdef MOZ_CRASHREPORTER - // Remove the crash report annotation. - CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"), - NS_LITERAL_CSTRING("")); -#endif -} - -bool -DriverCrashGuard::Crashed() -{ - InitializeIfNeeded(); - - // Note, we read mCrashDetected instead of GetStatus(), since in child - // processes we're not guaranteed that the prefs have been synced in - // time. - return mCrashDetected; -} - -nsCOMPtr -DriverCrashGuard::GetGuardFile() -{ - nsCString filename; - filename.Assign(sCrashGuardNames[size_t(mType)]); - filename.Append(".guard"); - - nsCOMPtr file; - NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, getter_AddRefs(file)); - if (!file) { - return nullptr; - } - if (!NS_SUCCEEDED(file->AppendNative(filename))) { - return nullptr; - } - return file; -} - -void -DriverCrashGuard::ActivateGuard() -{ - mGuardActivated = true; - -#ifdef MOZ_CRASHREPORTER - // Anotate crash reports only if we're a real guard. Otherwise, we could - // attribute a random parent process crash to a graphics problem in a child - // process. - if (mMode != Mode::Proxy) { - CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"), - NS_LITERAL_CSTRING("1")); - } -#endif - - // If we're in the content process, the rest of the guarding is handled - // in the parent. - if (XRE_IsContentProcess()) { - return; - } - - SetStatus(DriverInitStatus::Attempting); - - if (mMode != Mode::Proxy) { - // In parent process guards, we use two tombstones to detect crashes: a - // preferences and a zero-byte file on the filesystem. - FlushPreferences(); - - // Create a temporary tombstone/lockfile. - FILE* fp = nullptr; - mGuardFile = GetGuardFile(); - if (!mGuardFile || !NS_SUCCEEDED(mGuardFile->OpenANSIFileDesc("w", &fp))) { - return; - } - fclose(fp); - } -} - -void -DriverCrashGuard::NotifyCrashed() -{ - CheckOrRefreshEnvironment(); - SetStatus(DriverInitStatus::Crashed); - FlushPreferences(); - LogCrashRecovery(); -} - -bool -DriverCrashGuard::RecoverFromCrash() -{ - nsCOMPtr file = GetGuardFile(); - bool exists; - if ((file && - NS_SUCCEEDED(file->Exists(&exists)) && - exists) || - (GetStatus() == DriverInitStatus::Attempting)) - { - // If we get here, we've just recovered from a crash. Disable acceleration - // until the environment changes. - if (file) { - file->Remove(false); - } - NotifyCrashed(); - return true; - } - return false; -} - -// Return true if the caller should proceed to guard for crashes. False if -// the environment has not changed. We persist the "changed" status across -// calls, so that after an environment changes, all guards for the new -// session are activated rather than just the first. -bool -DriverCrashGuard::CheckOrRefreshEnvironment() -{ - static bool sBaseInfoChanged = false; - static bool sBaseInfoChecked = false; - - if (!sBaseInfoChecked) { - // None of the prefs we care about, so we cache the result statically. - sBaseInfoChecked = true; - sBaseInfoChanged = UpdateBaseEnvironment(); - } - - // Always update the full environment, even if the base info didn't change. - return UpdateEnvironment() || - sBaseInfoChanged || - GetStatus() == DriverInitStatus::Unknown; -} - -bool -DriverCrashGuard::UpdateBaseEnvironment() -{ - bool changed = false; - if (mGfxInfo = services::GetGfxInfo()) { - nsString value; - - // Driver properties. - mGfxInfo->GetAdapterDriverVersion(value); - changed |= CheckAndUpdatePref("driverVersion", value); - mGfxInfo->GetAdapterDeviceID(value); - changed |= CheckAndUpdatePref("deviceID", value); - } - - // Firefox properties. - changed |= CheckAndUpdatePref("appVersion", NS_LITERAL_STRING(MOZ_APP_VERSION)); - - return changed; -} - -bool -DriverCrashGuard::FeatureEnabled(int aFeature) -{ - int32_t status; - if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, &status))) { - return false; - } - return status == nsIGfxInfo::FEATURE_STATUS_OK; -} - -bool -DriverCrashGuard::CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue) -{ - std::string pref = GetFullPrefName(aPrefName); - - bool oldValue; - if (NS_SUCCEEDED(Preferences::GetBool(pref.c_str(), &oldValue)) && - oldValue == aCurrentValue) - { - return false; - } - Preferences::SetBool(pref.c_str(), aCurrentValue); - return true; -} - -bool -DriverCrashGuard::CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue) -{ - std::string pref = GetFullPrefName(aPrefName); - - nsAdoptingString oldValue = Preferences::GetString(pref.c_str()); - if (oldValue == aCurrentValue) { - return false; - } - Preferences::SetString(pref.c_str(), aCurrentValue); - return true; -} - -std::string -DriverCrashGuard::GetFullPrefName(const char* aPref) -{ - return std::string("gfx.crash-guard.") + - std::string(sCrashGuardNames[uint32_t(mType)]) + - std::string(".") + - std::string(aPref); -} - -DriverInitStatus -DriverCrashGuard::GetStatus() const -{ - return (DriverInitStatus)Preferences::GetInt(mStatusPref.get(), 0); -} - -void -DriverCrashGuard::SetStatus(DriverInitStatus aStatus) -{ - MOZ_ASSERT(XRE_IsParentProcess()); - - Preferences::SetInt(mStatusPref.get(), int32_t(aStatus)); -} - -void -DriverCrashGuard::FlushPreferences() -{ - MOZ_ASSERT(XRE_IsParentProcess()); - - if (nsIPrefService* prefService = Preferences::GetService()) { - prefService->SavePrefFile(nullptr); - } -} - -D3D11LayersCrashGuard::D3D11LayersCrashGuard(dom::ContentParent* aContentParent) - : DriverCrashGuard(CrashGuardType::D3D11Layers, aContentParent) -{ -} - -void -D3D11LayersCrashGuard::Initialize() -{ - if (!XRE_IsParentProcess()) { - // We assume the parent process already performed crash detection for - // graphics devices. - return; - } - - DriverCrashGuard::Initialize(); - - // If no telemetry states have been recorded, this will set the state to okay. - // Otherwise, it will have no effect. - RecordTelemetry(TelemetryState::Okay); -} - -bool -D3D11LayersCrashGuard::UpdateEnvironment() -{ - // None of the prefs we care about, so we cache the result statically. - static bool checked = false; - static bool changed = false; - - if (checked) { - return changed; - } - - checked = true; - - if (mGfxInfo) { - // Feature status. -#if defined(XP_WIN) - bool d2dEnabled = gfxPrefs::Direct2DForceEnabled() || - (!gfxPrefs::Direct2DDisabled() && FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT2D)); - changed |= CheckAndUpdateBoolPref("feature-d2d", d2dEnabled); - - bool d3d11Enabled = !gfxPrefs::LayersPreferD3D9(); - if (!FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS)) { - d3d11Enabled = false; - } - changed |= CheckAndUpdateBoolPref("feature-d3d11", d3d11Enabled); -#endif - } - - if (!changed) { - return false; - } - - RecordTelemetry(TelemetryState::EnvironmentChanged); - return true; -} - -void -D3D11LayersCrashGuard::LogCrashRecovery() -{ - RecordTelemetry(TelemetryState::RecoveredFromCrash); - gfxCriticalError(CriticalLog::DefaultOptions(false)) << "D3D11 layers just crashed; D3D11 will be disabled."; -} - -void -D3D11LayersCrashGuard::LogFeatureDisabled() -{ - RecordTelemetry(TelemetryState::FeatureDisabled); - gfxCriticalError(CriticalLog::DefaultOptions(false)) << "D3D11 layers disabled due to a prior crash."; -} - -void -D3D11LayersCrashGuard::RecordTelemetry(TelemetryState aState) -{ - // D3D11LayersCrashGuard is a no-op in the child process. - if (!XRE_IsParentProcess()) { - return; - } - - // Since we instantiate this class more than once, make sure we only record - // the first state (since that is really all we care about). - static bool sTelemetryStateRecorded = false; - if (sTelemetryStateRecorded) { - return; - } - - Telemetry::Accumulate(Telemetry::GRAPHICS_DRIVER_STARTUP_TEST, int32_t(aState)); - sTelemetryStateRecorded = true; -} - -D3D9VideoCrashGuard::D3D9VideoCrashGuard(dom::ContentParent* aContentParent) - : DriverCrashGuard(CrashGuardType::D3D9Video, aContentParent) -{ -} - -bool -D3D9VideoCrashGuard::UpdateEnvironment() -{ - // We don't care about any extra preferences here. - return false; -} - -void -D3D9VideoCrashGuard::LogCrashRecovery() -{ - gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DXVA2D3D9 just crashed; hardware video will be disabled."; -} - -void -D3D9VideoCrashGuard::LogFeatureDisabled() -{ - gfxCriticalError(CriticalLog::DefaultOptions(false)) << "DXVA2D3D9 video decoding is disabled due to a previous crash."; -} - -} // namespace gfx -} // namespace mozilla diff --git a/gfx/src/DriverCrashGuard.h b/gfx/src/DriverCrashGuard.h deleted file mode 100644 index 3d48c7f3912a..000000000000 --- a/gfx/src/DriverCrashGuard.h +++ /dev/null @@ -1,149 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; 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/. */ -#ifndef gfx_src_DriverCrashGuard_h__ -#define gfx_src_DriverCrashGuard_h__ - -#include "gfxCore.h" -#include "nsCOMPtr.h" -#include "nsIGfxInfo.h" -#include "nsIFile.h" -#include "nsString.h" -#include - -namespace mozilla { - -namespace dom { -class ContentParent; -} // namespace dom - -namespace gfx { - -enum class DriverInitStatus -{ - // Drivers have not been initialized yet. - Unknown, - - // We're attempting to initialize drivers. - Attempting, - - // Drivers were successfully initialized last run. - Okay, - - // We crashed during driver initialization, and have restarted. - Crashed -}; - -enum class CrashGuardType : uint32_t -{ - D3D11Layers, - D3D9Video, - 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. -// -// If a callsite has a previously encountered crash, and the environment has -// not changed since the last session, then the guard will set a status flag -// indicating that the driver should not be used. -class DriverCrashGuard -{ -public: - DriverCrashGuard(CrashGuardType aType, dom::ContentParent* aContentParent); - virtual ~DriverCrashGuard(); - - bool Crashed(); - void NotifyCrashed(); - - // These are the values reported to Telemetry (GRAPHICS_DRIVER_STARTUP_TEST). - // Values should not change; add new values to the end. - enum class TelemetryState { - Okay = 0, - EnvironmentChanged = 1, - RecoveredFromCrash = 2, - FeatureDisabled = 3 - }; - - enum class Mode { - // Normal operation. - Normal, - - // Acting as a proxy between the parent and child process. - Proxy - }; - -protected: - virtual void Initialize(); - virtual bool UpdateEnvironment() = 0; - virtual void LogCrashRecovery() = 0; - virtual void LogFeatureDisabled() = 0; - - // Helper functions. - bool FeatureEnabled(int aFeature); - bool CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue); - bool CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue); - std::string GetFullPrefName(const char* aPref); - -private: - // Either process. - void InitializeIfNeeded(); - bool CheckOrRefreshEnvironment(); - bool UpdateBaseEnvironment(); - DriverInitStatus GetStatus() const; - - // Parent process only. - nsCOMPtr GetGuardFile(); - bool RecoverFromCrash(); - void ActivateGuard(); - void FlushPreferences(); - void SetStatus(DriverInitStatus aStatus); - -private: - CrashGuardType mType; - Mode mMode; - bool mInitialized; - bool mGuardActivated; - bool mCrashDetected; - nsCOMPtr mGuardFile; - -protected: - nsCString mStatusPref; - nsCOMPtr mGfxInfo; -}; - -class D3D11LayersCrashGuard final : public DriverCrashGuard -{ - public: - explicit D3D11LayersCrashGuard(dom::ContentParent* aContentParent = nullptr); - - protected: - void Initialize() override; - bool UpdateEnvironment() override; - void LogCrashRecovery() override; - void LogFeatureDisabled() override; - - private: - void RecordTelemetry(TelemetryState aState); -}; - -class D3D9VideoCrashGuard final : public DriverCrashGuard -{ - public: - explicit D3D9VideoCrashGuard(dom::ContentParent* aContentParent = nullptr); - - protected: - bool UpdateEnvironment() override; - void LogCrashRecovery() override; - void LogFeatureDisabled() override; -}; - -} // namespace gfx -} // namespace mozilla - -#endif // gfx_src_DriverCrashGuard_h__ - diff --git a/gfx/src/DriverInitCrashDetection.cpp b/gfx/src/DriverInitCrashDetection.cpp new file mode 100644 index 000000000000..b75ff6f67223 --- /dev/null +++ b/gfx/src/DriverInitCrashDetection.cpp @@ -0,0 +1,253 @@ +/* -*- Mode: C++; tab-width: 2; 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 "DriverInitCrashDetection.h" +#include "gfxPrefs.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryServiceUtils.h" +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif +#include "nsServiceManagerUtils.h" +#include "nsString.h" +#include "nsXULAppAPI.h" +#include "mozilla/Preferences.h" +#include "mozilla/Telemetry.h" +#include "mozilla/Services.h" +#include "mozilla/gfx/Logging.h" + +namespace mozilla { +namespace gfx { + +bool DriverInitCrashDetection::sDisableAcceleration = false; +bool DriverInitCrashDetection::sEnvironmentHasBeenUpdated = false; + +DriverInitCrashDetection::DriverInitCrashDetection() + : mIsChromeProcess(XRE_GetProcessType() == GeckoProcessType_Default) +{ + if (sDisableAcceleration) { + // We already disabled acceleration earlier. + return; + } + + if (!mIsChromeProcess) { + // In child processes we only need to check the pref state set by the + // parent process. + sDisableAcceleration = (gfxPrefs::DriverInitStatus() == int32_t(DriverInitStatus::Recovered)); + return; + } + + if (!InitLockFilePath()) { + gfxCriticalError(CriticalLog::DefaultOptions(false)) << "Failed to create the graphics startup lockfile."; + return; + } + + if (RecoverFromDriverInitCrash()) { + // This is the first time we're checking for a crash recovery, so print + // a message and disable acceleration for anyone who asks for it. + gfxCriticalError(CriticalLog::DefaultOptions(false)) << "Recovered from graphics driver startup crash; acceleration disabled."; + sDisableAcceleration = true; + return; + } + + if (UpdateEnvironment() || sEnvironmentHasBeenUpdated) { + // Something in the environment changed, *or* a previous instance of this + // class already updated the environment. Allow a fresh attempt at driver + // acceleration. This doesn't mean the previous attempt failed, it just + // means we want to detect whether the new environment crashes. + AllowDriverInitAttempt(); + sEnvironmentHasBeenUpdated = true; + return; + } + + RecordTelemetry(TelemetryState::Okay); +} + +DriverInitCrashDetection::~DriverInitCrashDetection() +{ + if (mLockFile) { + mLockFile->Remove(false); + } + + if (gfxPrefs::DriverInitStatus() == int32_t(DriverInitStatus::Attempting)) { + // If we attempted to initialize the driver, and got this far without + // crashing, assume everything is okay. + gfxPrefs::SetDriverInitStatus(int32_t(DriverInitStatus::Okay)); + +#ifdef MOZ_CRASHREPORTER + // Remove the crash report annotation. + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"), + NS_LITERAL_CSTRING("")); +#endif + } +} + +bool +DriverInitCrashDetection::InitLockFilePath() +{ + NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, getter_AddRefs(mLockFile)); + if (!mLockFile) { + return false; + } + if (!NS_SUCCEEDED(mLockFile->AppendNative(NS_LITERAL_CSTRING("gfxinit.lock")))) { + return false; + } + return true; +} + +void +DriverInitCrashDetection::AllowDriverInitAttempt() +{ + // Create a temporary tombstone/lockfile. + FILE* fp; + if (!NS_SUCCEEDED(mLockFile->OpenANSIFileDesc("w", &fp))) { + return; + } + fclose(fp); + + gfxPrefs::SetDriverInitStatus(int32_t(DriverInitStatus::Attempting)); + + // Flush preferences, so if we crash, we don't think the environment has changed again. + FlushPreferences(); + + // If we crash, we'll just lose this. Not a big deal, next startup we'll + // record the failure. + RecordTelemetry(TelemetryState::EnvironmentChanged); + +#ifdef MOZ_CRASHREPORTER + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("GraphicsStartupTest"), + NS_LITERAL_CSTRING("1")); +#endif +} + +bool +DriverInitCrashDetection::RecoverFromDriverInitCrash() +{ + bool exists; + if (mLockFile && + NS_SUCCEEDED(mLockFile->Exists(&exists)) && + exists) + { + // If we get here, we've just recovered from a crash. Disable acceleration + // until the environment changes. Since we may have crashed before + // preferences we're flushed, we cache the environment again, then flush + // preferences so child processes can start right away. + gfxPrefs::SetDriverInitStatus(int32_t(DriverInitStatus::Recovered)); + UpdateEnvironment(); + FlushPreferences(); + RecordTelemetry(TelemetryState::RecoveredFromCrash); + return true; + } + if (gfxPrefs::DriverInitStatus() == int32_t(DriverInitStatus::Recovered)) { + // If we get here, we crashed in the current environment and have already + // disabled acceleration. + RecordTelemetry(TelemetryState::AccelerationDisabled); + return true; + } + return false; +} + +bool +DriverInitCrashDetection::UpdateEnvironment() +{ + mGfxInfo = services::GetGfxInfo(); + + bool changed = false; + if (mGfxInfo) { + nsString value; + + // Driver properties. + mGfxInfo->GetAdapterDriverVersion(value); + changed |= CheckAndUpdatePref("gfx.driver-init.driverVersion", value); + mGfxInfo->GetAdapterDeviceID(value); + changed |= CheckAndUpdatePref("gfx.driver-init.deviceID", value); + + // Feature status. +#if defined(XP_WIN) + bool d2dEnabled = gfxPrefs::Direct2DForceEnabled() || + (!gfxPrefs::Direct2DDisabled() && FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT2D)); + changed |= CheckAndUpdateBoolPref("gfx.driver-init.feature-d2d", d2dEnabled); + + bool d3d11Enabled = !gfxPrefs::LayersPreferD3D9(); + if (!FeatureEnabled(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS)) { + d3d11Enabled = false; + } + changed |= CheckAndUpdateBoolPref("gfx.driver-init.feature-d3d11", d3d11Enabled); +#endif + } + + // Firefox properties. + changed |= CheckAndUpdatePref("gfx.driver-init.appVersion", NS_LITERAL_STRING(MOZ_APP_VERSION)); + + // Finally, mark as changed if the status has been reset by the user. + changed |= (gfxPrefs::DriverInitStatus() == int32_t(DriverInitStatus::None)); + + mGfxInfo = nullptr; + return changed; +} + +bool +DriverInitCrashDetection::FeatureEnabled(int aFeature) +{ + int32_t status; + if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, &status))) { + return false; + } + return status == nsIGfxInfo::FEATURE_STATUS_OK; +} + +bool +DriverInitCrashDetection::CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue) +{ + bool oldValue; + if (NS_SUCCEEDED(Preferences::GetBool(aPrefName, &oldValue)) && + oldValue == aCurrentValue) + { + return false; + } + Preferences::SetBool(aPrefName, aCurrentValue); + return true; +} + +bool +DriverInitCrashDetection::CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue) +{ + nsAdoptingString oldValue = Preferences::GetString(aPrefName); + if (oldValue == aCurrentValue) { + return false; + } + Preferences::SetString(aPrefName, aCurrentValue); + return true; +} + +void +DriverInitCrashDetection::FlushPreferences() +{ + if (nsIPrefService* prefService = Preferences::GetService()) { + prefService->SavePrefFile(nullptr); + } +} + +void +DriverInitCrashDetection::RecordTelemetry(TelemetryState aState) +{ + // Since we run this in each child process, we only want the initial results + // from the chrome process. + if (XRE_GetProcessType() != GeckoProcessType_Default) { + return; + } + + // Since we instantiate this class more than once, make sure we only record + // the first state (since that is really all we care about). + static bool sTelemetryStateRecorded = false; + if (sTelemetryStateRecorded) { + return; + } + + Telemetry::Accumulate(Telemetry::GRAPHICS_DRIVER_STARTUP_TEST, int32_t(aState)); + sTelemetryStateRecorded = true; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/src/DriverInitCrashDetection.h b/gfx/src/DriverInitCrashDetection.h new file mode 100644 index 000000000000..dab79c9f7c79 --- /dev/null +++ b/gfx/src/DriverInitCrashDetection.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ +#ifndef gfx_src_DriverInitCrashDetection_h__ +#define gfx_src_DriverInitCrashDetection_h__ + +#include "gfxCore.h" +#include "nsCOMPtr.h" +#include "nsIGfxInfo.h" +#include "nsIFile.h" + +namespace mozilla { +namespace gfx { + +enum class DriverInitStatus +{ + // Drivers have not been initialized yet. + None, + + // We're attempting to initialize drivers. + Attempting, + + // Drivers were successfully initialized last run. + Okay, + + // We crashed during driver initialization, and have restarted. + Recovered +}; + +class DriverInitCrashDetection +{ +public: + DriverInitCrashDetection(); + ~DriverInitCrashDetection(); + + bool DisableAcceleration() const { + return sDisableAcceleration; + } + + // These are the values reported to Telemetry (GRAPHICS_DRIVER_STARTUP_TEST). + // Values should not change; add new values to the end. + enum class TelemetryState { + Okay = 0, + EnvironmentChanged = 1, + RecoveredFromCrash = 2, + AccelerationDisabled = 3 + }; + +private: + bool InitLockFilePath(); + bool UpdateEnvironment(); + bool CheckAndUpdatePref(const char* aPrefName, const nsAString& aCurrentValue); + bool CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue); + bool FeatureEnabled(int aFeature); + void AllowDriverInitAttempt(); + bool RecoverFromDriverInitCrash(); + void FlushPreferences(); + + void RecordTelemetry(TelemetryState aState); + +private: + static bool sDisableAcceleration; + static bool sEnvironmentHasBeenUpdated; + +private: + bool mIsChromeProcess; + nsCOMPtr mGfxInfo; + nsCOMPtr mLockFile; +}; + +} // namespace gfx +} // namespace mozilla + +#endif // gfx_src_DriverInitCrashDetection_h__ + diff --git a/gfx/src/moz.build b/gfx/src/moz.build index ea1ff8cb4cae..3d26b88016c5 100644 --- a/gfx/src/moz.build +++ b/gfx/src/moz.build @@ -14,7 +14,7 @@ XPIDL_MODULE = 'gfx' DEFINES['MOZ_APP_VERSION'] = '"%s"' % CONFIG['MOZ_APP_VERSION'] EXPORTS += [ - 'DriverCrashGuard.h', + 'DriverInitCrashDetection.h', 'FilterSupport.h', 'gfxCore.h', 'gfxCrashReporterUtils.h', @@ -54,7 +54,7 @@ if CONFIG['MOZ_X11']: ] UNIFIED_SOURCES += [ - 'DriverCrashGuard.cpp', + 'DriverInitCrashDetection.cpp', 'FilterSupport.cpp', 'gfxCrashReporterUtils.cpp', 'gfxTelemetry.cpp', @@ -77,12 +77,6 @@ SOURCES += [ FAIL_ON_WARNINGS = True -include('/ipc/chromium/chromium-config.mozbuild') - -LOCAL_INCLUDES += [ - '/dom/ipc', # for ContentChild.h -] - FINAL_LIBRARY = 'xul' CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index dde41004fd40..7b1b8f875cee 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -232,6 +232,9 @@ private: DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled", Direct2DForceEnabled, bool, false); DECL_GFX_PREF(Live, "gfx.direct2d.use1_1", Direct2DUse1_1, bool, false); DECL_GFX_PREF(Live, "gfx.draw-color-bars", CompositorDrawColorBars, bool, false); + // This should be set to values in the DriverInitStatus enumeration found in + // DriverInitCrashDetection.h. + DECL_GFX_PREF(Live, "gfx.driver-init.status", DriverInitStatus, int32_t, 0); DECL_GFX_PREF(Once, "gfx.font_rendering.directwrite.enabled", DirectWriteFontRenderingEnabled, bool, false); DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels", GrallocFenceWithReadPixels, bool, false); DECL_GFX_PREF(Live, "gfx.layerscope.enabled", LayerScopeEnabled, bool, false); diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index cf19a6b4d489..ddd226613721 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -73,7 +73,7 @@ #include "gfxPrefs.h" #include "VsyncSource.h" -#include "DriverCrashGuard.h" +#include "DriverInitCrashDetection.h" #include "mozilla/dom/ContentParent.h" using namespace mozilla; @@ -2183,8 +2183,8 @@ gfxWindowsPlatform::InitializeDevices() // If we previously crashed initializing devices, bail out now. This is // effectively a parent-process only check, since the content process // cannot create a lock file. - D3D11LayersCrashGuard detectCrashes; - if (detectCrashes.Crashed()) { + DriverInitCrashDetection detectCrashes; + if (detectCrashes.DisableAcceleration()) { mAcceleration = FeatureStatus::Blocked; return; }