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