From 25f0beed129469690756e8e3560c9c1751bac67f Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 6 Jul 2021 13:25:38 +0200 Subject: [PATCH] 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. --- .../arguments/K2JVMCompilerArguments.kt | 6 +-- .../common/profiling/AsyncProfilerWrapper.kt | 52 +++++++++++++------ compiler/testData/cli/jvm/extraHelp.out | 6 +-- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index 08bdf6856f8..a38f024a0d7 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -452,9 +452,9 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise"" @Argument( value = "-Xprofile", valueDescription = "", - 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=/async-profiler/build/libasyncProfiler.so:event=cpu,interval=1ms,threads,start,framebuf=50000000:" ) var profileCompilerCommand: String? by NullableStringFreezableVar(null) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/profiling/AsyncProfilerWrapper.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/profiling/AsyncProfilerWrapper.kt index 1f4b5e92d37..279e06b2941 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/common/profiling/AsyncProfilerWrapper.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/profiling/AsyncProfilerWrapper.kt @@ -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 { } } -} \ No newline at end of file + + 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) + } + } + } +} diff --git a/compiler/testData/cli/jvm/extraHelp.out b/compiler/testData/cli/jvm/extraHelp.out index d93c49e7c3d..0032ef30011 100644 --- a/compiler/testData/cli/jvm/extraHelp.out +++ b/compiler/testData/cli/jvm/extraHelp.out @@ -100,9 +100,9 @@ where advanced options include: 0 means use a thread per processor core. Default value is 1 -Xprofile= - 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=/async-profiler/build/libasyncProfiler.so:event=cpu,interval=1ms,threads,start,framebuf=50000000: -Xrepeat= Debug option: Repeats modules compilation times -Xsam-conversions={class|indy} Select code generation scheme for SAM conversions.