Skip to content

Commit fa6dd27

Browse files
committed
Add --view option to perfetto
1 parent dc9c567 commit fa6dd27

File tree

2 files changed

+70
-46
lines changed

2 files changed

+70
-46
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ open class PerfettoCommand(
205205
help = "Path to a custom Perfetto config file (text proto format). If not specified, uses the default config."
206206
).file(mustExist = true, canBeDir = false)
207207

208+
private val view by option(
209+
"--view",
210+
help = "Open the trace in Perfetto UI after collection. If multiple iterations, opens the first trace."
211+
).flag()
212+
208213
override fun run() {
209214
baseOpts.setup()
210215
val configPath = perfettoConfigFile?.absolutePath
@@ -215,6 +220,20 @@ open class PerfettoCommand(
215220
override val delegate: RunnerValues get() = runnerOpts
216221
}
217222
runBenchmark(baseOpts, optsWithProfiler)
223+
224+
if (view) {
225+
val outputDir = File(optsWithProfiler.hostOutputDir)
226+
val traceFile = outputDir.walkTopDown()
227+
.filter { it.isFile && it.extension == "perfetto-trace" }
228+
.sortedBy { it.name }
229+
.firstOrNull()
230+
if (traceFile != null) {
231+
log { "Opening trace in Perfetto UI: ${traceFile.absolutePath}" }
232+
ViewCommand.openTraceInPerfetto(traceFile)
233+
} else {
234+
log { "No .perfetto-trace files found in ${outputDir.absolutePath}" }
235+
}
236+
}
218237
}
219238

220239
open fun runBenchmark(baseVals: BaseValues, runnerVals: RunnerValues) {

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

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -35,67 +35,71 @@ class ViewCommand(
3535

3636
override fun run() {
3737
baseOpts.setup()
38+
openTraceInPerfetto(traceFile, port)
39+
}
3840

39-
val traceBytes = traceFile.readBytes()
40-
val fileName = traceFile.name
41+
companion object {
42+
fun openTraceInPerfetto(traceFile: File, port: Int = 9001) {
43+
val traceBytes = traceFile.readBytes()
44+
val fileName = traceFile.name
4145

42-
val html = generateHtml(fileName)
43-
val shutdownLatch = CountDownLatch(1)
46+
val html = generateHtml(fileName)
47+
val shutdownLatch = CountDownLatch(1)
4448

45-
val server = HttpServer.create(InetSocketAddress("127.0.0.1", port), 0)
46-
val actualPort = server.address.port
49+
val server = HttpServer.create(InetSocketAddress("127.0.0.1", port), 0)
50+
val actualPort = server.address.port
4751

48-
server.createContext("/") { exchange ->
49-
log { "${exchange.requestMethod} ${exchange.requestURI}" }
50-
val response = html.toByteArray()
51-
exchange.responseHeaders.add("Content-Type", "text/html; charset=utf-8")
52-
exchange.sendResponseHeaders(200, response.size.toLong())
53-
exchange.responseBody.use { it.write(response) }
54-
}
52+
server.createContext("/") { exchange ->
53+
log { "${exchange.requestMethod} ${exchange.requestURI}" }
54+
val response = html.toByteArray()
55+
exchange.responseHeaders.add("Content-Type", "text/html; charset=utf-8")
56+
exchange.sendResponseHeaders(200, response.size.toLong())
57+
exchange.responseBody.use { it.write(response) }
58+
}
5559

56-
server.createContext("/trace") { exchange ->
57-
log { "${exchange.requestMethod} ${exchange.requestURI}" }
58-
exchange.responseHeaders.add("Content-Type", "application/octet-stream")
59-
exchange.sendResponseHeaders(200, traceBytes.size.toLong())
60-
exchange.responseBody.use { it.write(traceBytes) }
61-
}
60+
server.createContext("/trace") { exchange ->
61+
log { "${exchange.requestMethod} ${exchange.requestURI}" }
62+
exchange.responseHeaders.add("Content-Type", "application/octet-stream")
63+
exchange.sendResponseHeaders(200, traceBytes.size.toLong())
64+
exchange.responseBody.use { it.write(traceBytes) }
65+
}
6266

63-
server.createContext("/shutdown") { exchange ->
64-
log { "${exchange.requestMethod} ${exchange.requestURI}" }
65-
exchange.sendResponseHeaders(204, -1)
66-
exchange.close()
67-
shutdownLatch.countDown()
68-
}
67+
server.createContext("/shutdown") { exchange ->
68+
log { "${exchange.requestMethod} ${exchange.requestURI}" }
69+
exchange.sendResponseHeaders(204, -1)
70+
exchange.close()
71+
shutdownLatch.countDown()
72+
}
6973

70-
server.executor = null
71-
server.start()
74+
server.executor = null
75+
server.start()
7276

73-
val localUrl = "http://127.0.0.1:$actualPort/"
77+
val localUrl = "http://127.0.0.1:$actualPort/"
7478

75-
log { "Serving loader page at $localUrl" }
76-
log { "Click the button in the browser to open the trace in Perfetto UI." }
79+
log { "Serving loader page at $localUrl" }
80+
log { "Click the button in the browser to open the trace in Perfetto UI." }
7781

78-
openInBrowser(localUrl)
82+
openInBrowser(localUrl)
7983

80-
shutdownLatch.await()
81-
log { "Trace sent. Shutting down server..." }
82-
server.stop(0)
83-
}
84+
shutdownLatch.await()
85+
log { "Trace sent. Shutting down server..." }
86+
server.stop(0)
87+
}
8488

85-
private fun openInBrowser(url: String) {
86-
val os = System.getProperty("os.name").lowercase()
87-
val command = when {
88-
os.contains("mac") -> arrayOf("open", url)
89-
os.contains("linux") -> arrayOf("xdg-open", url)
90-
else -> {
91-
log { "Open manually: $url" }
92-
return
89+
private fun openInBrowser(url: String) {
90+
val os = System.getProperty("os.name").lowercase()
91+
val command = when {
92+
os.contains("mac") -> arrayOf("open", url)
93+
os.contains("linux") -> arrayOf("xdg-open", url)
94+
else -> {
95+
log { "Open manually: $url" }
96+
return
97+
}
9398
}
99+
ProcessBuilder(*command).start()
94100
}
95-
ProcessBuilder(*command).start()
96-
}
97101

98-
private fun generateHtml(fileName: String): String {
102+
private fun generateHtml(fileName: String): String {
99103
return """
100104
<!DOCTYPE html>
101105
<html>
@@ -208,5 +212,6 @@ class ViewCommand(
208212
</body>
209213
</html>
210214
""".trimIndent()
215+
}
211216
}
212217
}

0 commit comments

Comments
 (0)