/* -*- 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 "CheckerboardEvent.h" #include "mozilla/Logging.h" #include // for std::sort static mozilla::LazyLogModule sApzCheckLog("apz.checkerboard"); namespace mozilla { namespace layers { // Relatively arbitrary limit to prevent a perma-checkerboard event from // eating up gobs of memory. Ideally we shouldn't have perma-checkerboarding // but better to guard against it. #define LOG_LENGTH_LIMIT (50 * 1024) const char* CheckerboardEvent::sDescriptions[] = { "page", "painted displayport", "requested displayport", "viewport", }; const char* CheckerboardEvent::sColors[] = { "brown", "lightgreen", "yellow", "red", }; CheckerboardEvent::CheckerboardEvent(bool aRecordTrace) : mRecordTrace(aRecordTrace), mOriginTime(TimeStamp::Now()), mCheckerboardingActive(false), mLastSampleTime(mOriginTime), mFrameCount(0), mTotalPixelMs(0), mPeakPixels(0), mRendertraceLock("Rendertrace") {} uint32_t CheckerboardEvent::GetSeverity() { // Scale the total into a 32-bit value return (uint32_t)sqrt((double)mTotalPixelMs); } uint32_t CheckerboardEvent::GetPeak() { return mPeakPixels; } TimeDuration CheckerboardEvent::GetDuration() { return mEndTime - mStartTime; } std::string CheckerboardEvent::GetLog() { MonitorAutoLock lock(mRendertraceLock); return mRendertraceInfo.str(); } bool CheckerboardEvent::IsRecordingTrace() { return mRecordTrace; } void CheckerboardEvent::UpdateRendertraceProperty( RendertraceProperty aProperty, const CSSRect& aRect, const std::string& aExtraInfo) { if (!mRecordTrace) { return; } MonitorAutoLock lock(mRendertraceLock); if (!mCheckerboardingActive) { mBufferedProperties[aProperty].Update(aProperty, aRect, aExtraInfo, lock); } else { LogInfo(aProperty, TimeStamp::Now(), aRect, aExtraInfo, lock); } } void CheckerboardEvent::LogInfo(RendertraceProperty aProperty, const TimeStamp& aTimestamp, const CSSRect& aRect, const std::string& aExtraInfo, const MonitorAutoLock& aProofOfLock) { MOZ_ASSERT(mRecordTrace); if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) { // The log is already long enough, don't put more things into it. We'll // append a truncation message when this event ends. return; } // The log is consumed by the page at about:checkerboard. The format is not // formally specced, but an informal description can be found at // https://searchfox.org/mozilla-central/rev/d866b96d74ec2a63f09ee418f048d23f4fd379a2/toolkit/components/aboutcheckerboard/content/aboutCheckerboard.js#86 mRendertraceInfo << "RENDERTRACE " << (aTimestamp - mOriginTime).ToMilliseconds() << " rect " << sColors[aProperty] << " " << aRect.X() << " " << aRect.Y() << " " << aRect.Width() << " " << aRect.Height() << " " << "// " << sDescriptions[aProperty] << aExtraInfo << '\n'; } bool CheckerboardEvent::RecordFrameInfo(uint32_t aCssPixelsCheckerboarded) { TimeStamp sampleTime = TimeStamp::Now(); bool eventEnding = false; if (aCssPixelsCheckerboarded > 0) { if (!mCheckerboardingActive) { StartEvent(); } MOZ_ASSERT(mCheckerboardingActive); MOZ_ASSERT(sampleTime >= mLastSampleTime); mTotalPixelMs += (uint64_t)((sampleTime - mLastSampleTime).ToMilliseconds() * aCssPixelsCheckerboarded); if (aCssPixelsCheckerboarded > mPeakPixels) { mPeakPixels = aCssPixelsCheckerboarded; } mFrameCount++; } else { if (mCheckerboardingActive) { StopEvent(); eventEnding = true; } MOZ_ASSERT(!mCheckerboardingActive); } mLastSampleTime = sampleTime; return eventEnding; } void CheckerboardEvent::StartEvent() { MOZ_LOG(sApzCheckLog, LogLevel::Debug, ("Starting checkerboard event")); MOZ_ASSERT(!mCheckerboardingActive); mCheckerboardingActive = true; mStartTime = TimeStamp::Now(); if (!mRecordTrace) { return; } MonitorAutoLock lock(mRendertraceLock); std::vector history; for (PropertyBuffer& bufferedProperty : mBufferedProperties) { bufferedProperty.Flush(history, lock); } std::sort(history.begin(), history.end()); for (const PropertyValue& p : history) { LogInfo(p.mProperty, p.mTimeStamp, p.mRect, p.mExtraInfo, lock); } mRendertraceInfo << " -- checkerboarding starts below --\n"; } void CheckerboardEvent::StopEvent() { MOZ_LOG(sApzCheckLog, LogLevel::Debug, ("Stopping checkerboard event")); mCheckerboardingActive = false; mEndTime = TimeStamp::Now(); if (!mRecordTrace) { return; } MonitorAutoLock lock(mRendertraceLock); if (mRendertraceInfo.tellp() >= LOG_LENGTH_LIMIT) { mRendertraceInfo << "[logging aborted due to length limitations]\n"; } mRendertraceInfo << "Checkerboarded for " << mFrameCount << " frames (" << (mEndTime - mStartTime).ToMilliseconds() << " ms), " << mPeakPixels << " peak, " << GetSeverity() << " severity." << '\n'; } bool CheckerboardEvent::PropertyValue::operator<( const PropertyValue& aOther) const { if (mTimeStamp < aOther.mTimeStamp) { return true; } else if (mTimeStamp > aOther.mTimeStamp) { return false; } return mProperty < aOther.mProperty; } CheckerboardEvent::PropertyBuffer::PropertyBuffer() : mIndex(0) {} void CheckerboardEvent::PropertyBuffer::Update( RendertraceProperty aProperty, const CSSRect& aRect, const std::string& aExtraInfo, const MonitorAutoLock& aProofOfLock) { mValues[mIndex] = {aProperty, TimeStamp::Now(), aRect, aExtraInfo}; mIndex = (mIndex + 1) % BUFFER_SIZE; } void CheckerboardEvent::PropertyBuffer::Flush( std::vector& aOut, const MonitorAutoLock& aProofOfLock) { for (uint32_t i = 0; i < BUFFER_SIZE; i++) { uint32_t ix = (mIndex + i) % BUFFER_SIZE; if (!mValues[ix].mTimeStamp.IsNull()) { aOut.push_back(mValues[ix]); mValues[ix].mTimeStamp = TimeStamp(); } } } } // namespace layers } // namespace mozilla