Bug 1213007 - Part 1. Implementing gfxCrash. r=dvander

This commit is contained in:
Milan Sreckovic 2015-10-14 08:24:00 +02:00
parent 8c01d793e6
commit 3450369434
4 changed files with 115 additions and 11 deletions

View File

@ -906,5 +906,13 @@ CriticalLogger::OutputMessage(const std::string &aString,
BasicLogger::OutputMessage(aString, aLevel, aNoNewline);
}
void
CriticalLogger::CrashAction(LogReason aReason)
{
if (Factory::GetLogForwarder()) {
Factory::GetLogForwarder()->CrashAction(aReason);
}
}
} // namespace gfx
} // namespace mozilla

View File

@ -127,6 +127,17 @@ private:
/// is further controlled by "gfx2d" PR logging module. However, in the case
/// where such module would disable the output, in all but gfxDebug cases,
/// we will still send a printf.
// The range is due to the values set in Histograms.json
enum class LogReason : int {
MustBeMoreThanThis = -1,
// Start. Do not insert, always add at end. If you remove items,
// make sure the other items retain their values.
// End
MustBeLessThanThis = 101,
};
struct BasicLogger
{
// For efficiency, this method exists and copies the logic of the
@ -151,6 +162,9 @@ struct BasicLogger
return false;
}
// Only for really critical errors.
static void CrashAction(LogReason aReason) {}
static void OutputMessage(const std::string &aString,
int aLevel,
bool aNoNewline) {
@ -183,6 +197,7 @@ struct BasicLogger
struct CriticalLogger {
static void OutputMessage(const std::string &aString, int aLevel, bool aNoNewline);
static void CrashAction(LogReason aReason);
};
// Implement this interface and init the Factory with an instance to
@ -191,6 +206,7 @@ class LogForwarder {
public:
virtual ~LogForwarder() {}
virtual void Log(const std::string &aString) = 0;
virtual void CrashAction(LogReason aReason) = 0;
// Provide a copy of the logs to the caller. The int is the index
// of the Log call, if the number of logs exceeds some preset capacity
@ -215,7 +231,8 @@ public:
enum class LogOptions : int {
NoNewline = 0x01,
AutoPrefix = 0x02,
AssertOnCall = 0x04
AssertOnCall = 0x04,
CrashAction = 0x08,
};
template<typename T>
@ -241,8 +258,9 @@ public:
// Logger::ShouldOutputMessage. Since we currently don't have a different
// version of that method for different loggers, this is OK. Once we do,
// change BasicLogger::ShouldOutputMessage to Logger::ShouldOutputMessage.
explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL)) {
Init(aOptions, BasicLogger::ShouldOutputMessage(L));
explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL),
LogReason aReason = LogReason::MustBeMoreThanThis) {
Init(aOptions, BasicLogger::ShouldOutputMessage(L), aReason);
}
~Log() {
@ -451,22 +469,30 @@ public:
inline bool LogIt() const { return mLogIt; }
inline bool NoNewline() const { return mOptions & int(LogOptions::NoNewline); }
inline bool AutoPrefix() const { return mOptions & int(LogOptions::AutoPrefix); }
inline bool ValidReason() const { return (int)mReason > (int)LogReason::MustBeMoreThanThis && (int)mReason < (int)LogReason::MustBeLessThanThis; }
// We do not want this version to do any work, and stringstream can't be
// copied anyway. It does come in handy for the "Once" macro defined below.
MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false); }
MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false, log.mReason); }
private:
// Initialization common to two constructors
void Init(int aOptions, bool aLogIt) {
void Init(int aOptions, bool aLogIt, LogReason aReason) {
mOptions = aOptions;
mReason = aReason;
mLogIt = aLogIt;
if (mLogIt && AutoPrefix()) {
if (mOptions & int(LogOptions::AssertOnCall)) {
mMessage << "[GFX" << L << "]: ";
} else {
mMessage << "[GFX" << L << "-]: ";
if (mLogIt) {
if (AutoPrefix()) {
if (mOptions & int(LogOptions::AssertOnCall)) {
mMessage << "[GFX" << L;
} else {
mMessage << "[GFX" << L << "-";
}
}
if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
mMessage << " " << (int)mReason;
}
mMessage << "]: ";
}
}
@ -476,11 +502,15 @@ private:
if (mOptions & int(LogOptions::AssertOnCall)) {
MOZ_ASSERT(false, "An assert from the graphics logger");
}
if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) {
Logger::CrashAction(mReason);
}
}
}
std::stringstream mMessage;
int mOptions;
LogReason mReason;
bool mLogIt;
};
@ -531,6 +561,14 @@ typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog;
#define gfxWarningOnce if (1) ; else mozilla::gfx::NoLog
#endif
// In the debug build, this is equivalent to the default gfxCriticalError.
// In the non-debug build, on nightly and dev edition, it will MOZ_CRASH.
// On beta and release versions, it will telemetry count, but proceed.
//
// You should create a (new) enum in the LogReason and use it for the reason
// parameter to ensure uniqueness.
#define gfxCrash(reason) gfxCriticalError(int(LogOptions::AutoPrefix) | int(LogOptions::AssertOnCall) | int(LogOptions::CrashAction), (reason))
// See nsDebug.h and the NS_WARN_IF macro
#ifdef __cplusplus

View File

@ -9,6 +9,7 @@
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/SharedBufferManagerChild.h"
#include "mozilla/layers/ISurfaceAllocator.h" // for GfxMemoryImageReporter
#include "mozilla/Telemetry.h"
#include "mozilla/Logging.h"
#include "mozilla/Services.h"
@ -185,6 +186,7 @@ class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder
public:
explicit CrashStatsLogForwarder(const char* aKey);
virtual void Log(const std::string& aString) override;
virtual void CrashAction(LogReason aReason) override;
virtual std::vector<std::pair<int32_t,std::string> > StringsVectorCopy() override;
@ -282,6 +284,55 @@ void CrashStatsLogForwarder::Log(const std::string& aString)
}
}
class CrashTelemetryEvent : public nsRunnable
{
virtual ~CrashTelemetryEvent() {}
NS_DECL_ISUPPORTS_INHERITED
explicit CrashTelemetryEvent(uint32_t aReason) : mReason(aReason) {}
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread());
Telemetry::Accumulate(Telemetry::GFX_CRASH, mReason);
return NS_OK;
}
protected:
uint32_t mReason;
};
NS_IMPL_ISUPPORTS_INHERITED0(CrashTelemetryEvent, nsRunnable);
void
CrashStatsLogForwarder::CrashAction(LogReason aReason)
{
#ifndef RELEASE_BUILD
// Non-release builds crash by default, but will use telemetry
// if this environment variable is present.
static bool useTelemetry = getenv("MOZ_GFX_CRASH_TELEMETRY") != 0;
#else
// Release builds use telemetry bu default, but will crash
// if this environment variable is present. Double negative
// to make the intent clear.
static bool useTelemetry = !(getenv("MOZ_GFX_CRASH_MOZ_CRASH") != 0);
#endif
if (useTelemetry) {
// The callers need to assure that aReason is in the range
// that the telemetry call below supports.
if (NS_IsMainThread()) {
Telemetry::Accumulate(Telemetry::GFX_CRASH, (uint32_t)aReason);
} else {
nsCOMPtr<nsIRunnable> r1 = new CrashTelemetryEvent((uint32_t)aReason);
NS_DispatchToMainThread(r1);
}
} else {
// ignoring aReason, we can get the information we need from the stack
MOZ_CRASH("GFX_CRASH");
}
}
NS_IMPL_ISUPPORTS(SRGBOverrideObserver, nsIObserver, nsISupportsWeakReference)
#define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"
@ -755,7 +806,7 @@ gfxPlatform::CreateDrawTargetForUpdateSurface(gfxASurface *aSurface, const IntSi
return Factory::CreateDrawTargetForCairoCGContext(static_cast<gfxQuartzSurface*>(aSurface)->GetCGContext(), aSize);
}
#endif
MOZ_CRASH();
MOZ_CRASH("unused function");
return nullptr;
}

View File

@ -9851,6 +9851,13 @@
"kind": "count",
"description": "Number of times the D3D11 compositor failed to get a texture sync handle."
},
"GFX_CRASH": {
"expires_in_version": "never",
"kind": "enumerated",
"n_values": 100,
"releaseChannelCollection": "opt-out",
"description": "Graphics Crash Reason (...)"
},
"PLUGIN_ACTIVATION_COUNT": {
"alert_emails": ["cpeterson@mozilla.com"],
"expires_in_version": "48",