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