Add deadline histograms into timestats
* SF deadline histogram
* Present time jitter histogram
* app deadline histogram
Histograms are only incremented if the associated (actual - predicted)
deltas are positive and jank is reported by the timeline.
Bug: 178003475
Test: builds, boots
Change-Id: Ic348cf9f2ed8d4e30bd0b26835cc0abf22cedfb2
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index c3f3671..b93f30e 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -147,17 +147,17 @@
mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_app_buffer_stuffing
mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.displayRefreshRateBucket);
std::string sfDeadlineMissedBytes =
- histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
+ histogramToProtoByteString(globalSlice.second.displayDeadlineDeltas.hist,
mMaxPulledHistogramBuckets);
- mStatsDelegate
- ->statsEventWriteByteArray(event, (const uint8_t*)sfDeadlineMissedBytes.c_str(),
- sfDeadlineMissedBytes.size()); // sf_deadline_misses
+ mStatsDelegate->statsEventWriteByteArray(event,
+ (const uint8_t*)sfDeadlineMissedBytes.c_str(),
+ sfDeadlineMissedBytes.size());
std::string sfPredictionErrorBytes =
- histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
+ histogramToProtoByteString(globalSlice.second.displayPresentDeltas.hist,
mMaxPulledHistogramBuckets);
- mStatsDelegate
- ->statsEventWriteByteArray(event, (const uint8_t*)sfPredictionErrorBytes.c_str(),
- sfPredictionErrorBytes.size()); // sf_prediction_errors
+ mStatsDelegate->statsEventWriteByteArray(event,
+ (const uint8_t*)sfPredictionErrorBytes.c_str(),
+ sfPredictionErrorBytes.size());
mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.renderRateBucket);
mStatsDelegate->statsEventBuild(event);
}
@@ -170,7 +170,7 @@
AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) {
std::lock_guard<std::mutex> lock(mMutex);
- std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats;
+ std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats;
uint32_t numLayers = 0;
for (const auto& globalSlice : mTimeStats.stats) {
numLayers += globalSlice.second.stats.size();
@@ -178,8 +178,8 @@
dumpStats.reserve(numLayers);
- for (const auto& globalSlice : mTimeStats.stats) {
- for (const auto& layerSlice : globalSlice.second.stats) {
+ for (auto& globalSlice : mTimeStats.stats) {
+ for (auto& layerSlice : globalSlice.second.stats) {
dumpStats.push_back(&layerSlice.second);
}
}
@@ -194,7 +194,7 @@
dumpStats.resize(mMaxPulledLayers);
}
- for (const auto& layer : dumpStats) {
+ for (auto& layer : dumpStats) {
AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO);
mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str());
@@ -234,11 +234,11 @@
mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(),
frameRateVoteBytes.size()); // set_frame_rate_vote
std::string appDeadlineMissedBytes =
- histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(),
+ histogramToProtoByteString(layer->deltas["appDeadlineDeltas"].hist,
mMaxPulledHistogramBuckets);
- mStatsDelegate
- ->statsEventWriteByteArray(event, (const uint8_t*)appDeadlineMissedBytes.c_str(),
- appDeadlineMissedBytes.size()); // app_deadline_misses
+ mStatsDelegate->statsEventWriteByteArray(event,
+ (const uint8_t*)appDeadlineMissedBytes.c_str(),
+ appDeadlineMissedBytes.size());
mStatsDelegate->statsEventBuild(event);
}
@@ -772,13 +772,14 @@
flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate);
}
+static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+ JankType::DisplayHAL;
+
template <class T>
static void updateJankPayload(T& t, int32_t reasons) {
t.jankPayload.totalFrames++;
- static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed |
- JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
- JankType::DisplayHAL;
if (reasons & kValidJankyReason) {
t.jankPayload.totalJankyFrames++;
if ((reasons & JankType::SurfaceFlingerCpuDeadlineMissed) != 0) {
@@ -796,8 +797,7 @@
}
}
-void TimeStats::incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
- const std::string& layerName, int32_t reasons) {
+void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -816,9 +816,11 @@
static const std::string kDefaultLayerName = "none";
- const int32_t refreshRateBucket = clampToSmallestBucket(refreshRate, REFRESH_RATE_BUCKET_WIDTH);
+ const int32_t refreshRateBucket =
+ clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
const int32_t renderRateBucket =
- clampToSmallestBucket(renderRate ? *renderRate : refreshRate, RENDER_RATE_BUCKET_WIDTH);
+ clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate,
+ RENDER_RATE_BUCKET_WIDTH);
const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket};
if (!mTimeStats.stats.count(timelineKey)) {
@@ -827,19 +829,29 @@
TimeStatsHelper::TimelineStats& timelineStats = mTimeStats.stats[timelineKey];
- updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, reasons);
+ updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, info.reasons);
- TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName};
+ TimeStatsHelper::LayerStatsKey layerKey = {info.uid, info.layerName};
if (!timelineStats.stats.count(layerKey)) {
- layerKey = {uid, kDefaultLayerName};
+ layerKey = {info.uid, kDefaultLayerName};
timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket;
timelineStats.stats[layerKey].renderRateBucket = renderRateBucket;
- timelineStats.stats[layerKey].uid = uid;
+ timelineStats.stats[layerKey].uid = info.uid;
timelineStats.stats[layerKey].layerName = kDefaultLayerName;
}
TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey];
- updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons);
+ updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, info.reasons);
+
+ if (info.reasons & kValidJankyReason) {
+ // TimeStats Histograms only retain positive values, so we don't need to check if these
+ // deadlines were really missed if we know that the frame had jank, since deadlines
+ // that were met will be dropped.
+ timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta));
+ timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter));
+ timeStatsLayer.deltas["appDeadlineDeltas"].insert(
+ static_cast<int32_t>(info.appDeadlineDelta));
+ }
}
void TimeStats::onDestroy(int32_t layerId) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index e76849f..fd112b9 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -121,10 +121,48 @@
// from FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there
// are no jank reasons, then total frames are incremented but jank is not, for accurate
// accounting of janky frames.
- // When these frame counts are incremented, these are also aggregated into a global reporting
- // packet to help with data validation and assessing of overall device health.
- virtual void incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
- const std::string& layerName, int32_t reasons) = 0;
+ // displayDeadlineDelta, displayPresentJitter, and appDeadlineDelta are also provided in order
+ // to provide contextual information about a janky frame. These values may only be uploaded if
+ // there was an associated valid jank reason, and they must be positive. When these frame counts
+ // are incremented, these are also aggregated into a global reporting packet to help with data
+ // validation and assessing of overall device health.
+ struct JankyFramesInfo {
+ Fps refreshRate;
+ std::optional<Fps> renderRate;
+ uid_t uid = 0;
+ std::string layerName;
+ int32_t reasons = 0;
+ nsecs_t displayDeadlineDelta = 0;
+ nsecs_t displayPresentJitter = 0;
+ nsecs_t appDeadlineDelta = 0;
+
+ bool operator==(const JankyFramesInfo& o) const {
+ return Fps::EqualsInBuckets{}(refreshRate, o.refreshRate) &&
+ ((renderRate == std::nullopt && o.renderRate == std::nullopt) ||
+ (renderRate != std::nullopt && o.renderRate != std::nullopt &&
+ Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) &&
+ uid == o.uid && layerName == o.layerName && reasons == o.reasons &&
+ displayDeadlineDelta == o.displayDeadlineDelta &&
+ displayPresentJitter == o.displayPresentJitter &&
+ appDeadlineDelta == o.appDeadlineDelta;
+ }
+
+ friend std::ostream& operator<<(std::ostream& os, const JankyFramesInfo& info) {
+ os << "JankyFramesInfo {";
+ os << "\n .refreshRate = " << info.refreshRate;
+ os << "\n .renderRate = "
+ << (info.renderRate ? to_string(*info.renderRate) : "nullopt");
+ os << "\n .uid = " << info.uid;
+ os << "\n .layerName = " << info.layerName;
+ os << "\n .reasons = " << info.reasons;
+ os << "\n .displayDeadlineDelta = " << info.displayDeadlineDelta;
+ os << "\n .displayPresentJitter = " << info.displayPresentJitter;
+ os << "\n .appDeadlineDelta = " << info.appDeadlineDelta;
+ return os << "\n}";
+ }
+ };
+
+ virtual void incrementJankyFrames(const JankyFramesInfo& info) = 0;
// Clean up the layer record
virtual void onDestroy(int32_t layerId) = 0;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -273,8 +311,8 @@
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
std::optional<Fps> renderRate) override;
- void incrementJankyFrames(Fps refreshRate, std::optional<Fps> renderRate, uid_t uid,
- const std::string& layerName, int32_t reasons) override;
+
+ void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
void onDestroy(int32_t layerId) override;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index df7be1f..814f046 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -41,7 +41,7 @@
if (delta < 0) return;
// std::lower_bound won't work on out of range values
if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) {
- hist[histogramConfig[HISTOGRAM_SIZE - 1]] += delta / histogramConfig[HISTOGRAM_SIZE - 1];
+ hist[histogramConfig[HISTOGRAM_SIZE - 1]]++;
return;
}
auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta);
@@ -154,6 +154,10 @@
ele.second.key.displayRefreshRateBucket);
StringAppendF(&result, "renderRate = %d fps\n", ele.second.key.renderRateBucket);
result.append(ele.second.jankPayload.toString());
+ StringAppendF(&result, "sfDeadlineMisses histogram is as below:\n");
+ result.append(ele.second.displayDeadlineDeltas.toString());
+ StringAppendF(&result, "sfPredictionErrors histogram is as below:\n");
+ result.append(ele.second.displayPresentDeltas.toString());
}
const auto dumpStats = generateDumpStats(maxLayers);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 0144abc0..38ee888 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -121,6 +121,8 @@
struct TimelineStats {
TimelineStatsKey key;
JankPayload jankPayload;
+ Histogram displayDeadlineDeltas;
+ Histogram displayPresentDeltas;
std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats;
};