• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base.jank_tracker;
6 
7 import android.os.Handler;
8 
9 import org.chromium.base.TraceEvent;
10 
11 /**
12  * This runnable receives a FrameMetricsStore instance and starts/stops tracking a given scenario.
13  * When a scenario stops it takes its metrics and sends them to native to be recorded in UMA.
14  * This is executed by JankReportingScheduler on its own thread.
15  */
16 class JankReportingRunnable implements Runnable {
17     private final FrameMetricsStore mMetricsStore;
18     private final @JankScenario int mScenario;
19     private final boolean mIsStartingTracking;
20     // This must be the same handler that this is running on.
21     private final Handler mHandler;
22     // If metrics should be collected based on the state (scrolling) specify a
23     // JankEndScenarioTime.
24     private final JankEndScenarioTime mJankEndScenarioTime;
25 
26     // When a JankEndScenarioTime is specified we don't immediately collect the metrics but instead
27     // post a task (this runnable). However to keep code reuse the same between delay/no-delay we
28     // use the same runnable but don't post it when there is no delay.
29     private class FinalReportingRunnable implements Runnable {
30         @Override
run()31         public void run() {
32             try (TraceEvent e = TraceEvent.scoped("ReportingCUJScenarioData", mScenario)) {
33                 JankMetrics metrics;
34                 if (mJankEndScenarioTime == null) {
35                     metrics = mMetricsStore.stopTrackingScenario(mScenario);
36                 } else {
37                     // Since this is after the timeout we just unconditionally get the metrics.
38                     metrics =
39                             mMetricsStore.stopTrackingScenario(
40                                     mScenario, mJankEndScenarioTime.endScenarioTimeNs);
41                 }
42 
43                 if (metrics == null || metrics.timestampsNs.length == 0) {
44                     TraceEvent.instant("no metrics");
45                     return;
46                 }
47 
48                 long startTime = metrics.timestampsNs[0] / 1000000;
49                 long lastTime = metrics.timestampsNs[metrics.timestampsNs.length - 1] / 1000000;
50                 long lastDuration = metrics.durationsNs[metrics.durationsNs.length - 1] / 1000000;
51                 // The time that we have metrics covering is from the first VSYNC_TIMESTAMP
52                 // (startTime) to the last frame has finished (lastTime + lastDuration).
53                 long endTime = lastTime - startTime + lastDuration;
54 
55                 // Confirm that the current call context is valid.
56                 // Debug builds will assert and fail; release builds will optimize this out.
57                 JankMetricUMARecorderJni.get();
58                 // TODO(salg@): Cache metrics in case native takes >30s to initialize.
59                 JankMetricUMARecorder.recordJankMetricsToUMA(
60                         metrics, startTime, endTime, mScenario);
61             }
62         }
63     }
64 
JankReportingRunnable( FrameMetricsStore metricsStore, @JankScenario int scenario, boolean isStartingTracking, Handler handler, JankEndScenarioTime endScenarioTime)65     JankReportingRunnable(
66             FrameMetricsStore metricsStore,
67             @JankScenario int scenario,
68             boolean isStartingTracking,
69             Handler handler,
70             JankEndScenarioTime endScenarioTime) {
71         mMetricsStore = metricsStore;
72         mScenario = scenario;
73         mIsStartingTracking = isStartingTracking;
74         mHandler = handler;
75         mJankEndScenarioTime = endScenarioTime;
76     }
77 
78     @Override
run()79     public void run() {
80         try (TraceEvent e =
81                 TraceEvent.scoped(
82                         "StartingOrStoppingJankScenario",
83                         "StartingScenario:"
84                                 + Boolean.toString(mIsStartingTracking)
85                                 + ",Scenario:"
86                                 + Integer.toString(mScenario))) {
87             if (mIsStartingTracking) {
88                 if (mMetricsStore == null) {
89                     TraceEvent.instant("StartTrackingScenario metrics store null");
90                     return;
91                 }
92                 mMetricsStore.startTrackingScenario(mScenario);
93                 return;
94             }
95             boolean dataIsReady =
96                     mJankEndScenarioTime == null
97                             || (mJankEndScenarioTime != null
98                                     && mMetricsStore.hasReceivedMetricsPast(
99                                             mJankEndScenarioTime.endScenarioTimeNs));
100 
101             if (dataIsReady) {
102                 new FinalReportingRunnable().run();
103             } else {
104                 mHandler.postDelayed(
105                         new FinalReportingRunnable(), mJankEndScenarioTime.timeoutDelayMs);
106             }
107         }
108     }
109 }
110