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.device.traces.io 18 19 import android.tools.common.Logger 20 import android.tools.common.Tag 21 import android.tools.common.Timestamp 22 import android.tools.common.io.Artifact 23 import android.tools.common.io.FLICKER_IO_TAG 24 import android.tools.common.io.Reader 25 import android.tools.common.io.ResultArtifactDescriptor 26 import android.tools.common.io.TraceType 27 import android.tools.common.parsers.events.EventLogParser 28 import android.tools.common.traces.events.CujTrace 29 import android.tools.common.traces.events.EventLog 30 import android.tools.common.traces.surfaceflinger.LayersTrace 31 import android.tools.common.traces.surfaceflinger.TransactionsTrace 32 import android.tools.common.traces.wm.TransitionsTrace 33 import android.tools.common.traces.wm.WindowManagerTrace 34 import android.tools.device.traces.TraceConfig 35 import android.tools.device.traces.TraceConfigs 36 import android.tools.device.traces.parsers.surfaceflinger.LayersTraceParser 37 import android.tools.device.traces.parsers.surfaceflinger.TransactionsTraceParser 38 import android.tools.device.traces.parsers.wm.TransitionTraceParser 39 import android.tools.device.traces.parsers.wm.WindowManagerDumpParser 40 import android.tools.device.traces.parsers.wm.WindowManagerTraceParser 41 import androidx.annotation.VisibleForTesting 42 import java.io.IOException 43 44 /** 45 * Helper class to read results from a flicker artifact 46 * 47 * @param _result to read from 48 * @param traceConfig 49 */ 50 open class ResultReader(_result: IResultData, internal val traceConfig: TraceConfigs) : Reader { 51 @VisibleForTesting 52 var result = _result 53 internal set 54 override val artifact: Artifact = result.artifact 55 override val artifactPath: String 56 get() = result.artifact.absolutePath 57 override val runStatus 58 get() = result.runStatus 59 internal val transitionTimeRange 60 get() = result.transitionTimeRange 61 override val isFailure 62 get() = runStatus.isFailure 63 override val executionError 64 get() = result.executionError 65 readBytesnull66 override fun readBytes(traceType: TraceType, tag: String): ByteArray? = 67 artifact.readBytes(ResultArtifactDescriptor(traceType, tag)) 68 69 /** 70 * {@inheritDoc} 71 * 72 * @throws IOException if the artifact file doesn't exist or can't be read 73 */ 74 @Throws(IOException::class) 75 override fun readWmState(tag: String): WindowManagerTrace? { 76 return Logger.withTracing("readWmState#$tag") { 77 val descriptor = ResultArtifactDescriptor(TraceType.WM_DUMP, tag) 78 Logger.d(FLICKER_IO_TAG, "Reading WM trace descriptor=$descriptor from $result") 79 val traceData = artifact.readBytes(descriptor) 80 traceData?.let { WindowManagerDumpParser().parse(it, clearCache = true) } 81 } 82 } 83 84 /** 85 * {@inheritDoc} 86 * 87 * @throws IOException if the artifact file doesn't exist or can't be read 88 */ 89 @Throws(IOException::class) readWmTracenull90 override fun readWmTrace(): WindowManagerTrace? { 91 return Logger.withTracing("readWmTrace") { 92 val descriptor = ResultArtifactDescriptor(TraceType.WM) 93 artifact.readBytes(descriptor)?.let { 94 val trace = 95 WindowManagerTraceParser() 96 .parse( 97 it, 98 from = transitionTimeRange.start, 99 to = transitionTimeRange.end, 100 addInitialEntry = true, 101 clearCache = true 102 ) 103 val minimumEntries = minimumTraceEntriesForConfig(traceConfig.wmTrace) 104 require(trace.entries.size >= minimumEntries) { 105 "WM trace contained ${trace.entries.size} entries, " + 106 "expected at least $minimumEntries... :: " + 107 "transition starts at ${transitionTimeRange.start} and " + 108 "ends at ${transitionTimeRange.end}." 109 } 110 trace 111 } 112 } 113 } 114 115 /** 116 * {@inheritDoc} 117 * 118 * @throws IOException if the artifact file doesn't exist or can't be read 119 */ 120 @Throws(IOException::class) readLayersTracenull121 override fun readLayersTrace(): LayersTrace? { 122 return Logger.withTracing("readLayersTrace") { 123 val descriptor = ResultArtifactDescriptor(TraceType.SF) 124 artifact.readBytes(descriptor)?.let { 125 val trace = 126 LayersTraceParser() 127 .parse( 128 it, 129 transitionTimeRange.start, 130 transitionTimeRange.end, 131 addInitialEntry = true, 132 clearCache = true 133 ) 134 val minimumEntries = minimumTraceEntriesForConfig(traceConfig.layersTrace) 135 require(trace.entries.size >= minimumEntries) { 136 "Layers trace contained ${trace.entries.size} entries, " + 137 "expected at least $minimumEntries... :: " + 138 "transition starts at ${transitionTimeRange.start} and " + 139 "ends at ${transitionTimeRange.end}." 140 } 141 trace 142 } 143 } 144 } 145 146 /** 147 * {@inheritDoc} 148 * 149 * @throws IOException if the artifact file doesn't exist or can't be read 150 */ 151 @Throws(IOException::class) readLayersDumpnull152 override fun readLayersDump(tag: String): LayersTrace? { 153 return Logger.withTracing("readLayersDump#$tag") { 154 val descriptor = ResultArtifactDescriptor(TraceType.SF_DUMP, tag) 155 val traceData = artifact.readBytes(descriptor) 156 traceData?.let { LayersTraceParser().parse(it, clearCache = true) } 157 } 158 } 159 160 /** 161 * {@inheritDoc} 162 * 163 * @throws IOException if the artifact file doesn't exist or can't be read 164 */ 165 @Throws(IOException::class) readTransactionsTracenull166 override fun readTransactionsTrace(): TransactionsTrace? = 167 Logger.withTracing("readTransactionsTrace") { 168 doReadTransactionsTrace(from = transitionTimeRange.start, to = transitionTimeRange.end) 169 } 170 doReadTransactionsTracenull171 private fun doReadTransactionsTrace(from: Timestamp, to: Timestamp): TransactionsTrace? { 172 val traceData = artifact.readBytes(ResultArtifactDescriptor(TraceType.TRANSACTION)) 173 return traceData?.let { 174 val trace = TransactionsTraceParser().parse(it, from, to, addInitialEntry = true) 175 require(trace.entries.isNotEmpty()) { "Transactions trace cannot be empty" } 176 trace 177 } 178 } 179 180 /** 181 * {@inheritDoc} 182 * 183 * @throws IOException if the artifact file doesn't exist or can't be read 184 */ 185 @Throws(IOException::class) readTransitionsTracenull186 override fun readTransitionsTrace(): TransitionsTrace? { 187 return Logger.withTracing("readTransitionsTrace") { 188 val wmSideTraceData = 189 artifact.readBytes(ResultArtifactDescriptor(TraceType.WM_TRANSITION)) 190 val shellSideTraceData = 191 artifact.readBytes(ResultArtifactDescriptor(TraceType.SHELL_TRANSITION)) 192 193 if (wmSideTraceData == null || shellSideTraceData == null) { 194 null 195 } else { 196 val trace = 197 TransitionTraceParser() 198 .parse( 199 wmSideTraceData, 200 shellSideTraceData, 201 from = transitionTimeRange.start, 202 to = transitionTimeRange.end 203 ) 204 if (!traceConfig.transitionsTrace.allowNoChange) { 205 require(trace.entries.isNotEmpty()) { "Transitions trace cannot be empty" } 206 } 207 trace 208 } 209 } 210 } 211 minimumTraceEntriesForConfignull212 private fun minimumTraceEntriesForConfig(config: TraceConfig): Int { 213 return if (config.allowNoChange) 1 else 2 214 } 215 216 /** 217 * {@inheritDoc} 218 * 219 * @throws IOException if the artifact file doesn't exist or can't be read 220 */ 221 @Throws(IOException::class) readEventLogTracenull222 override fun readEventLogTrace(): EventLog? { 223 return Logger.withTracing("readEventLogTrace") { 224 val descriptor = ResultArtifactDescriptor(TraceType.EVENT_LOG) 225 artifact.readBytes(descriptor)?.let { 226 EventLogParser() 227 .parseSlice(it, from = transitionTimeRange.start, to = transitionTimeRange.end) 228 } 229 } 230 } 231 232 /** 233 * {@inheritDoc} 234 * 235 * @throws IOException if the artifact file doesn't exist or can't be read 236 */ 237 @Throws(IOException::class) readCujTracenull238 override fun readCujTrace(): CujTrace? = readEventLogTrace()?.cujTrace 239 240 /** @return an [Reader] for the subsection of the trace we are reading in this reader */ 241 override fun slice(startTimestamp: Timestamp, endTimestamp: Timestamp): ResultReader { 242 val slicedResult = result.slice(startTimestamp, endTimestamp) 243 return ResultReader(slicedResult, traceConfig) 244 } 245 toStringnull246 override fun toString(): String = "$result" 247 248 /** @return the number of files in the artifact */ 249 @VisibleForTesting fun countFiles(): Int = artifact.traceCount() 250 251 /** @return if a file with type [traceType] linked to a [tag] exists in the artifact */ 252 fun hasTraceFile(traceType: TraceType, tag: String = Tag.ALL): Boolean { 253 val descriptor = ResultArtifactDescriptor(traceType, tag) 254 return artifact.hasTrace(descriptor) 255 } 256 } 257