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.monitors 18 19 import android.tools.ScenarioBuilder 20 import android.tools.function.Supplier 21 import android.tools.io.TraceType 22 import android.tools.traces.io.IResultData 23 import android.tools.traces.io.IoUtils 24 import android.tools.traces.io.ResultReader 25 import android.tools.traces.io.ResultWriter 26 import java.io.File 27 import kotlin.io.path.createTempDirectory 28 29 /** 30 * Base class for monitors containing common logic to read the trace as a byte array and save the 31 * trace to another location. 32 */ 33 abstract class TraceMonitor : ITransitionMonitor { 34 abstract val isEnabled: Boolean 35 abstract val traceType: TraceType 36 37 protected abstract fun doStart() 38 39 protected abstract fun doStop(): File 40 41 protected open fun doStopTraces(): Map<TraceType, File> = mapOf(traceType to doStop()) 42 43 final override fun start() { 44 android.tools.withTracing("${this::class.simpleName}#start") { 45 validateStart() 46 doStart() 47 } 48 } 49 50 open fun validateStart() { 51 if (this.isEnabled) { 52 throw UnsupportedOperationException( 53 "${traceType.name} trace already running. " + 54 "This is likely due to chained 'withTracing' calls." 55 ) 56 } 57 } 58 59 /** Stops monitor. */ 60 override fun stop(writer: ResultWriter) { 61 val artifacts = 62 try { 63 android.tools.withTracing("${this::class.simpleName}#stop") { 64 doStopTraces() 65 .map { (key, value) -> key to moveTraceFileToTmpDir(value) } 66 .toMap() 67 } 68 } catch (e: Throwable) { 69 throw RuntimeException( 70 "Could not stop ${traceType.name} trace and save it to ${traceType.fileName}", 71 e, 72 ) 73 } 74 artifacts.forEach { (key, value) -> writer.addTraceResult(key, value) } 75 } 76 77 private fun moveTraceFileToTmpDir(sourceFile: File): File { 78 val newFile = File.createTempFile(sourceFile.name, "") 79 IoUtils.moveFile(sourceFile, newFile) 80 require(newFile.exists()) { "Unable to save trace file $newFile" } 81 return newFile 82 } 83 84 /** 85 * Uses [writer] to write the trace generated by executing the commands defined by [predicate]. 86 * 87 * @param writer Write to use to write the collected traces 88 * @param predicate Commands to execute 89 * @throws UnsupportedOperationException If tracing is already activated 90 */ 91 fun withTracing(writer: ResultWriter, predicate: Runnable) { 92 android.tools.withTracing("${this::class.simpleName}#withTracing") { 93 try { 94 this.start() 95 predicate.run() 96 } finally { 97 this.stop(writer) 98 } 99 } 100 } 101 102 /** 103 * Acquires the trace generated when executing the commands defined in the [predicate]. 104 * 105 * @param predicate Commands to execute 106 * @param resultReaderProvider Predicate to generate new result readers 107 * @param 108 * @throws UnsupportedOperationException If tracing is already activated 109 */ 110 fun withTracing( 111 resultReaderProvider: Supplier<IResultData, ResultReader>, 112 predicate: Runnable, 113 ): ResultReader { 114 val writer = createWriter() 115 withTracing(writer, predicate) 116 val result = writer.write() 117 return resultReaderProvider.get(result) 118 } 119 120 private fun createWriter(): ResultWriter { 121 val className = this::class.simpleName ?: error("Missing class name for $this") 122 val scenario = ScenarioBuilder().forClass(className).build() 123 val tmpDir = createTempDirectory("withTracing").toFile() 124 return ResultWriter().forScenario(scenario).withOutputDir(tmpDir) 125 } 126 127 companion object { 128 @JvmStatic protected val TRACE_DIR = File("/data/misc/wmtrace/") 129 } 130 } 131