• 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.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