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 @file:JvmName("MonitorUtils")
18 @file:OptIn(
19 androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi::class,
20 androidx.benchmark.traceprocessor.ExperimentalTraceProcessorApi::class,
21 )
22
23 package android.tools.traces.monitors
24
25 import android.tools.ScenarioBuilder
26 import android.tools.Tag
27 import android.tools.io.Reader
28 import android.tools.io.TraceType
29 import android.tools.traces.SERVICE_TRACE_CONFIG
30 import android.tools.traces.TRACE_CONFIG_REQUIRE_CHANGES
31 import android.tools.traces.io.IResultData
32 import android.tools.traces.io.ResultReader
33 import android.tools.traces.io.ResultReaderWithLru
34 import android.tools.traces.io.ResultWriter
35 import android.tools.traces.monitors.wm.WindowManagerTraceMonitor
36 import android.tools.traces.parsers.perfetto.LayersTraceParser
37 import android.tools.traces.parsers.perfetto.TraceProcessorSession
38 import android.tools.traces.parsers.perfetto.TransactionsTraceParser
39 import android.tools.traces.parsers.perfetto.WindowManagerTraceParser
40 import android.tools.traces.surfaceflinger.LayersTrace
41 import android.tools.traces.surfaceflinger.TransactionsTrace
42 import android.tools.traces.wm.WindowManagerTrace
43 import java.io.File
44 import perfetto.protos.PerfettoConfig.SurfaceFlingerLayersConfig
45 import perfetto.protos.PerfettoConfig.WindowManagerConfig
46
47 private fun buildResultReader(resultData: IResultData): ResultReader =
48 ResultReader(resultData, TRACE_CONFIG_REQUIRE_CHANGES)
49
50 /**
51 * Reads the Perfetto file form the result reader and keep (or not) a copy
52 *
53 * @param debugFile File to keep a copy of the parsed trace, leave null to not keep any copies
54 * @throws UnsupportedOperationException If tracing is already activated
55 */
56 private fun readBytes(
57 reader: ResultReader,
58 debugFile: File?,
59 traceType: TraceType = TraceType.PERFETTO,
60 tag: String = Tag.ALL,
61 ): ByteArray {
62 val bytes = reader.readBytes(traceType, tag) ?: error("Missing trace $traceType")
63 reader.artifact.deleteIfExists()
64 debugFile?.writeBytes(bytes)
65 return bytes
66 }
67
68 /**
69 * Acquire the [WindowManagerTrace] with the device state changes that happen when executing the
70 * commands defined in the [predicate].
71 *
72 * @param predicate Commands to execute
73 * @throws UnsupportedOperationException If tracing is already activated
74 */
withWMTracingnull75 fun withWMTracing(
76 logFrequency: WindowManagerConfig.LogFrequency =
77 WindowManagerConfig.LogFrequency.LOG_FREQUENCY_FRAME,
78 debugFile: File? = null,
79 predicate: Runnable,
80 ): WindowManagerTrace {
81 val reader =
82 PerfettoTraceMonitor.newBuilder()
83 .enableWindowManagerTrace(logFrequency)
84 .build()
85 .withTracing(resultReaderProvider = { buildResultReader(it) }, predicate)
86
87 val bytes = readBytes(reader, debugFile)
88 return TraceProcessorSession.loadPerfettoTrace(bytes) { session ->
89 WindowManagerTraceParser().parse(session)
90 }
91 }
92
93 /**
94 * Acquire the [LayersTrace] with the device state changes that happen when executing the commands
95 * defined in the [predicate].
96 *
97 * @param flags Flags to indicate tracing level
98 * @param predicate Commands to execute
99 * @param debugFile File to keep a copy of the parsed trace, leave null to not keep any copies
100 * @throws UnsupportedOperationException If tracing is already activated
101 */
102 @JvmOverloads
withSFTracingnull103 fun withSFTracing(
104 flags: List<SurfaceFlingerLayersConfig.TraceFlag>? = null,
105 debugFile: File? = null,
106 predicate: Runnable,
107 ): LayersTrace {
108 val reader =
109 PerfettoTraceMonitor.newBuilder()
110 .enableLayersTrace(flags)
111 .build()
112 .withTracing(resultReaderProvider = { buildResultReader(it) }, predicate)
113
114 val bytes = readBytes(reader, debugFile)
115 return TraceProcessorSession.loadPerfettoTrace(bytes) { session ->
116 LayersTraceParser().parse(session)
117 }
118 }
119
120 /**
121 * Acquire the [TransactionsTrace] with the device state changes that happen when executing the
122 * commands defined in the [predicate].
123 *
124 * @param debugFile File to keep a copy of the parsed trace, leave null to not keep any copies
125 * @param predicate Commands to execute
126 * @throws UnsupportedOperationException If tracing is already activated
127 */
withTransactionsTracingnull128 fun withTransactionsTracing(debugFile: File? = null, predicate: Runnable): TransactionsTrace {
129 val reader =
130 PerfettoTraceMonitor.newBuilder()
131 .enableTransactionsTrace()
132 .build()
133 .withTracing(resultReaderProvider = { buildResultReader(it) }, predicate)
134 val bytes = readBytes(reader, debugFile)
135 return TraceProcessorSession.loadPerfettoTrace(bytes) { session ->
136 TransactionsTraceParser().parse(session)
137 }
138 }
139
140 /**
141 * Acquire the [WindowManagerTrace] and [LayersTrace] with the device state changes that happen when
142 * executing the commands defined in the [predicate].
143 *
144 * @param traceMonitors List of monitors to start
145 * @param debugFile File to keep a copy of the parsed trace, leave null to not keep any copies
146 * @param predicate Commands to execute
147 * @throws UnsupportedOperationException If tracing is already activated
148 */
withTracingnull149 fun withTracing(
150 traceMonitors: List<TraceMonitor> =
151 mutableListOf<TraceMonitor>()
152 .apply {
153 if (!android.tracing.Flags.perfettoWmTracing()) {
154 this.add(WindowManagerTraceMonitor())
155 }
156 }
<lambda>null157 .apply {
158 val monitorBuilder =
159 PerfettoTraceMonitor.newBuilder().enableLayersTrace().enableTransactionsTrace()
160
161 if (android.tracing.Flags.perfettoWmTracing()) {
162 monitorBuilder.enableWindowManagerTrace()
163 }
164
165 this.add(monitorBuilder.build())
166 }
167 .toList(),
168 debugFile: File? = null,
169 predicate: Runnable,
170 ): Reader {
171 val tmpFile = File.createTempFile("recordTraces", "")
172 val writer =
173 ResultWriter()
174 .forScenario(ScenarioBuilder().forClass(tmpFile.name).build())
175 .withOutputDir(tmpFile.parentFile)
176
177 try {
<lambda>null178 traceMonitors.forEach { it.start() }
179 predicate.run()
180 } finally {
<lambda>null181 traceMonitors.forEach { it.stop(writer) }
182 }
183 val reader = ResultReaderWithLru(writer.write(), SERVICE_TRACE_CONFIG)
184 debugFile?.writeBytes(reader.artifact.readBytes())
185 return reader
186 }
187
188 /**
189 * Acquire the [WindowManagerTrace] and [LayersTrace] with the device state changes that happen when
190 * executing the commands defined in the [predicate].
191 *
192 * @param predicate Commands to execute
193 * @return a pair containing the WM and SF traces
194 * @throws UnsupportedOperationException If tracing is already activated
195 */
recordTracesnull196 fun recordTraces(predicate: Runnable): ResultReader {
197 return PerfettoTraceMonitor.newBuilder()
198 .enableLayersTrace()
199 .enableWindowManagerTrace()
200 .build()
201 .withTracing(resultReaderProvider = { buildResultReader(it) }, predicate)
202 }
203