/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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"gfxEnv.h" #include"gfxConfig.h" #include"nsAppDirectoryServiceDefs.h" #include"nsDirectoryServiceUtils.h" #include"nsExceptionHandler.h" #include"nsServiceManagerUtils.h" #include"nsString.h" #include"nsXULAppAPI.h" #include"mozilla/Preferences.h" #include"mozilla/StaticPrefs_gfx.h" #include"mozilla/StaticPrefs_webgl.h" #include"mozilla/Telemetry.h" #include"mozilla/Components.h" #include"mozilla/gfx/Logging.h" #include"mozilla/dom/ContentChild.h"
namespace mozilla { namespace gfx {
staticconst size_t NUM_CRASH_GUARD_TYPES = size_t(CrashGuardType::NUM_TYPES); staticconstchar* sCrashGuardNames[] = { "d3d11layers", "glcontext", "wmfvpxvideo",
};
static_assert(std::size(sCrashGuardNames) == NUM_CRASH_GUARD_TYPES, "CrashGuardType updated without a name string");
void DriverCrashGuard::InitializeIfNeeded() { if (mInitialized) { return;
}
mInitialized = true;
Initialize();
}
staticinlinebool AreCrashGuardsEnabled(CrashGuardType aType) { // Crash guard isn't supported in the GPU or RDD process since the entire // process is basically a crash guard. if (XRE_IsGPUProcess() || XRE_IsRDDProcess()) { returnfalse;
} #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. Unless the user forces is with an environment // variable, which comes in handy for testing. // We handle the WMFVPXVideo crash guard differently to the other and always // enable it as it completely breaks playback and there's no way around it. if (aType != CrashGuardType::WMFVPXVideo) { return gfxEnv::MOZ_FORCE_CRASH_GUARD_NIGHTLY();
} #endif // Check to see if all guards have been disabled through the environment. return !gfxEnv::MOZ_DISABLE_CRASH_GUARD();
}
void DriverCrashGuard::Initialize() { if (!AreCrashGuardsEnabled(mType)) { 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;
}
mGfxInfo = components::GfxInfo::Service();
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));
}
// 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;
}
// 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::RecordAnnotationBool(
CrashReporter::Annotation::GraphicsStartupTest, true);
}
// 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();
nsCOMPtr<nsIFile> 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(); returntrue;
} returnfalse;
}
// 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() { // Our result can be cached statically since we don't check live prefs. // We need to cache once per crash guard type. // The first call to CheckOrRefrechEnvironment will always return true should // the configuration had changed, following calls will return false. staticbool sBaseInfoChanged[NUM_CRASH_GUARD_TYPES]; staticbool sBaseInfoChecked[NUM_CRASH_GUARD_TYPES];
const uint32_t type = uint32_t(mType); if (!sBaseInfoChecked[type]) { // None of the prefs we care about, so we cache the result statically.
sBaseInfoChecked[type] = true;
sBaseInfoChanged[type] = UpdateBaseEnvironment();
}
// Always update the full environment, even if the base info didn't change. bool result = UpdateEnvironment() || sBaseInfoChanged[type] ||
GetStatus() == DriverInitStatus::Unknown;
sBaseInfoChanged[type] = false; return result;
}
if (nsIPrefService* prefService = Preferences::GetService()) { static_cast<Preferences*>(prefService)->SavePrefFileBlocking();
}
}
void DriverCrashGuard::ForEachActiveCrashGuard( const CrashGuardCallback& aCallback) { for (size_t i = 0; i < NUM_CRASH_GUARD_TYPES; i++) {
CrashGuardType type = static_cast<CrashGuardType>(i);
if (!AreCrashGuardsEnabled(type)) { // Even if guards look active (via prefs), they can be ignored if globally // disabled. continue;
}
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() { // Our result can be cached statically since we don't check live prefs. staticbool checked = false;
if (checked) { // We no longer need to bypass the crash guard. returnfalse;
}
void D3D11LayersCrashGuard::LogCrashRecovery() {
RecordTelemetry(TelemetryState::RecoveredFromCrash);
gfxCriticalNote << "D3D11 layers just crashed; D3D11 will be disabled.";
}
void D3D11LayersCrashGuard::LogFeatureDisabled() {
RecordTelemetry(TelemetryState::FeatureDisabled);
gfxCriticalNote << "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). staticbool sTelemetryStateRecorded = false; if (sTelemetryStateRecorded) { return;
}
void GLContextCrashGuard::Initialize() { if (XRE_IsContentProcess()) { // Disable the GL crash guard in content processes, since we're not going // to lose the entire browser and we don't want to hinder WebGL // availability. return;
}
#ifdefined(MOZ_WIDGET_ANDROID) // Disable the WebGL crash guard on Android - it doesn't use E10S, and // its drivers will essentially never change, so the crash guard could // permanently disable WebGL. return; #endif
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.