Skip to content

Commit bc1aa0e

Browse files
committed
Implement perfetto command
1 parent 7b0bb16 commit bc1aa0e

File tree

3 files changed

+147
-26
lines changed

3 files changed

+147
-26
lines changed

demo/src/main/kotlin/com/squareup/francis/demo/Main.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ class DemoRunnerOptions(
1717
fun main(rawArgs: Array<String>) = runFrancis(
1818
rawArgs = rawArgs,
1919
name = "francis-demo",
20-
instrumentationOptionsFactory = { config -> DemoRunnerOptions(releaseDir, config) },
20+
runnerOptionsFactory = { config -> DemoRunnerOptions(releaseDir, config) },
2121
)

hostSdk/src/main/kotlin/com/squareup/francis/Commands.kt

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,26 @@ open class CompareCommand : CliktCommand(name = "compare") {
191191
}
192192
}
193193

194-
open class PerfettoCommand : CliktCommand(name = "perfetto") {
195-
override fun help(context: Context) = "Collect a raw Perfetto trace (not yet implemented)"
194+
open class PerfettoCommand(
195+
runnerOpts: RunnerOptions,
196+
private val benchCommandFactory: (RunnerOptions) -> BenchCommand,
197+
) : CliktCommand(name = "perfetto") {
198+
override fun help(context: Context) = "Collect a raw Perfetto trace"
199+
200+
protected val baseOpts by runnerOpts.base
201+
protected val runnerOpts by runnerOpts
196202

197203
override fun run() {
198-
throw NotImplementedError("Perfetto tracing is not yet implemented")
204+
baseOpts.setup()
205+
val optsWithProfiler = object : RunnerValues by runnerOpts {
206+
override val profiler: String = "perfetto"
207+
override val delegate: RunnerValues get() = runnerOpts
208+
}
209+
runBenchmark(baseOpts, optsWithProfiler)
210+
}
211+
212+
open fun runBenchmark(baseVals: BaseValues, runnerVals: RunnerValues) {
213+
benchCommandFactory(runnerOpts).runBenchmark(baseVals, runnerVals)
199214
}
200215
}
201216

@@ -249,7 +264,7 @@ fun runFrancis(
249264
benchCommandFactory: (RunnerOptions) -> BenchCommand = { opts -> BenchCommand(opts) },
250265
simplePerfCommandFactory: (RunnerOptions) -> SimpleperfCommand = { opts -> SimpleperfCommand(opts, benchCommandFactory) },
251266
compareCommandFactory: () -> CompareCommand = { CompareCommand() },
252-
perfettoCommandFactory: () -> PerfettoCommand = { PerfettoCommand() },
267+
perfettoCommandFactory: (RunnerOptions) -> PerfettoCommand = { opts -> PerfettoCommand(opts, benchCommandFactory) },
253268
) = pithyMain(rawArgs) {
254269
val baseConfig = BaseConfig()
255270

@@ -270,7 +285,7 @@ fun runFrancis(
270285
benchCommandFactory(runnerOptionsFactory(baseConfig)),
271286
AbCommand(baseConfig, runnerOptionsFactory, benchCommandFactory, rawArgs.toList(), entrypointName = name),
272287
compareCommandFactory(),
273-
perfettoCommandFactory(),
288+
perfettoCommandFactory(runnerOptionsFactory(baseConfig)),
274289
simplePerfCommandFactory(runnerOptionsFactory(baseConfig)),
275290
)
276291
try {

instrumentationSdk/src/main/kotlin/com/squareup/francis/FrancisBenchmarkRule.kt

Lines changed: 126 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,38 @@ package com.squareup.francis
22

33
import androidx.benchmark.ExperimentalBenchmarkConfigApi
44
import androidx.benchmark.ExperimentalConfig
5+
import androidx.benchmark.macro.ExperimentalMetricApi
6+
import androidx.benchmark.macro.Metric.Measurement
7+
import androidx.benchmark.macro.TraceMetric
58
import androidx.benchmark.perfetto.ExperimentalPerfettoCaptureApi
9+
import androidx.benchmark.perfetto.PerfettoConfig
610
import androidx.benchmark.macro.CompilationMode
711
import androidx.benchmark.macro.MacrobenchmarkScope
812
import androidx.benchmark.macro.Metric
913
import androidx.benchmark.macro.StartupMode
1014
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
15+
import androidx.benchmark.traceprocessor.TraceProcessor
1116
import androidx.test.platform.app.InstrumentationRegistry
1217
import org.junit.rules.TestRule
1318
import org.junit.runner.Description
1419
import org.junit.runners.model.Statement
1520

21+
@OptIn(ExperimentalMetricApi::class)
22+
private class NoOpMetric : TraceMetric() {
23+
override fun getMeasurements(
24+
captureInfo: Metric.CaptureInfo,
25+
traceSession: TraceProcessor.Session
26+
): List<Measurement> = listOf(Measurement("francis.placeholder", 0.0))
27+
}
28+
1629
class FrancisBenchmarkRule : TestRule {
1730
private val macrobenchmarkRule = MacrobenchmarkRule()
1831

1932
override fun apply(base: Statement, description: Description): Statement {
2033
return macrobenchmarkRule.apply(base, description)
2134
}
2235

36+
@OptIn(ExperimentalBenchmarkConfigApi::class, ExperimentalPerfettoCaptureApi::class)
2337
fun measureRepeated(
2438
packageName: String,
2539
metrics: List<Metric>,
@@ -29,17 +43,34 @@ class FrancisBenchmarkRule : TestRule {
2943
setupBlock: MacrobenchmarkScope.() -> Unit = {},
3044
measureBlock: MacrobenchmarkScope.() -> Unit
3145
) {
32-
macrobenchmarkRule.measureRepeated(
33-
packageName = packageName,
34-
metrics = metrics,
35-
compilationMode = compilationMode,
36-
startupMode = startupMode,
37-
iterations = francisIterations ?: iterations,
38-
setupBlock = setupBlock,
39-
measureBlock = wrapMeasureBlock(measureBlock)
40-
)
46+
if (francisProfiler == "perfetto") {
47+
@OptIn(ExperimentalMetricApi::class)
48+
macrobenchmarkRule.measureRepeated(
49+
packageName = packageName,
50+
metrics = listOf(NoOpMetric()),
51+
iterations = francisIterations ?: iterations,
52+
experimentalConfig = ExperimentalConfig(
53+
perfettoConfig = createPerfettoConfig(packageName)
54+
),
55+
compilationMode = compilationMode,
56+
startupMode = startupMode,
57+
setupBlock = setupBlock,
58+
measureBlock = measureBlock
59+
)
60+
} else {
61+
macrobenchmarkRule.measureRepeated(
62+
packageName = packageName,
63+
metrics = metrics,
64+
compilationMode = compilationMode,
65+
startupMode = startupMode,
66+
iterations = francisIterations ?: iterations,
67+
setupBlock = setupBlock,
68+
measureBlock = wrapMeasureBlock(measureBlock)
69+
)
70+
}
4171
}
4272

73+
@OptIn(ExperimentalPerfettoCaptureApi::class)
4374
@ExperimentalBenchmarkConfigApi
4475
fun measureRepeated(
4576
packageName: String,
@@ -51,17 +82,32 @@ class FrancisBenchmarkRule : TestRule {
5182
setupBlock: MacrobenchmarkScope.() -> Unit = {},
5283
measureBlock: MacrobenchmarkScope.() -> Unit
5384
) {
54-
@OptIn(ExperimentalPerfettoCaptureApi::class)
55-
macrobenchmarkRule.measureRepeated(
56-
packageName = packageName,
57-
metrics = metrics,
58-
iterations = francisIterations ?: iterations,
59-
experimentalConfig = experimentalConfig,
60-
compilationMode = compilationMode,
61-
startupMode = startupMode,
62-
setupBlock = setupBlock,
63-
measureBlock = wrapMeasureBlock(measureBlock)
64-
)
85+
if (francisProfiler == "perfetto") {
86+
@OptIn(ExperimentalMetricApi::class)
87+
macrobenchmarkRule.measureRepeated(
88+
packageName = packageName,
89+
metrics = listOf(NoOpMetric()),
90+
iterations = francisIterations ?: iterations,
91+
experimentalConfig = ExperimentalConfig(
92+
perfettoConfig = createPerfettoConfig(packageName)
93+
),
94+
compilationMode = compilationMode,
95+
startupMode = startupMode,
96+
setupBlock = setupBlock,
97+
measureBlock = measureBlock
98+
)
99+
} else {
100+
macrobenchmarkRule.measureRepeated(
101+
packageName = packageName,
102+
metrics = metrics,
103+
iterations = francisIterations ?: iterations,
104+
experimentalConfig = experimentalConfig,
105+
compilationMode = compilationMode,
106+
startupMode = startupMode,
107+
setupBlock = setupBlock,
108+
measureBlock = wrapMeasureBlock(measureBlock)
109+
)
110+
}
65111
}
66112

67113
private fun wrapMeasureBlock(
@@ -103,5 +149,65 @@ class FrancisBenchmarkRule : TestRule {
103149
private val simpleperfCallGraph: String? by lazy {
104150
args.getString("simpleperfCallGraph")
105151
}
152+
153+
@OptIn(ExperimentalPerfettoCaptureApi::class)
154+
private fun createPerfettoConfig(packageName: String): PerfettoConfig {
155+
return PerfettoConfig.Text(DEFAULT_PERFETTO_CONFIG.replace("{PACKAGE}", packageName))
156+
}
157+
158+
private val DEFAULT_PERFETTO_CONFIG = """
159+
buffers {
160+
size_kb: 65536
161+
fill_policy: RING_BUFFER
162+
}
163+
buffers {
164+
size_kb: 4096
165+
fill_policy: RING_BUFFER
166+
}
167+
data_sources {
168+
config {
169+
name: "linux.ftrace"
170+
target_buffer: 0
171+
ftrace_config {
172+
ftrace_events: "sched/sched_switch"
173+
ftrace_events: "sched/sched_wakeup"
174+
ftrace_events: "sched/sched_waking"
175+
ftrace_events: "sched/sched_blocked_reason"
176+
ftrace_events: "power/cpu_frequency"
177+
ftrace_events: "power/cpu_idle"
178+
ftrace_events: "power/suspend_resume"
179+
atrace_categories: "am"
180+
atrace_categories: "dalvik"
181+
atrace_categories: "gfx"
182+
atrace_categories: "input"
183+
atrace_categories: "pm"
184+
atrace_categories: "res"
185+
atrace_categories: "sched"
186+
atrace_categories: "view"
187+
atrace_categories: "wm"
188+
atrace_apps: "{PACKAGE}"
189+
}
190+
}
191+
}
192+
data_sources {
193+
config {
194+
name: "linux.process_stats"
195+
target_buffer: 1
196+
process_stats_config {
197+
scan_all_processes_on_start: true
198+
proc_stats_poll_ms: 1000
199+
}
200+
}
201+
}
202+
data_sources {
203+
config {
204+
name: "android.packages_list"
205+
target_buffer: 1
206+
}
207+
}
208+
write_into_file: true
209+
file_write_period_ms: 2500
210+
flush_period_ms: 5000
211+
""".trimIndent()
106212
}
107213
}

0 commit comments

Comments
 (0)