• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.tools.flicker.extractors
18 
19 import android.tools.Timestamp
20 import android.tools.Timestamps
21 import android.tools.io.Reader
22 import android.tools.traces.surfaceflinger.Display
23 import android.tools.traces.surfaceflinger.LayerTraceEntry
24 import android.tools.traces.wm.Transition
25 import android.util.Log
26 import kotlin.math.abs
27 
28 object Utils {
29     const val LOG_TAG = "FlickerExtractorUtils"
30 
interpolateStartTimestampFromTransitionnull31     fun interpolateStartTimestampFromTransition(transition: Transition, reader: Reader): Timestamp {
32         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
33         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
34         val transactionsTrace =
35             reader.readTransactionsTrace() ?: error("Missing transactions trace")
36 
37         val lastWmEntryBeforeTransitionWasSentToShell = try {
38             wmTrace.getEntryAt(transition.sendTime)
39         } catch (e: Exception) {
40             throw RuntimeException(
41                 "Failed to get last WM entry before transition was sent to shell: $transition", e)
42         }
43         val elapsedNanos = lastWmEntryBeforeTransitionWasSentToShell.timestamp.elapsedNanos
44         val unixNanos = lastWmEntryBeforeTransitionWasSentToShell.timestamp.unixNanos
45 
46         assert(elapsedNanos != 0L) {
47             "lastWmEntryBeforeTransitionWasSentToShell.timestamp.elapsedNanos is 0"
48         }
49         assert(unixNanos != 0L) {
50             "lastWmEntryBeforeTransitionWasSentToShell.timestamp.unixNanos is 0"
51         }
52 
53         val startTransactionAppliedTimestamp =
54             transition.getStartTransaction(transactionsTrace)?.let {
55                 layersTrace.getEntryForTransaction(it).timestamp
56             }
57 
58         if (startTransactionAppliedTimestamp != null) {
59             require(startTransactionAppliedTimestamp.systemUptimeNanos != 0L) {
60                 "Start transaction applied timestamp is missing system uptime"
61             }
62         }
63 
64         // If we don't have a startTransactionAppliedTimestamp it's likely because the start
65         // transaction was merged into another transaction so we can't match the id, so we need to
66         // fallback on the send time reported on the WM side.
67         val systemUptimeNanos =
68             startTransactionAppliedTimestamp?.systemUptimeNanos
69                 ?: transition.createTime.systemUptimeNanos
70 
71         require(systemUptimeNanos != 0L) {
72             "Both startTransactionAppliedTimestamp and transition's create time are missing " +
73                 "system uptime: $startTransactionAppliedTimestamp and $transition"
74         }
75 
76         Log.d(
77             LOG_TAG,
78             "Interpolated start timestamp for $transition to: " +
79                 "elapsed $elapsedNanos, uptime $systemUptimeNanos, unix $unixNanos",
80         )
81 
82         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
83     }
84 
interpolateFinishTimestampFromTransitionnull85     fun interpolateFinishTimestampFromTransition(
86         transition: Transition,
87         reader: Reader,
88         debugString: String? = null,
89     ): Timestamp {
90         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
91         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
92         val transactionsTrace =
93             reader.readTransactionsTrace() ?: error("Missing transactions trace")
94 
95         // There is a delay between when we flag that transition as finished with the CUJ tags
96         // and when it is actually finished on the SF side. We try and account for that by
97         // checking when the finish transaction is actually applied.
98         // TODO: Figure out how to get the vSyncId that the Jank tracker actually gets to avoid
99         //       relying on the transition and have a common end point.
100         val finishTransactionAppliedTimestamp =
101             transition.getFinishTransaction(transactionsTrace)?.let {
102                 layersTrace.getEntryForTransaction(it).timestamp
103             }
104 
105         val elapsedNanos: Long
106         val systemUptimeNanos: Long
107         val unixNanos: Long
108         val sfEntryAtTransitionFinished: LayerTraceEntry
109         if (finishTransactionAppliedTimestamp == null) {
110             // If we don't have a finishTransactionAppliedTimestamp it's likely because the finish
111             // transaction was merged into another transaction so we can't match the id, so we need
112             // to fallback on the finish time reported on the WM side.
113             val wmEntryAtTransitionFinished =
114                 wmTrace.entries.firstOrNull { it.timestamp >= transition.finishTime }
115 
116             elapsedNanos =
117                 wmEntryAtTransitionFinished?.timestamp?.elapsedNanos
118                     ?: transition.finishTime.elapsedNanos
119 
120             unixNanos =
121                 if (wmEntryAtTransitionFinished != null) {
122                     wmEntryAtTransitionFinished.timestamp.unixNanos
123                 } else {
124                     require(wmTrace.entries.isNotEmpty()) { "WM trace should not be empty!" }
125                     val closestWmEntry =
126                         wmTrace.entries.minByOrNull {
127                             abs(it.timestamp.elapsedNanos - transition.finishTime.elapsedNanos)
128                         } ?: error("WM entry was unexpectedly empty!")
129                     val offset =
130                         closestWmEntry.timestamp.unixNanos - closestWmEntry.timestamp.elapsedNanos
131                     transition.finishTime.elapsedNanos + offset
132                 }
133 
134             sfEntryAtTransitionFinished =
135                 layersTrace.entries.firstOrNull { it.timestamp.unixNanos >= unixNanos }
136                     ?: error("No SF entry for finish timestamp")
137             systemUptimeNanos = sfEntryAtTransitionFinished.timestamp.systemUptimeNanos
138         } else {
139             elapsedNanos =
140                 (wmTrace.entries.firstOrNull { it.timestamp >= finishTransactionAppliedTimestamp }
141                         ?: wmTrace.entries.last())
142                     .timestamp
143                     .elapsedNanos
144             systemUptimeNanos =
145                 layersTrace
146                     .getEntryAt(finishTransactionAppliedTimestamp)
147                     .timestamp
148                     .systemUptimeNanos
149             unixNanos =
150                 layersTrace.getEntryAt(finishTransactionAppliedTimestamp).timestamp.unixNanos
151         }
152 
153         Log.d(
154             LOG_TAG,
155             "Interpolated finish timestamp for $transition to: " +
156                 "elapsed $elapsedNanos, uptime $systemUptimeNanos, unix $unixNanos",
157         )
158         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
159     }
160 
getFullTimestampAtnull161     fun getFullTimestampAt(layersTraceEntry: LayerTraceEntry, reader: Reader): Timestamp {
162         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
163 
164         val elapsedNanos =
165             (wmTrace.entries.firstOrNull { it.timestamp >= layersTraceEntry.timestamp }
166                     ?: wmTrace.entries.last())
167                 .timestamp
168                 .elapsedNanos
169         val systemUptimeNanos = layersTraceEntry.timestamp.systemUptimeNanos
170         val unixNanos = layersTraceEntry.timestamp.unixNanos
171 
172         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
173     }
174 
getOnDisplayFornull175     fun getOnDisplayFor(layerTraceEntry: LayerTraceEntry): Display {
176         val displays = layerTraceEntry.displays.filter { !it.isVirtual }
177         require(displays.isNotEmpty()) { "Failed to get a display for provided entry" }
178         val onDisplays = displays.filter { it.isOn }
179         require(onDisplays.isNotEmpty()) { "No on displays found for entry" }
180         require(onDisplays.size == 1) { "More than one on display found!" }
181         return onDisplays.first()
182     }
183 }
184