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.traces.monitors 18 19 import android.tools.io.TraceType 20 import android.tools.traces.executeShellCommand 21 import com.android.internal.protolog.common.LogLevel 22 import java.io.File 23 import java.util.concurrent.locks.ReentrantLock 24 import perfetto.protos.PerfettoConfig 25 import perfetto.protos.PerfettoConfig.DataSourceConfig 26 import perfetto.protos.PerfettoConfig.SurfaceFlingerLayersConfig 27 import perfetto.protos.PerfettoConfig.SurfaceFlingerTransactionsConfig 28 import perfetto.protos.PerfettoConfig.TraceConfig 29 import perfetto.protos.PerfettoConfig.WindowManagerConfig 30 31 /* Captures traces from Perfetto. */ 32 open class PerfettoTraceMonitor(val config: TraceConfig) : TraceMonitor() { 33 override val traceType = TraceType.PERFETTO 34 override val isEnabled 35 get() = perfettoPid != null 36 37 private var perfettoPid: Int? = null 38 private var traceFile: File? = null 39 private val PERFETTO_TRACES_DIR = File("/data/misc/perfetto-traces") 40 captureDumpnull41 fun captureDump(): File { 42 doStart() 43 return doStop() 44 } 45 doStartnull46 override fun doStart() { 47 val fileName = File.createTempFile(traceType.fileName, "").name 48 traceFile = PERFETTO_TRACES_DIR.resolve(fileName) 49 50 val command = 51 "perfetto --background-wait" + " --config -" + " --out ${traceFile?.absolutePath}" 52 val stdout = String(executeShellCommand(command, config.toByteArray())) 53 val pid = stdout.trim().toInt() 54 55 perfettoPid = pid 56 allPerfettoPidsLock.lock() 57 try { 58 allPerfettoPids.add(pid) 59 } finally { 60 allPerfettoPidsLock.unlock() 61 } 62 } 63 doStopnull64 override fun doStop(): File { 65 require(isEnabled) { "Attempted to stop disabled trace monitor" } 66 killPerfettoProcess(requireNotNull(perfettoPid)) 67 waitPerfettoProcessExits(requireNotNull(perfettoPid)) 68 perfettoPid = null 69 return requireNotNull(traceFile) 70 } 71 72 class Builder { 73 private val DEFAULT_SF_LAYER_FLAGS = 74 listOf( 75 SurfaceFlingerLayersConfig.TraceFlag.TRACE_FLAG_INPUT, 76 SurfaceFlingerLayersConfig.TraceFlag.TRACE_FLAG_COMPOSITION, 77 SurfaceFlingerLayersConfig.TraceFlag.TRACE_FLAG_VIRTUAL_DISPLAYS, 78 ) 79 80 private val dataSourceConfigs = mutableSetOf<DataSourceConfig>() 81 private var incrementalTimeoutMs: Int? = null 82 <lambda>null83 fun enableImeTrace(): Builder = apply { enableCustomTrace(createImeDataSourceConfig()) } 84 enableLayersTracenull85 fun enableLayersTrace(flags: List<SurfaceFlingerLayersConfig.TraceFlag>? = null): Builder = 86 apply { 87 enableCustomTrace( 88 createLayersTraceDataSourceConfig(flags ?: DEFAULT_SF_LAYER_FLAGS) 89 ) 90 } 91 enableLayersDumpnull92 fun enableLayersDump(flags: List<SurfaceFlingerLayersConfig.TraceFlag>? = null): Builder = 93 apply { 94 enableCustomTrace(createLayersDumpDataSourceConfig(flags ?: DEFAULT_SF_LAYER_FLAGS)) 95 } 96 <lambda>null97 fun enableTransactionsTrace(): Builder = apply { 98 enableCustomTrace(createTransactionsDataSourceConfig()) 99 } 100 <lambda>null101 fun enableTransitionsTrace(): Builder = apply { 102 enableCustomTrace(createTransitionsDataSourceConfig()) 103 } 104 105 data class ProtoLogGroupOverride( 106 val groupName: String, 107 val logFrom: LogLevel, 108 val collectStackTrace: Boolean, 109 ) 110 <lambda>null111 fun enableProtoLog(dataSourceName: String): Builder = apply { 112 enableProtoLog(logAll = true, dataSourceName = dataSourceName) 113 } 114 115 @JvmOverloads enableProtoLognull116 fun enableProtoLog( 117 logAll: Boolean = true, 118 groupOverrides: List<ProtoLogGroupOverride> = emptyList(), 119 dataSourceName: String = PROTOLOG_DATA_SOURCE, 120 ): Builder = apply { 121 enableCustomTrace( 122 createProtoLogDataSourceConfig(logAll, null, groupOverrides, dataSourceName) 123 ) 124 } 125 126 @JvmOverloads enableProtoLognull127 fun enableProtoLog( 128 defaultLogFrom: LogLevel, 129 groupOverrides: List<ProtoLogGroupOverride> = emptyList(), 130 dataSourceName: String = PROTOLOG_DATA_SOURCE, 131 ): Builder = apply { 132 enableCustomTrace( 133 createProtoLogDataSourceConfig( 134 false, 135 defaultLogFrom, 136 groupOverrides, 137 dataSourceName, 138 ) 139 ) 140 } 141 enableViewCaptureTracenull142 fun enableViewCaptureTrace(): Builder = apply { 143 val config = DataSourceConfig.newBuilder().setName(VIEWCAPTURE_DATA_SOURCE).build() 144 enableCustomTrace(config) 145 } 146 147 @JvmOverloads enableWindowManagerTracenull148 fun enableWindowManagerTrace( 149 logFrequency: WindowManagerConfig.LogFrequency = 150 WindowManagerConfig.LogFrequency.LOG_FREQUENCY_FRAME, 151 dataSourceName: String = WINDOWMANAGER_DATA_SOURCE, 152 ): Builder = apply { 153 val config = 154 DataSourceConfig.newBuilder() 155 .setName(dataSourceName) 156 .setWindowmanagerConfig( 157 WindowManagerConfig.newBuilder() 158 .setLogLevel(WindowManagerConfig.LogLevel.LOG_LEVEL_VERBOSE) 159 .setLogFrequency(logFrequency) 160 .build() 161 ) 162 .build() 163 164 enableCustomTrace(config) 165 } 166 167 @JvmOverloads enableWindowManagerDumpnull168 fun enableWindowManagerDump(dataSourceName: String = WINDOWMANAGER_DATA_SOURCE): Builder = 169 apply { 170 val config = 171 DataSourceConfig.newBuilder() 172 .setName(dataSourceName) 173 .setWindowmanagerConfig( 174 WindowManagerConfig.newBuilder() 175 .setLogLevel(WindowManagerConfig.LogLevel.LOG_LEVEL_VERBOSE) 176 .setLogFrequency( 177 WindowManagerConfig.LogFrequency.LOG_FREQUENCY_SINGLE_DUMP 178 ) 179 .build() 180 ) 181 .build() 182 183 enableCustomTrace(config) 184 } 185 <lambda>null186 fun enableCustomTrace(dataSourceConfig: DataSourceConfig): Builder = apply { 187 dataSourceConfigs.add(dataSourceConfig) 188 } 189 <lambda>null190 fun setIncrementalTimeout(timeoutMs: Int) = apply { incrementalTimeoutMs = timeoutMs } 191 buildnull192 fun build(): PerfettoTraceMonitor { 193 val configBuilder = 194 TraceConfig.newBuilder() 195 .setDurationMs(0) 196 .addBuffers( 197 TraceConfig.BufferConfig.newBuilder() 198 .setSizeKb(TRACE_BUFFER_SIZE_KB) 199 .build() 200 ) 201 202 for (dataSourceConfig in dataSourceConfigs) { 203 configBuilder.addDataSources(createDataSourceWithConfig(dataSourceConfig)) 204 } 205 206 val incrementalTimeoutMs = incrementalTimeoutMs 207 if (incrementalTimeoutMs != null) { 208 configBuilder.setIncrementalStateConfig( 209 TraceConfig.IncrementalStateConfig.newBuilder() 210 .setClearPeriodMs(incrementalTimeoutMs) 211 ) 212 } 213 214 return PerfettoTraceMonitor(config = configBuilder.build()) 215 } 216 createImeDataSourceConfignull217 private fun createImeDataSourceConfig(): DataSourceConfig { 218 return DataSourceConfig.newBuilder().setName(IME_DATA_SOURCE).build() 219 } 220 createLayersTraceDataSourceConfignull221 private fun createLayersTraceDataSourceConfig( 222 traceFlags: List<SurfaceFlingerLayersConfig.TraceFlag> 223 ): DataSourceConfig { 224 return DataSourceConfig.newBuilder() 225 .setName(SF_LAYERS_DATA_SOURCE) 226 .setSurfaceflingerLayersConfig( 227 SurfaceFlingerLayersConfig.newBuilder() 228 .setMode(SurfaceFlingerLayersConfig.Mode.MODE_ACTIVE) 229 .apply { traceFlags.forEach { addTraceFlags(it) } } 230 .build() 231 ) 232 .build() 233 } 234 createLayersDumpDataSourceConfignull235 private fun createLayersDumpDataSourceConfig( 236 traceFlags: List<SurfaceFlingerLayersConfig.TraceFlag> 237 ): DataSourceConfig { 238 return DataSourceConfig.newBuilder() 239 .setName(SF_LAYERS_DATA_SOURCE) 240 .setSurfaceflingerLayersConfig( 241 SurfaceFlingerLayersConfig.newBuilder() 242 .setMode(SurfaceFlingerLayersConfig.Mode.MODE_DUMP) 243 .apply { traceFlags.forEach { addTraceFlags(it) } } 244 .build() 245 ) 246 .build() 247 } 248 createTransactionsDataSourceConfignull249 private fun createTransactionsDataSourceConfig(): DataSourceConfig { 250 return DataSourceConfig.newBuilder() 251 .setName(SF_TRANSACTIONS_DATA_SOURCE) 252 .setSurfaceflingerTransactionsConfig( 253 SurfaceFlingerTransactionsConfig.newBuilder() 254 .setMode(SurfaceFlingerTransactionsConfig.Mode.MODE_ACTIVE) 255 .build() 256 ) 257 .build() 258 } 259 createTransitionsDataSourceConfignull260 private fun createTransitionsDataSourceConfig(): DataSourceConfig { 261 return DataSourceConfig.newBuilder().setName(TRANSITIONS_DATA_SOURCE).build() 262 } 263 createProtoLogDataSourceConfignull264 private fun createProtoLogDataSourceConfig( 265 logAll: Boolean, 266 logFrom: LogLevel?, 267 groupOverrides: List<ProtoLogGroupOverride>, 268 dataSourceName: String = PROTOLOG_DATA_SOURCE, 269 ): DataSourceConfig { 270 val protoLogConfigBuilder = PerfettoConfig.ProtoLogConfig.newBuilder() 271 272 if (logAll) { 273 protoLogConfigBuilder.setTracingMode( 274 PerfettoConfig.ProtoLogConfig.TracingMode.ENABLE_ALL 275 ) 276 } 277 278 if (logFrom != null) { 279 protoLogConfigBuilder.setDefaultLogFromLevel( 280 PerfettoConfig.ProtoLogLevel.forNumber(logFrom.id) 281 ) 282 } 283 284 for (groupOverride in groupOverrides) { 285 protoLogConfigBuilder.addGroupOverrides( 286 PerfettoConfig.ProtoLogGroup.newBuilder() 287 .setGroupName(groupOverride.groupName) 288 .setLogFrom( 289 PerfettoConfig.ProtoLogLevel.forNumber(groupOverride.logFrom.id) 290 ) 291 .setCollectStacktrace(groupOverride.collectStackTrace) 292 ) 293 } 294 295 return DataSourceConfig.newBuilder() 296 .setName(dataSourceName) 297 .setProtologConfig(protoLogConfigBuilder) 298 .build() 299 } 300 createDataSourceWithConfignull301 private fun createDataSourceWithConfig( 302 dataSourceConfig: DataSourceConfig 303 ): TraceConfig.DataSource { 304 return TraceConfig.DataSource.newBuilder().setConfig(dataSourceConfig).build() 305 } 306 } 307 308 companion object { 309 private const val TRACE_BUFFER_SIZE_KB = 1024 * 1024 310 311 private const val IME_DATA_SOURCE = "android.inputmethod" 312 private const val SF_LAYERS_DATA_SOURCE = "android.surfaceflinger.layers" 313 private const val SF_TRANSACTIONS_DATA_SOURCE = "android.surfaceflinger.transactions" 314 private const val TRANSITIONS_DATA_SOURCE = "com.android.wm.shell.transition" 315 private const val PROTOLOG_DATA_SOURCE = "android.protolog" 316 private const val VIEWCAPTURE_DATA_SOURCE = "android.viewcapture" 317 private const val WINDOWMANAGER_DATA_SOURCE = "android.windowmanager" 318 319 private val allPerfettoPids = mutableListOf<Int>() 320 private val allPerfettoPidsLock = ReentrantLock() 321 322 @JvmStatic newBuildernull323 fun newBuilder(): Builder { 324 return Builder() 325 } 326 327 @JvmStatic stopAllSessionsnull328 fun stopAllSessions() { 329 allPerfettoPidsLock.lock() 330 try { 331 allPerfettoPids.forEach { killPerfettoProcess(it) } 332 allPerfettoPids.forEach { waitPerfettoProcessExits(it) } 333 allPerfettoPids.clear() 334 } finally { 335 allPerfettoPidsLock.unlock() 336 } 337 } 338 339 @JvmStatic killPerfettoProcessnull340 fun killPerfettoProcess(pid: Int) { 341 if (isPerfettoProcessUp(pid)) { 342 executeShellCommand("kill $pid") 343 } 344 } 345 waitPerfettoProcessExitsnull346 private fun waitPerfettoProcessExits(pid: Int) { 347 while (true) { 348 if (!isPerfettoProcessUp(pid)) { 349 break 350 } 351 Thread.sleep(50) 352 } 353 } 354 isPerfettoProcessUpnull355 private fun isPerfettoProcessUp(pid: Int): Boolean { 356 val out = String(executeShellCommand("ps -p $pid -o CMD")) 357 return out.contains("perfetto") 358 } 359 } 360 } 361