From d8d778d935fa76bbb9678d85dfd5431aeb57e0b0 Mon Sep 17 00:00:00 2001 From: Milan Sreckovic Date: Mon, 17 Nov 2014 17:16:55 -0500 Subject: [PATCH] Bug 1074952 - Part 4. For the critical log, save the first, then circular buffer the rest into the crash annotations. r=bas --- gfx/thebes/gfxPlatform.cpp | 112 +++++++++++++++++++++++++++++++++---- gfx/thebes/gfxPrefs.h | 2 + 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index a542568d476..0202e1aa5b5 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -11,6 +11,7 @@ #include "mozilla/layers/ISurfaceAllocator.h" // for GfxMemoryImageReporter #include "prlog.h" +#include "prprf.h" #include "gfxPlatform.h" #include "gfxPrefs.h" @@ -152,22 +153,103 @@ public: NS_DECL_NSIOBSERVER }; +/// This override of the LogForwarder, initially used for the critical graphics +/// errors, is sending the log to the crash annotations as well, but only +/// if the capacity set with the method below is >= 2. We always retain the +/// very first critical message, and the latest capacity-1 messages are +/// rotated through. Note that we don't expect the total number of times +/// this gets called to be large - it is meant for critical errors only. + class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder { public: - virtual void Log(const std::string& aString) MOZ_OVERRIDE { - if (!NS_IsMainThread()) { - return; - } -#ifdef MOZ_CRASHREPORTER - nsCString reportString(aString.c_str()); - CrashReporter::AppendAppNotesToCrashReport(reportString); -#else - printf("GFX ERROR: %s", aString.c_str()); -#endif - } + CrashStatsLogForwarder(const char* aKey); + virtual void Log(const std::string& aString) MOZ_OVERRIDE; + + void SetCircularBufferSize(uint32_t aCapacity); + +private: + // Helpers for the Log() + bool UpdateStringsVector(const std::string& aString); + void UpdateCrashReport(); + +private: + std::vector > mBuffer; + nsCString mCrashCriticalKey; + uint32_t mMaxCapacity; + int32_t mIndex; }; +CrashStatsLogForwarder::CrashStatsLogForwarder(const char* aKey) + : mBuffer() + , mCrashCriticalKey(aKey) + , mMaxCapacity(0) + , mIndex(-1) +{ +} + +void CrashStatsLogForwarder::SetCircularBufferSize(uint32_t aCapacity) +{ + mMaxCapacity = aCapacity; + mBuffer.reserve(static_cast(aCapacity)); +} + +bool +CrashStatsLogForwarder::UpdateStringsVector(const std::string& aString) +{ + // We want at least the first one and the last one. Otherwise, no point. + if (mMaxCapacity < 2) { + return false; + } + + mIndex += 1; + MOZ_ASSERT(mIndex >= 0); + + // index will count 0, 1, 2, ..., max-1, 1, 2, ..., max-1, 1, 2, ... + int32_t index = mIndex ? (mIndex-1) % (mMaxCapacity-1) + 1 : 0; + MOZ_ASSERT(index >= 0 && index < (int32_t)mMaxCapacity); + MOZ_ASSERT(index <= mIndex && index <= (int32_t)mBuffer.size()); + + // Checking for index >= mBuffer.size(), rather than index == mBuffer.size() + // just out of paranoia, but we know index <= mBuffer.size(). + std::pair newEntry(mIndex,aString); + if (index >= static_cast(mBuffer.size())) { + mBuffer.push_back(newEntry); + } else { + mBuffer[index] = newEntry; + } + return true; +} + +void CrashStatsLogForwarder::UpdateCrashReport() +{ + std::stringstream message; + for(std::vector >::iterator it = mBuffer.begin(); it != mBuffer.end(); ++it) { + message << "|[" << (*it).first << "]" << (*it).second; + } + +#ifdef MOZ_CRASHREPORTER + if (!NS_IsMainThread()) { + return; + } + nsCString reportString(message.str().c_str()); + nsresult annotated = CrashReporter::AnnotateCrashReport(mCrashCriticalKey, reportString); +#else + nsresult annotated = NS_ERROR_NOT_IMPLEMENTED; +#endif + if (annotated != NS_OK) { + printf("Crash Annotation %s: %s", + mCrashCriticalKey.get(), message.str().c_str()); + } +} + +void CrashStatsLogForwarder::Log(const std::string& aString) +{ + if (UpdateStringsVector(aString)) { + UpdateCrashReport(); + } +} + NS_IMPL_ISUPPORTS(SRGBOverrideObserver, nsIObserver, nsISupportsWeakReference) #define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled" @@ -358,11 +440,14 @@ gfxPlatform::Init() } gEverInitialized = true; - mozilla::gfx::Factory::SetLogForwarder(new CrashStatsLogForwarder); + CrashStatsLogForwarder* logForwarder = new CrashStatsLogForwarder("GraphicsCriticalError"); + mozilla::gfx::Factory::SetLogForwarder(logForwarder); // Initialize the preferences by creating the singleton. gfxPrefs::GetSingleton(); + logForwarder->SetCircularBufferSize(gfxPrefs::GfxLoggingCrashLength()); + gGfxPlatformPrefsLock = new Mutex("gfxPlatform::gGfxPlatformPrefsLock"); /* Initialize the GfxInfo service. @@ -530,6 +615,9 @@ gfxPlatform::Shutdown() mozilla::gl::GLContextProviderEGL::Shutdown(); #endif + // This is a bit iffy - we're assuming that we were the ones that set the + // log forwarder in the Factory, so that it's our responsibility to + // delete it. delete mozilla::gfx::Factory::GetLogForwarder(); mozilla::gfx::Factory::SetLogForwarder(nullptr); diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index d6e86235106..cce19600b32 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -209,6 +209,8 @@ private: DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels", GrallocFenceWithReadPixels, bool, false); DECL_GFX_PREF(Live, "gfx.layerscope.enabled", LayerScopeEnabled, bool, false); DECL_GFX_PREF(Live, "gfx.layerscope.port", LayerScopePort, int32_t, 23456); + // Note that "gfx.logging.level" is defined in Logging.h + DECL_GFX_PREF(Once, "gfx.logging.crash.length", GfxLoggingCrashLength, uint32_t, 6); DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled", PerfWarnings, bool, false); DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs", WorkAroundDriverBugs, bool, true);