Load async-profiler.jar if possible when using -Xprofile

Instead of requiring it to be on the compiler classpath.

This will make it much easier to profile the Kotlin compiler daemon in
Gradle, by just specifying a compiler argument instead of also manually
patching the compiler jar.
This commit is contained in:
Alexander Udalov
2021-07-06 13:25:38 +02:00
parent 787ce6335c
commit 25f0beed12
3 changed files with 41 additions and 23 deletions
@@ -452,9 +452,9 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
@Argument(
value = "-Xprofile",
valueDescription = "<profilerPath:command:outputDir>",
description = "Debug option: Run compiler with async profiler, save snapshots to outputDir, command is passed to async-profiler on start\n" +
"You'll have to provide async-profiler.jar on classpath to use this\n" +
"profilerPath is a path to libasyncProfiler.so\n" +
description = "Debug option: Run compiler with async profiler and save snapshots to `outputDir`; `command` is passed to async-profiler on start.\n" +
"`profilerPath` is a path to libasyncProfiler.so; async-profiler.jar should be on the compiler classpath.\n" +
"If it's not on the classpath, the compiler will attempt to load async-profiler.jar from the containing directory of profilerPath.\n" +
"Example: -Xprofile=<PATH_TO_ASYNC_PROFILER>/async-profiler/build/libasyncProfiler.so:event=cpu,interval=1ms,threads,start,framebuf=50000000:<SNAPSHOT_DIR_PATH>"
)
var profileCompilerCommand: String? by NullableStringFreezableVar(null)
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.cli.common.profiling
import java.io.File
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.net.URLClassLoader
interface AsyncProfilerReflected {
fun execute(command: String): String
@@ -14,24 +16,22 @@ interface AsyncProfilerReflected {
val version: String
}
object AsyncProfilerHelper {
private val profilerClass = Class.forName("one.profiler.AsyncProfiler")
private val getInstanceHandle =
MethodHandles.lookup().findStatic(profilerClass, "getInstance", MethodType.methodType(profilerClass, String::class.java))
private val executeHandle =
MethodHandles.lookup().findVirtual(
profilerClass,
"execute",
MethodType.methodType(String::class.java, String::class.java)
)
private val stopHandle =
MethodHandles.lookup().findVirtual(profilerClass, "stop", MethodType.methodType(Void.TYPE))
private val getVersionHandle =
MethodHandles.lookup().findVirtual(profilerClass, "getVersion", MethodType.methodType(String::class.java))
fun getInstance(libPath: String?): AsyncProfilerReflected {
val profilerClass = loadAsyncProfilerClass(libPath)
val getInstanceHandle =
MethodHandles.lookup().findStatic(profilerClass, "getInstance", MethodType.methodType(profilerClass, String::class.java))
val executeHandle =
MethodHandles.lookup().findVirtual(
profilerClass,
"execute",
MethodType.methodType(String::class.java, String::class.java)
)
val stopHandle =
MethodHandles.lookup().findVirtual(profilerClass, "stop", MethodType.methodType(Void.TYPE))
val getVersionHandle =
MethodHandles.lookup().findVirtual(profilerClass, "getVersion", MethodType.methodType(String::class.java))
val instance = getInstanceHandle.invokeWithArguments(libPath)
return object : AsyncProfilerReflected {
private val boundExecute = executeHandle.bindTo(instance)
@@ -51,4 +51,22 @@ object AsyncProfilerHelper {
}
}
}
private fun loadAsyncProfilerClass(libPath: String?): Class<*> {
val fqName = "one.profiler.AsyncProfiler"
return try {
Class.forName(fqName)
} catch (e: ClassNotFoundException) {
if (libPath == null) throw e
else {
val directory = File(libPath).parentFile
check(directory.isDirectory) { directory }
val apiJar = directory.resolve("async-profiler.jar")
if (!apiJar.exists())
error("To use async-profiler, either add it to the compiler classpath, or put async-profiler.jar at this path: $apiJar")
val classLoader = URLClassLoader(arrayOf(apiJar.toURI().toURL()), null)
classLoader.loadClass(fqName)
}
}
}
}
+3 -3
View File
@@ -100,9 +100,9 @@ where advanced options include:
0 means use a thread per processor core.
Default value is 1
-Xprofile=<profilerPath:command:outputDir>
Debug option: Run compiler with async profiler, save snapshots to outputDir, command is passed to async-profiler on start
You'll have to provide async-profiler.jar on classpath to use this
profilerPath is a path to libasyncProfiler.so
Debug option: Run compiler with async profiler and save snapshots to `outputDir`; `command` is passed to async-profiler on start.
`profilerPath` is a path to libasyncProfiler.so; async-profiler.jar should be on the compiler classpath.
If it's not on the classpath, the compiler will attempt to load async-profiler.jar from the containing directory of profilerPath.
Example: -Xprofile=<PATH_TO_ASYNC_PROFILER>/async-profiler/build/libasyncProfiler.so:event=cpu,interval=1ms,threads,start,framebuf=50000000:<SNAPSHOT_DIR_PATH>
-Xrepeat=<number> Debug option: Repeats modules compilation <number> times
-Xsam-conversions={class|indy} Select code generation scheme for SAM conversions.