Source code

Revision control

Other Tools

/* -*- 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 <algorithm> // 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 critical displayport",
"painted displayport",
"requested displayport",
"viewport",
};
const char* CheckerboardEvent::sColors[] = {
"brown", "darkgreen", "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
mRendertraceInfo << "RENDERTRACE "
<< (aTimestamp - mOriginTime).ToMilliseconds() << " rect "
<< sColors[aProperty] << " " << aRect.X() << " " << aRect.Y()
<< " " << aRect.Width() << " " << aRect.Height() << " "
<< "// " << sDescriptions[aProperty] << aExtraInfo
<< std::endl;
}
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<PropertyValue> 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 --" << std::endl;
}
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."
<< std::endl;
}
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<PropertyValue>& 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