Frame Timeline Perfetto producer
This change adds a Perfetto Producer inside FrameTimeline that emits
DisplayFrame and SurfaceFrame as TracePackets as per the proto defined
in frame_timeline_event.proto
This change also adds a MockFrameTimeline to be used in unittests as a
part of TestableSurfaceFlinger. Due to perfetto capability, running
production FrameTimeline in TestableSurfaceFlinger has caused issues and
its best to isolate them.
Bug: 170914689
Test: libsurfaceflinger_unittest (whole test suite without filter)
Change-Id: Iabd5521629d16ded6ba3f165229caa74b1cb8eb7
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index bd87482..ef5f5ad 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -29,6 +29,7 @@
namespace android::frametimeline::impl {
using base::StringAppendF;
+using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
@@ -143,6 +144,32 @@
});
}
+FrameTimelineEvent::PresentType presentTypeToProto(int32_t jankMetadata) {
+ if (jankMetadata & EarlyPresent) {
+ return FrameTimelineEvent::PRESENT_EARLY;
+ }
+ if (jankMetadata & LatePresent) {
+ return FrameTimelineEvent::PRESENT_LATE;
+ }
+ return FrameTimelineEvent::PRESENT_ON_TIME;
+}
+
+FrameTimelineEvent::JankType JankTypeToProto(TimeStats::JankType jankType) {
+ switch (jankType) {
+ case TimeStats::None:
+ return FrameTimelineEvent::JANK_NONE;
+ case TimeStats::Display:
+ return FrameTimelineEvent::JANK_DISPLAY_HAL;
+ case TimeStats::SurfaceFlingerDeadlineMissed:
+ return FrameTimelineEvent::JANK_SF_DEADLINE_MISSED;
+ case TimeStats::AppDeadlineMissed:
+ case TimeStats::PredictionExpired:
+ return FrameTimelineEvent::JANK_APP_DEADLINE_MISSED;
+ default:
+ return FrameTimelineEvent::JANK_UNKNOWN;
+ }
+}
+
int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mMutex);
@@ -177,10 +204,11 @@
}
}
-SurfaceFrame::SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName,
+SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName,
std::string debugName, PredictionState predictionState,
frametimeline::TimelineItem&& predictions)
- : mOwnerPid(ownerPid),
+ : mToken(token),
+ mOwnerPid(ownerPid),
mOwnerUid(ownerUid),
mLayerName(std::move(layerName)),
mDebugName(std::move(debugName)),
@@ -291,17 +319,70 @@
dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
}
+void SurfaceFrame::traceSurfaceFrame(int64_t displayFrameToken) {
+ using FrameTimelineDataSource = FrameTimeline::FrameTimelineDataSource;
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) {
+ ALOGD("Cannot trace SurfaceFrame - %s with invalid token", mLayerName.c_str());
+ return;
+ } else if (displayFrameToken == ISurfaceComposer::INVALID_VSYNC_ID) {
+ ALOGD("Cannot trace SurfaceFrame - %s with invalid displayFrameToken",
+ mLayerName.c_str());
+ return;
+ }
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* surfaceFrameEvent = event->set_surface_frame();
+
+ surfaceFrameEvent->set_token(mToken);
+ surfaceFrameEvent->set_display_frame_token(displayFrameToken);
+
+ if (mPresentState == PresentState::Dropped) {
+ surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED);
+ } else if (mPresentState == PresentState::Unknown) {
+ surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED);
+ } else {
+ surfaceFrameEvent->set_present_type(presentTypeToProto(mJankMetadata));
+ }
+ surfaceFrameEvent->set_on_time_finish(!(mJankMetadata & LateFinish));
+ surfaceFrameEvent->set_gpu_composition(mJankMetadata & GpuComposition);
+ surfaceFrameEvent->set_jank_type(JankTypeToProto(mJankType));
+
+ surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime);
+ surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime);
+
+ surfaceFrameEvent->set_actual_start_ns(mActuals.startTime);
+ surfaceFrameEvent->set_actual_end_ns(mActuals.endTime);
+
+ surfaceFrameEvent->set_layer_name(mDebugName);
+ surfaceFrameEvent->set_pid(mOwnerPid);
+ });
+}
+
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
: mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
mMaxDisplayFrames(kDefaultMaxDisplayFrames),
mTimeStats(std::move(timeStats)) {}
+void FrameTimeline::onBootFinished() {
+ perfetto::TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ perfetto::Tracing::Initialize(args);
+ registerDataSource();
+}
+
+void FrameTimeline::registerDataSource() {
+ perfetto::DataSourceDescriptor dsd;
+ dsd.set_name(kFrameTimelineDataSource);
+ FrameTimelineDataSource::Register(dsd);
+}
+
FrameTimeline::DisplayFrame::DisplayFrame()
- : surfaceFlingerPredictions(TimelineItem()),
- surfaceFlingerActuals(TimelineItem()),
- predictionState(PredictionState::None),
- jankType(TimeStats::JankType::None),
- jankMetadata(0) {
+ : surfaceFlingerPredictions(TimelineItem()), surfaceFlingerActuals(TimelineItem()) {
this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
}
@@ -310,17 +391,19 @@
std::optional<int64_t> token) {
ATRACE_CALL();
if (!token) {
- return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+ return std::make_unique<impl::SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid,
+ ownerUid, std::move(layerName),
std::move(debugName), PredictionState::None,
TimelineItem());
}
std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
if (predictions) {
- return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
- std::move(debugName), PredictionState::Valid,
+ return std::make_unique<impl::SurfaceFrame>(*token, ownerPid, ownerUid,
+ std::move(layerName), std::move(debugName),
+ PredictionState::Valid,
std::move(*predictions));
}
- return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+ return std::make_unique<impl::SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName),
std::move(debugName), PredictionState::Expired,
TimelineItem());
}
@@ -340,6 +423,7 @@
ATRACE_CALL();
const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
std::lock_guard<std::mutex> lock(mMutex);
+ mCurrentDisplayFrame->token = token;
if (!prediction) {
mCurrentDisplayFrame->predictionState = PredictionState::Expired;
} else {
@@ -408,6 +492,7 @@
}
totalJankReasons |= displayFrame->jankType;
+ traceDisplayFrame(*displayFrame);
for (auto& surfaceFrame : displayFrame->surfaceFrames) {
if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
@@ -419,7 +504,6 @@
if (predictionState == PredictionState::Expired) {
// Jank analysis cannot be done on apps that don't use predictions
surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0);
- continue;
} else if (predictionState == PredictionState::Valid) {
const auto& actuals = surfaceFrame->getActuals();
const auto& predictions = surfaceFrame->getPredictions();
@@ -453,6 +537,7 @@
surfaceFrame->setJankInfo(jankType, jankMetadata);
}
}
+ surfaceFrame->traceSurfaceFrame(displayFrame->token);
}
mTimeStats->incrementJankyFrames(totalJankReasons);
@@ -569,4 +654,31 @@
setMaxDisplayFrames(kDefaultMaxDisplayFrames);
}
+void FrameTimeline::traceDisplayFrame(const DisplayFrame& displayFrame) {
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ if (displayFrame.token == ISurfaceComposer::INVALID_VSYNC_ID) {
+ ALOGD("Cannot trace DisplayFrame with invalid token");
+ return;
+ }
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
+ packet->set_timestamp(static_cast<uint64_t>(systemTime()));
+
+ auto* event = packet->set_frame_timeline_event();
+ auto* displayFrameEvent = event->set_display_frame();
+
+ displayFrameEvent->set_token(displayFrame.token);
+ displayFrameEvent->set_present_type(presentTypeToProto(displayFrame.jankMetadata));
+ displayFrameEvent->set_on_time_finish(!(displayFrame.jankMetadata & LateFinish));
+ displayFrameEvent->set_gpu_composition(displayFrame.jankMetadata & GpuComposition);
+ displayFrameEvent->set_jank_type(JankTypeToProto(displayFrame.jankType));
+
+ displayFrameEvent->set_expected_start_ns(displayFrame.surfaceFlingerPredictions.startTime);
+ displayFrameEvent->set_expected_end_ns(displayFrame.surfaceFlingerPredictions.endTime);
+
+ displayFrameEvent->set_actual_start_ns(displayFrame.surfaceFlingerActuals.startTime);
+ displayFrameEvent->set_actual_end_ns(displayFrame.surfaceFlingerActuals.endTime);
+ });
+}
+
} // namespace android::frametimeline::impl