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 kotlin.math.abs 26 27 object Utils { interpolateStartTimestampFromTransitionnull28 fun interpolateStartTimestampFromTransition(transition: Transition, reader: Reader): Timestamp { 29 val wmTrace = reader.readWmTrace() ?: error("Missing WM trace") 30 val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace") 31 val transactionsTrace = 32 reader.readTransactionsTrace() ?: error("Missing transactions trace") 33 34 val lastWmEntryBeforeTransitionCreated = wmTrace.getEntryAt(transition.createTime) 35 val elapsedNanos = lastWmEntryBeforeTransitionCreated.timestamp.elapsedNanos 36 val unixNanos = lastWmEntryBeforeTransitionCreated.timestamp.unixNanos 37 38 val startTransactionAppliedTimestamp = 39 transition.getStartTransaction(transactionsTrace)?.let { 40 layersTrace.getEntryForTransaction(it).timestamp 41 } 42 43 // If we don't have a startTransactionAppliedTimestamp it's likely because the start 44 // transaction was merged into another transaction so we can't match the id, so we need to 45 // fallback on the send time reported on the WM side. 46 val systemUptimeNanos = 47 startTransactionAppliedTimestamp?.systemUptimeNanos 48 ?: transition.createTime.systemUptimeNanos 49 50 // This fallback doesn't really work because then systemUptimeNanos is 0... 51 return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos) 52 } 53 interpolateFinishTimestampFromTransitionnull54 fun interpolateFinishTimestampFromTransition( 55 transition: Transition, 56 reader: Reader, 57 debugString: String? = null, 58 ): Timestamp { 59 val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace") 60 val wmTrace = reader.readWmTrace() ?: error("Missing WM trace") 61 val transactionsTrace = 62 reader.readTransactionsTrace() ?: error("Missing transactions trace") 63 64 // There is a delay between when we flag that transition as finished with the CUJ tags 65 // and when it is actually finished on the SF side. We try and account for that by 66 // checking when the finish transaction is actually applied. 67 // TODO: Figure out how to get the vSyncId that the Jank tracker actually gets to avoid 68 // relying on the transition and have a common end point. 69 val finishTransactionAppliedTimestamp = 70 transition.getFinishTransaction(transactionsTrace)?.let { 71 layersTrace.getEntryForTransaction(it).timestamp 72 } 73 74 val elapsedNanos: Long 75 val systemUptimeNanos: Long 76 val unixNanos: Long 77 val sfEntryAtTransitionFinished: LayerTraceEntry 78 if (finishTransactionAppliedTimestamp == null) { 79 // If we don't have a finishTransactionAppliedTimestamp it's likely because the finish 80 // transaction was merged into another transaction so we can't match the id, so we need 81 // to fallback on the finish time reported on the WM side. 82 val wmEntryAtTransitionFinished = 83 wmTrace.entries.firstOrNull { it.timestamp >= transition.finishTime } 84 85 elapsedNanos = 86 wmEntryAtTransitionFinished?.timestamp?.elapsedNanos 87 ?: transition.finishTime.elapsedNanos 88 89 unixNanos = 90 if (wmEntryAtTransitionFinished != null) { 91 wmEntryAtTransitionFinished.timestamp.unixNanos 92 } else { 93 require(wmTrace.entries.isNotEmpty()) { "WM trace should not be empty!" } 94 val closestWmEntry = 95 wmTrace.entries.minByOrNull { 96 abs(it.timestamp.elapsedNanos - transition.finishTime.elapsedNanos) 97 } ?: error("WM entry was unexpectedly empty!") 98 val offset = 99 closestWmEntry.timestamp.unixNanos - closestWmEntry.timestamp.elapsedNanos 100 transition.finishTime.elapsedNanos + offset 101 } 102 103 sfEntryAtTransitionFinished = 104 layersTrace.entries.firstOrNull { it.timestamp.unixNanos >= unixNanos } 105 ?: error("No SF entry for finish timestamp") 106 systemUptimeNanos = sfEntryAtTransitionFinished.timestamp.systemUptimeNanos 107 } else { 108 elapsedNanos = 109 (wmTrace.entries.firstOrNull { it.timestamp >= finishTransactionAppliedTimestamp } 110 ?: wmTrace.entries.last()) 111 .timestamp 112 .elapsedNanos 113 systemUptimeNanos = 114 layersTrace 115 .getEntryAt(finishTransactionAppliedTimestamp) 116 .timestamp 117 .systemUptimeNanos 118 unixNanos = 119 layersTrace.getEntryAt(finishTransactionAppliedTimestamp).timestamp.unixNanos 120 } 121 122 return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos) 123 } 124 getFullTimestampAtnull125 fun getFullTimestampAt(layersTraceEntry: LayerTraceEntry, reader: Reader): Timestamp { 126 val wmTrace = reader.readWmTrace() ?: error("Missing WM trace") 127 128 val elapsedNanos = 129 (wmTrace.entries.firstOrNull { it.timestamp >= layersTraceEntry.timestamp } 130 ?: wmTrace.entries.last()) 131 .timestamp 132 .elapsedNanos 133 val systemUptimeNanos = layersTraceEntry.timestamp.systemUptimeNanos 134 val unixNanos = layersTraceEntry.timestamp.unixNanos 135 136 return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos) 137 } 138 getOnDisplayFornull139 fun getOnDisplayFor(layerTraceEntry: LayerTraceEntry): Display { 140 val displays = layerTraceEntry.displays.filter { !it.isVirtual } 141 require(displays.isNotEmpty()) { "Failed to get a display for provided entry" } 142 val onDisplays = displays.filter { it.isOn } 143 require(onDisplays.isNotEmpty()) { "No on displays found for entry" } 144 require(onDisplays.size == 1) { "More than one on display found!" } 145 return onDisplays.first() 146 } 147 } 148