Profiling and repeat support for JVM CLI Compiler

This commit is contained in:
simon.ogorodnik
2020-06-03 21:44:17 +03:00
parent ce1ef6c159
commit cd9273028b
9 changed files with 189 additions and 9 deletions
@@ -352,6 +352,23 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
)
var noKotlinNothingValueException: Boolean by FreezableVar(false)
@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" +
"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)
@Argument(
value = "-Xrepeat",
valueDescription = "<number>",
description = "Debug option: Repeats modules compilation <number> times"
)
var repeatCompileModules: String? by NullableStringFreezableVar(null)
override fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
val result = super.configureAnalysisFlags(collector)
result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics
@@ -45,6 +45,8 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
protected abstract val performanceManager: CommonCompilerPerformanceManager
protected open fun createPerformanceManager(arguments: A): CommonCompilerPerformanceManager = performanceManager
// Used in CompilerRunnerUtil#invokeExecMethod, in Eclipse plugin (KotlinCLICompiler) and in kotlin-gradle-plugin (GradleCompilerRunner)
fun execAndOutputXml(errStream: PrintStream, services: Services, vararg args: String): ExitCode {
return exec(errStream, services, MessageRenderer.XML, args)
@@ -56,7 +58,7 @@ abstract class CLICompiler<A : CommonCompilerArguments> : CLITool<A>() {
}
public override fun execImpl(messageCollector: MessageCollector, services: Services, arguments: A): ExitCode {
val performanceManager = performanceManager
val performanceManager = createPerformanceManager(arguments)
if (arguments.reportPerf || arguments.dumpPerf != null) {
performanceManager.enableCollectingPerformanceStatistics()
}
@@ -48,6 +48,9 @@ public class CLIConfigurationKeys {
public static final CompilerConfigurationKey<PhaseConfig> PHASE_CONFIG =
CompilerConfigurationKey.create("phase configuration");
public static final CompilerConfigurationKey<Integer> REPEAT_COMPILE_MODULES =
CompilerConfigurationKey.create("debug key for profiling, repeats compileModules");
private CLIConfigurationKeys() {
}
}
@@ -87,4 +87,6 @@ abstract class CommonCompilerPerformanceManager(private val presentableName: Str
appendln("$presentableName performance report")
measurements.map { it.render() }.sorted().forEach { appendln(it) }
}.toByteArray()
open fun notifyRepeat(total: Int, number: Int) {}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.cli.common.profiling
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
interface AsyncProfilerReflected {
fun execute(command: String): String
fun stop()
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 instance = getInstanceHandle.invokeWithArguments(libPath)
return object : AsyncProfilerReflected {
private val boundExecute = executeHandle.bindTo(instance)
private val boundStop = stopHandle.bindTo(instance)
private val boundGetVersion = getVersionHandle.bindTo(instance)
override fun execute(command: String): String {
return boundExecute.invokeWithArguments(command) as String
}
override fun stop() {
boundStop.invokeWithArguments()
}
override val version: String
get() = boundGetVersion.invokeWithArguments() as String
}
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.cli.common.profiling
import org.jetbrains.kotlin.cli.common.CommonCompilerPerformanceManager
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
class ProfilingCompilerPerformanceManager(
profilerPath: String,
val command: String,
val outputDir: File
) : CommonCompilerPerformanceManager("Profiling") {
private val profiler = AsyncProfilerHelper.getInstance(profilerPath)
private val runDate = Date()
private val formatter = SimpleDateFormat("yyyy-MM-dd__HH-mm")
private var active = false
init {
startProfiling()
}
private fun startProfiling() {
profiler.execute(command)
active = true
}
private fun stopProfiling() {
if (active) profiler.stop()
active = false
}
private fun restartProfiling() {
stopProfiling()
startProfiling()
}
private fun dumpProfile(postfix: String) {
outputDir.mkdirs()
val outputFile = outputDir.resolve("snapshot-${formatter.format(runDate)}-$postfix.collapsed")
outputFile.writeText(profiler.execute("collapsed"))
active = false
}
override fun notifyRepeat(total: Int, number: Int) {
dumpProfile("repeat$number")
restartProfiling()
}
override fun notifyCompilationFinished() {
dumpProfile("final")
stopProfiling()
}
companion object {
fun create(profileCompilerArgument: String): ProfilingCompilerPerformanceManager {
val (path, command, outputDir) = profileCompilerArgument.split(":", limit = 3)
return ProfilingCompilerPerformanceManager(path, command, File(outputDir))
}
}
}
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageUtil
import org.jetbrains.kotlin.cli.common.messages.OutputMessageUtil
import org.jetbrains.kotlin.cli.common.modules.ModuleBuilder
import org.jetbrains.kotlin.cli.common.modules.ModuleChunk
import org.jetbrains.kotlin.cli.common.profiling.ProfilingCompilerPerformanceManager
import org.jetbrains.kotlin.cli.jvm.compiler.CompileEnvironmentUtil
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
@@ -50,8 +51,6 @@ import java.io.File
class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
override val performanceManager = K2JVMCompilerPerformanceManager()
override fun doExecute(
arguments: K2JVMCompilerArguments,
configuration: CompilerConfiguration,
@@ -60,6 +59,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
): ExitCode {
val messageCollector = configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
configuration.putIfNotNull(CLIConfigurationKeys.REPEAT_COMPILE_MODULES, arguments.repeatCompileModules?.toIntOrNull())
configuration.put(CLIConfigurationKeys.PHASE_CONFIG, createPhaseConfig(jvmPhases, arguments, messageCollector))
if (!configuration.configureJdkHome(arguments)) return COMPILATION_ERROR
@@ -219,7 +219,7 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
val environment = KotlinCoreEnvironment.createForProduction(rootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
performanceManager.notifyCompilerInitialized()
configuration[CLIConfigurationKeys.PERF_MANAGER]?.notifyCompilerInitialized()
return if (messageCollector.hasErrors()) null else environment
}
@@ -263,6 +263,14 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
}
}
override val performanceManager: CommonCompilerPerformanceManager
get() = error("Unsupported")
override fun createPerformanceManager(arguments: K2JVMCompilerArguments): CommonCompilerPerformanceManager {
val argument = arguments.profileCompilerCommand ?: return K2JVMCompilerPerformanceManager()
return ProfilingCompilerPerformanceManager.create(argument)
}
}
@@ -162,9 +162,24 @@ object KotlinToJVMBytecodeCompiler {
}
}
internal fun compileModules(environment: KotlinCoreEnvironment, buildFile: File?, chunk: List<Module>): Boolean {
internal fun compileModules(
environment: KotlinCoreEnvironment,
buildFile: File?,
chunk: List<Module>,
repeat: Boolean = false
): Boolean {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val repeats = environment.configuration[CLIConfigurationKeys.REPEAT_COMPILE_MODULES]
if (repeats != null && !repeat) {
val performanceManager = environment.configuration[CLIConfigurationKeys.PERF_MANAGER]
return (0 until repeats).map {
val result = compileModules(environment, buildFile, chunk, repeat = true)
performanceManager?.notifyRepeat(repeats, it)
result
}.last()
}
val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
for (module in chunk) {
moduleVisibilityManager.addModule(module)
@@ -289,6 +304,8 @@ object KotlinToJVMBytecodeCompiler {
private fun compileModulesUsingFrontendIR(environment: KotlinCoreEnvironment, buildFile: File?, chunk: List<Module>): Boolean {
val project = environment.project
val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER)
Extensions.getArea(project)
.getExtensionPoint(PsiElementFinder.EP_NAME)
.unregisterExtension(JavaElementFinder::class.java)
@@ -297,6 +314,7 @@ object KotlinToJVMBytecodeCompiler {
val localFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val outputs = newLinkedHashMapWithExpectedSize<Module, GenerationState>(chunk.size)
for (module in chunk) {
performanceManager?.notifyAnalysisStarted()
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val ktFiles = module.getSourceFiles(environment, localFileSystem, chunk.size > 1, buildFile)
@@ -349,6 +367,11 @@ object KotlinToJVMBytecodeCompiler {
}
}
val debugTargetDescription = "target " + module.getModuleName() + "-" + module.getModuleType() + " "
val codeLines = environment.countLinesOfCode(ktFiles)
performanceManager?.notifyAnalysisFinished(ktFiles.size, codeLines, debugTargetDescription)
performanceManager?.notifyGenerationStarted()
val signaturer = IdSignatureDescriptor(JvmManglerDesc())
val (moduleFragment, symbolTable, sourceManager, components) =
@@ -379,8 +402,7 @@ object KotlinToJVMBytecodeCompiler {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
val performanceManager = environment.configuration.get(CLIConfigurationKeys.PERF_MANAGER)
performanceManager?.notifyGenerationStarted()
generationState.beforeCompile()
codegenFactory.generateModuleInFrontendIRMode(
generationState, moduleFragment, symbolTable, sourceManager
@@ -391,8 +413,8 @@ object KotlinToJVMBytecodeCompiler {
generationState.factory.done()
performanceManager?.notifyGenerationFinished(
ktFiles.size,
environment.countLinesOfCode(ktFiles),
additionalDescription = "target " + module.getModuleName() + "-" + module.getModuleType() + " "
codeLines,
additionalDescription = debugTargetDescription
)
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
+6
View File
@@ -74,6 +74,12 @@ where advanced options include:
-Xno-param-assertions Don't generate not-null assertions on parameters of methods accessible from Java
-Xno-receiver-assertions Don't generate not-null assertion for extension receiver arguments of platform types
-Xno-use-ir Do not use the IR backend. Useful for a custom-built compiler where IR backend is enabled by default
-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
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
-Xsanitize-parentheses Transform '(' and ')' in method names to some other character sequence.
This mode can BREAK BINARY COMPATIBILITY and is only supposed to be used to workaround
problems with parentheses in identifiers on certain platforms