| /* |
| * Copyright 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.benchmark |
| |
| import android.os.Build |
| import android.os.Debug |
| import android.util.Log |
| import java.io.File |
| |
| /** |
| * Profiler abstraction used for the timing stage. |
| * |
| * Controlled externally by `androidx.benchmark.profiling.mode` |
| * Subclasses are objects, as these generally refer to device or process global state. For |
| * example, things like whether the simpleperf process is running, or whether the runtime is |
| * capturing method trace. |
| * |
| * Note: flags on this class would be simpler if we either had a 'Default'/'Noop' profiler, or a |
| * wrapper extension function (e.g. `fun Profiler? .requiresSingleMeasurementIteration`). We |
| * avoid these however, in order to avoid the runtime visiting a new class in the hot path, when |
| * switching from warmup -> timing phase, when [start] would be called. |
| */ |
| internal sealed class Profiler { |
| abstract fun start(traceUniqueName: String) |
| abstract fun stop() |
| |
| /** |
| * Measure exactly one loop (one repeat, one iteration). |
| * |
| * Generally only set for tracing profilers. |
| */ |
| open val requiresSingleMeasurementIteration = false |
| |
| /** |
| * Generally only set for sampling profilers. |
| */ |
| open val requiresExtraRuntime = false |
| |
| /** |
| * Currently, debuggable is required to support studio-connected profiling. |
| * |
| * Remove this once stable Studio supports profileable. |
| */ |
| open val requiresDebuggable = false |
| |
| /** |
| * Connected modes don't need dir, since library isn't doing the capture. |
| */ |
| open val requiresLibraryOutputDir = true |
| |
| companion object { |
| const val CONNECTED_PROFILING_SLEEP_MS = 20_000L |
| |
| fun getByName(name: String): Profiler? = mapOf( |
| "MethodSampling" to MethodSampling, |
| "MethodTracing" to MethodTracing, |
| |
| "ConnectedAllocation" to ConnectedAllocation, |
| "ConnectedSampling" to ConnectedSampling, |
| |
| // Below are compat codepaths for old names. Remove before 1.1 stable. |
| "Method" to MethodTracing, |
| "Sampled" to MethodSampling, |
| "ConnectedSampled" to ConnectedSampling |
| ) |
| .mapKeys { it.key.toLowerCase() }[name.toLowerCase()] |
| } |
| } |
| |
| internal fun startRuntimeMethodTracing(traceUniqueName: String, sampled: Boolean) { |
| val traceType = if (sampled) "methodSampling" else "methodTracing" |
| val path = File( |
| Arguments.testOutputDir, |
| "$traceUniqueName-$traceType.trace" |
| ).absolutePath |
| |
| Log.d(BenchmarkState.TAG, "Profiling output file: $path") |
| InstrumentationResults.reportAdditionalFileToCopy("profiling_trace", path) |
| |
| val bufferSize = 16 * 1024 * 1024 |
| if (sampled && |
| Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP |
| ) { |
| Debug.startMethodTracingSampling(path, bufferSize, 100) |
| } else { |
| Debug.startMethodTracing(path, bufferSize, 0) |
| } |
| } |
| |
| internal fun stopRuntimeMethodTracing() { |
| Debug.stopMethodTracing() |
| } |
| |
| internal object MethodSampling : Profiler() { |
| override fun start(traceUniqueName: String) { |
| startRuntimeMethodTracing(traceUniqueName = traceUniqueName, sampled = true) |
| } |
| |
| override fun stop() { |
| stopRuntimeMethodTracing() |
| } |
| |
| override val requiresExtraRuntime: Boolean = true |
| } |
| |
| internal object MethodTracing : Profiler() { |
| override fun start(traceUniqueName: String) { |
| startRuntimeMethodTracing(traceUniqueName = traceUniqueName, sampled = false) |
| } |
| |
| override fun stop() { |
| stopRuntimeMethodTracing() |
| } |
| |
| override val requiresSingleMeasurementIteration: Boolean = true |
| } |
| |
| internal object ConnectedAllocation : Profiler() { |
| override fun start(traceUniqueName: String) { |
| Thread.sleep(CONNECTED_PROFILING_SLEEP_MS) |
| } |
| |
| override fun stop() { |
| Thread.sleep(CONNECTED_PROFILING_SLEEP_MS) |
| } |
| |
| override val requiresSingleMeasurementIteration: Boolean = true |
| override val requiresDebuggable: Boolean = true |
| override val requiresLibraryOutputDir: Boolean = false |
| } |
| |
| internal object ConnectedSampling : Profiler() { |
| override fun start(traceUniqueName: String) { |
| Thread.sleep(CONNECTED_PROFILING_SLEEP_MS) |
| } |
| |
| override fun stop() { |
| Thread.sleep(CONNECTED_PROFILING_SLEEP_MS) |
| } |
| |
| override val requiresDebuggable: Boolean = true |
| override val requiresLibraryOutputDir: Boolean = false |
| } |