1 /* <lambda>null2 * 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.traces.io 18 19 import android.tools.Tag 20 import android.tools.Timestamp 21 import android.tools.io.Artifact 22 import android.tools.io.FLICKER_IO_TAG 23 import android.tools.io.Reader 24 import android.tools.io.ResultArtifactDescriptor 25 import android.tools.io.TraceType 26 import android.tools.parsers.events.EventLogParser 27 import android.tools.traces.TraceConfig 28 import android.tools.traces.TraceConfigs 29 import android.tools.traces.events.CujTrace 30 import android.tools.traces.events.EventLog 31 import android.tools.traces.parsers.perfetto.LayersTraceParser 32 import android.tools.traces.parsers.perfetto.ProtoLogTraceParser 33 import android.tools.traces.parsers.perfetto.TraceProcessorSession 34 import android.tools.traces.parsers.perfetto.TransactionsTraceParser 35 import android.tools.traces.parsers.perfetto.TransitionsTraceParser 36 import android.tools.traces.parsers.wm.LegacyTransitionTraceParser 37 import android.tools.traces.parsers.wm.WindowManagerDumpParser 38 import android.tools.traces.parsers.wm.WindowManagerTraceParser 39 import android.tools.traces.protolog.ProtoLogTrace 40 import android.tools.traces.surfaceflinger.LayersTrace 41 import android.tools.traces.surfaceflinger.TransactionsTrace 42 import android.tools.traces.wm.TransitionsTrace 43 import android.tools.traces.wm.WindowManagerTrace 44 import android.tools.withTracing 45 import android.util.Log 46 import androidx.annotation.VisibleForTesting 47 import java.io.IOException 48 49 /** 50 * Helper class to read results from a flicker artifact 51 * 52 * @param _result to read from 53 * @param traceConfig 54 */ 55 open class ResultReader(_result: IResultData, internal val traceConfig: TraceConfigs) : Reader { 56 @VisibleForTesting 57 var result = _result 58 internal set 59 override val artifact: Artifact = result.artifact 60 override val artifactPath: String 61 get() = result.artifact.absolutePath 62 override val runStatus 63 get() = result.runStatus 64 internal val transitionTimeRange 65 get() = result.transitionTimeRange 66 override val isFailure 67 get() = runStatus.isFailure 68 override val executionError 69 get() = result.executionError 70 71 override fun readBytes(traceType: TraceType, tag: String): ByteArray? = 72 artifact.readBytes(ResultArtifactDescriptor(traceType, tag)) 73 74 /** 75 * {@inheritDoc} 76 * 77 * @throws IOException if the artifact file doesn't exist or can't be read 78 */ 79 @Throws(IOException::class) 80 override fun readWmState(tag: String): WindowManagerTrace? { 81 return withTracing("readWmState#$tag") { 82 val descriptor = ResultArtifactDescriptor(TraceType.WM_DUMP, tag) 83 Log.d(FLICKER_IO_TAG, "Reading WM trace descriptor=$descriptor from $result") 84 val traceData = artifact.readBytes(descriptor) 85 traceData?.let { WindowManagerDumpParser().parse(it, clearCache = true) } 86 } 87 } 88 89 /** 90 * {@inheritDoc} 91 * 92 * @throws IOException if the artifact file doesn't exist or can't be read 93 */ 94 @Throws(IOException::class) 95 override fun readWmTrace(): WindowManagerTrace? { 96 return withTracing("readWmTrace") { 97 val descriptor = ResultArtifactDescriptor(TraceType.WM) 98 artifact.readBytes(descriptor)?.let { 99 val trace = 100 WindowManagerTraceParser() 101 .parse( 102 it, 103 from = transitionTimeRange.start, 104 to = transitionTimeRange.end, 105 addInitialEntry = true, 106 clearCache = true 107 ) 108 val minimumEntries = minimumTraceEntriesForConfig(traceConfig.wmTrace) 109 require(trace.entries.size >= minimumEntries) { 110 "WM trace contained ${trace.entries.size} entries, " + 111 "expected at least $minimumEntries... :: " + 112 "transition starts at ${transitionTimeRange.start} and " + 113 "ends at ${transitionTimeRange.end}." 114 } 115 trace 116 } 117 } 118 } 119 120 /** 121 * {@inheritDoc} 122 * 123 * @throws IOException if the artifact file doesn't exist or can't be read 124 */ 125 @Throws(IOException::class) 126 override fun readLayersTrace(): LayersTrace? { 127 return withTracing("readLayersTrace") { 128 val descriptor = ResultArtifactDescriptor(TraceType.SF) 129 artifact.readBytes(descriptor)?.let { 130 val trace = 131 TraceProcessorSession.loadPerfettoTrace(it) { session -> 132 LayersTraceParser() 133 .parse( 134 session, 135 transitionTimeRange.start, 136 transitionTimeRange.end, 137 addInitialEntry = true, 138 clearCache = true 139 ) 140 } 141 val minimumEntries = minimumTraceEntriesForConfig(traceConfig.layersTrace) 142 require(trace.entries.size >= minimumEntries) { 143 "Layers trace contained ${trace.entries.size} entries, " + 144 "expected at least $minimumEntries... :: " + 145 "transition starts at ${transitionTimeRange.start} and " + 146 "ends at ${transitionTimeRange.end}." 147 } 148 trace 149 } 150 } 151 } 152 153 /** 154 * {@inheritDoc} 155 * 156 * @throws IOException if the artifact file doesn't exist or can't be read 157 */ 158 @Throws(IOException::class) 159 override fun readLayersDump(tag: String): LayersTrace? { 160 return withTracing("readLayersDump#$tag") { 161 val descriptor = ResultArtifactDescriptor(TraceType.SF_DUMP, tag) 162 val traceData = artifact.readBytes(descriptor) 163 if (traceData != null) { 164 TraceProcessorSession.loadPerfettoTrace(traceData) { session -> 165 LayersTraceParser().parse(session, clearCache = true) 166 } 167 } else { 168 null 169 } 170 } 171 } 172 173 /** 174 * {@inheritDoc} 175 * 176 * @throws IOException if the artifact file doesn't exist or can't be read 177 */ 178 @Throws(IOException::class) 179 override fun readTransactionsTrace(): TransactionsTrace? = 180 withTracing("readTransactionsTrace") { 181 doReadTransactionsTrace(from = transitionTimeRange.start, to = transitionTimeRange.end) 182 } 183 184 private fun doReadTransactionsTrace(from: Timestamp, to: Timestamp): TransactionsTrace? { 185 val traceData = artifact.readBytes(ResultArtifactDescriptor(TraceType.TRANSACTION)) 186 return traceData?.let { 187 val trace = 188 TraceProcessorSession.loadPerfettoTrace(traceData) { session -> 189 TransactionsTraceParser().parse(session, from, to, addInitialEntry = true) 190 } 191 require(trace.entries.isNotEmpty()) { "Transactions trace cannot be empty" } 192 trace 193 } 194 } 195 196 /** 197 * {@inheritDoc} 198 * 199 * @throws IOException if the artifact file doesn't exist or can't be read 200 */ 201 @Throws(IOException::class) 202 override fun readTransitionsTrace(): TransitionsTrace? { 203 return withTracing("readTransitionsTrace") { 204 val trace = 205 if (android.tracing.Flags.perfettoTransitionTracing()) { 206 readPerfettoTransitionsTrace() 207 } else { 208 readLegacyTransitionTrace() 209 } 210 211 if (trace == null) { 212 return@withTracing null 213 } 214 215 if (!traceConfig.transitionsTrace.allowNoChange) { 216 require(trace.entries.isNotEmpty()) { "Transitions trace cannot be empty" } 217 } 218 219 trace 220 } 221 } 222 223 /** 224 * {@inheritDoc} 225 * 226 * @throws IOException if the artifact file doesn't exist or can't be read 227 */ 228 @Throws(IOException::class) 229 override fun readProtoLogTrace(): ProtoLogTrace? { 230 return withTracing("readProtoLogTrace") { 231 val traceData = artifact.readBytes(ResultArtifactDescriptor(TraceType.PERFETTO)) 232 233 traceData?.let { 234 TraceProcessorSession.loadPerfettoTrace(traceData) { session -> 235 ProtoLogTraceParser() 236 .parse( 237 session, 238 from = transitionTimeRange.start, 239 to = transitionTimeRange.end 240 ) 241 } 242 } 243 } 244 } 245 246 private fun readPerfettoTransitionsTrace(): TransitionsTrace? { 247 val traceData = artifact.readBytes(ResultArtifactDescriptor(TraceType.PERFETTO)) 248 249 return traceData?.let { 250 TraceProcessorSession.loadPerfettoTrace(traceData) { session -> 251 TransitionsTraceParser() 252 .parse(session, from = transitionTimeRange.start, to = transitionTimeRange.end) 253 } 254 } 255 } 256 257 private fun readLegacyTransitionTrace(): TransitionsTrace? { 258 val wmSideTraceData = 259 artifact.readBytes(ResultArtifactDescriptor(TraceType.LEGACY_WM_TRANSITION)) 260 val shellSideTraceData = 261 artifact.readBytes(ResultArtifactDescriptor(TraceType.LEGACY_SHELL_TRANSITION)) 262 263 return if (wmSideTraceData == null || shellSideTraceData == null) { 264 null 265 } else { 266 LegacyTransitionTraceParser() 267 .parse( 268 wmSideTraceData, 269 shellSideTraceData, 270 from = transitionTimeRange.start, 271 to = transitionTimeRange.end 272 ) 273 } 274 } 275 276 private fun minimumTraceEntriesForConfig(config: TraceConfig): Int { 277 return if (config.allowNoChange) 1 else 2 278 } 279 280 /** 281 * {@inheritDoc} 282 * 283 * @throws IOException if the artifact file doesn't exist or can't be read 284 */ 285 @Throws(IOException::class) 286 override fun readEventLogTrace(): EventLog? { 287 return withTracing("readEventLogTrace") { 288 val descriptor = ResultArtifactDescriptor(TraceType.EVENT_LOG) 289 artifact.readBytes(descriptor)?.let { 290 EventLogParser() 291 .parseSlice(it, from = transitionTimeRange.start, to = transitionTimeRange.end) 292 } 293 } 294 } 295 296 /** 297 * {@inheritDoc} 298 * 299 * @throws IOException if the artifact file doesn't exist or can't be read 300 */ 301 @Throws(IOException::class) 302 override fun readCujTrace(): CujTrace? = readEventLogTrace()?.cujTrace 303 304 /** @return an [Reader] for the subsection of the trace we are reading in this reader */ 305 override fun slice(startTimestamp: Timestamp, endTimestamp: Timestamp): ResultReader { 306 val slicedResult = result.slice(startTimestamp, endTimestamp) 307 return ResultReader(slicedResult, traceConfig) 308 } 309 310 override fun toString(): String = "$result" 311 312 /** @return the number of files in the artifact */ 313 @VisibleForTesting fun countFiles(): Int = artifact.traceCount() 314 315 /** @return if a file with type [traceType] linked to a [tag] exists in the artifact */ 316 fun hasTraceFile(traceType: TraceType, tag: String = Tag.ALL): Boolean { 317 val descriptor = ResultArtifactDescriptor(traceType, tag) 318 return artifact.hasTrace(descriptor) 319 } 320 } 321