• 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.monitors
18 
19 import android.tools.common.ScenarioBuilder
20 import android.tools.common.io.TraceType
21 import android.tools.device.traces.TRACE_CONFIG_REQUIRE_CHANGES
22 import android.tools.device.traces.io.IoUtils
23 import android.tools.device.traces.io.ResultReader
24 import android.tools.device.traces.io.ResultWriter
25 import java.io.File
26 import kotlin.io.path.createTempDirectory
27 
28 /**
29  * Base class for monitors containing common logic to read the trace as a byte array and save the
30  * trace to another location.
31  */
32 abstract class TraceMonitor : ITransitionMonitor {
33     abstract val isEnabled: Boolean
34     abstract val traceType: TraceType
doStartnull35     protected abstract fun doStart()
36     protected abstract fun doStop(): File
37 
38     final override fun start() {
39         validateStart()
40         doStart()
41     }
42 
validateStartnull43     open fun validateStart() {
44         if (this.isEnabled) {
45             throw UnsupportedOperationException(
46                 "Trace already running. " + "This is likely due to chained 'withTracing' calls."
47             )
48         }
49     }
50 
51     /** Stops monitor. */
stopnull52     final override fun stop(writer: ResultWriter) {
53         val artifact =
54             try {
55                 val srcFile = doStop()
56                 moveTraceFileToTmpDir(srcFile)
57             } catch (e: Throwable) {
58                 throw RuntimeException("Could not stop trace", e)
59             }
60         writer.addTraceResult(traceType, artifact)
61     }
62 
moveTraceFileToTmpDirnull63     private fun moveTraceFileToTmpDir(sourceFile: File): File {
64         val newFile = File.createTempFile(sourceFile.name, "")
65         IoUtils.moveFile(sourceFile, newFile)
66         require(newFile.exists()) { "Unable to save trace file $newFile" }
67         return newFile
68     }
69 
70     /**
71      * Uses [writer] to write the trace generated by executing the commands defined by [predicate].
72      *
73      * @param writer Write to use to write the collected traces
74      * @param predicate Commands to execute
75      * @throws UnsupportedOperationException If tracing is already activated
76      */
withTracingnull77     fun withTracing(writer: ResultWriter, predicate: () -> Unit) {
78         try {
79             this.start()
80             predicate()
81         } finally {
82             this.stop(writer)
83         }
84     }
85 
86     /**
87      * Acquires the trace generated when executing the commands defined in the [predicate].
88      *
89      * @param predicate Commands to execute
90      * @throws UnsupportedOperationException If tracing is already activated
91      */
withTracingnull92     fun withTracing(predicate: () -> Unit): ByteArray {
93         val writer = createWriter()
94         withTracing(writer, predicate)
95         val result = writer.write()
96         val reader = ResultReader(result, TRACE_CONFIG_REQUIRE_CHANGES)
97         val bytes = reader.readBytes(traceType) ?: error("Missing trace $traceType")
98         result.artifact.deleteIfExists()
99         return bytes
100     }
101 
createWriternull102     private fun createWriter(): ResultWriter {
103         val className = this::class.simpleName ?: error("Missing class name for $this")
104         val scenario = ScenarioBuilder().forClass(className).build()
105         val tmpDir = createTempDirectory("withTracing").toFile()
106         return ResultWriter().forScenario(scenario).withOutputDir(tmpDir)
107     }
108 
109     companion object {
110         @JvmStatic protected val TRACE_DIR = File("/data/misc/wmtrace/")
111     }
112 }
113