diff --git a/integration/src/test/kotlin/kotlinx/benchmark/integration/JvmProfilerTest.kt b/integration/src/test/kotlin/kotlinx/benchmark/integration/JvmProfilerTest.kt new file mode 100644 index 00000000..2b3b52f7 --- /dev/null +++ b/integration/src/test/kotlin/kotlinx/benchmark/integration/JvmProfilerTest.kt @@ -0,0 +1,74 @@ +package kotlinx.benchmark.integration + +import kotlin.test.* + +class JvmProfilerTest : GradleTest() { + + @Test + fun testGcProfiler() { + val runner = project("kotlin-multiplatform") { + configuration("gcProfiler") { + iterations = 1 + iterationTime = 100 + iterationTimeUnit = "ms" + advanced("jvmProfiler", "gc") + } + } + + runner.run("jvmGcProfilerBenchmark") { + assertOutputContains("gc.alloc.rate") + assertOutputContains("BUILD SUCCESSFUL") + } + } + + @Test + fun testStackProfilerEffect() { + val runner = project("kotlin-multiplatform") { + configuration("stackProfiler") { + iterations = 1 + iterationTime = 100 + iterationTimeUnit = "ms" + advanced("jvmProfiler", "stack") + } + } + + runner.run("jvmStackProfilerBenchmark") { + assertOutputContains("stack") + assertOutputContains("BUILD SUCCESSFUL") + } + } + + @Test + fun testClProfiler() { + val runner = project("kotlin-multiplatform") { + configuration("clProfiler") { + iterations = 1 + iterationTime = 100 + iterationTimeUnit = "ms" + advanced("jvmProfiler", "cl") + } + } + + runner.run("jvmClProfilerBenchmark") { + assertOutputContains("class.unload.norm") + assertOutputContains("BUILD SUCCESSFUL") + } + } + + @Test + fun testCompProfilerEffect() { + val runner = project("kotlin-multiplatform") { + configuration("compProfiler") { + iterations = 1 + iterationTime = 100 + iterationTimeUnit = "ms" + advanced("jvmProfiler", "comp") + } + } + + runner.run("jvmCompProfilerBenchmark") { + assertOutputContains("compiler.time.profiled") + assertOutputContains("BUILD SUCCESSFUL") + } + } +} diff --git a/integration/src/test/kotlin/kotlinx/benchmark/integration/OptionsValidationTest.kt b/integration/src/test/kotlin/kotlinx/benchmark/integration/OptionsValidationTest.kt index 9cb63fdf..a96f4a84 100644 --- a/integration/src/test/kotlin/kotlinx/benchmark/integration/OptionsValidationTest.kt +++ b/integration/src/test/kotlin/kotlinx/benchmark/integration/OptionsValidationTest.kt @@ -226,6 +226,13 @@ class OptionsValidationTest : GradleTest() { iterationTimeUnit = "ms" advanced("jsUseBridge", "x") } + + configuration("invalidJvmProfiler") { + iterations = 1 + iterationTime = 100 + iterationTimeUnit = "ms" + advanced("jvmProfiler", "x") + } } runner.runAndFail("blankAdvancedConfigNameBenchmark") { @@ -246,6 +253,9 @@ class OptionsValidationTest : GradleTest() { runner.runAndFail("invalidJsUseBridgeBenchmark") { assertOutputContains("Invalid value for 'jsUseBridge': 'x'. Expected a Boolean value.") } + runner.runAndFail("invalidJvmProfiler") { + assertOutputContains("Invalid value for 'jvmProfiler': 'x'. Accepted values: ${ValidOptions.jvmProfilers.joinToString(", ")}.") + } } } @@ -260,4 +270,5 @@ private object ValidOptions { ) val modes = setOf("thrpt", "avgt", "Throughput", "AverageTime") val nativeForks = setOf("perBenchmark", "perIteration") + val jvmProfilers = setOf("stack", "gc", "cl", "comp", "perf", "perfnorm", "perfasm", "xperfasm", " dtraceasm") } \ No newline at end of file diff --git a/plugin/main/src/kotlinx/benchmark/gradle/Utils.kt b/plugin/main/src/kotlinx/benchmark/gradle/Utils.kt index c07d4a11..509b0627 100644 --- a/plugin/main/src/kotlinx/benchmark/gradle/Utils.kt +++ b/plugin/main/src/kotlinx/benchmark/gradle/Utils.kt @@ -228,7 +228,12 @@ private fun validateConfig(config: BenchmarkConfiguration) { "jsUseBridge" -> require(value is Boolean) { "Invalid value for 'jsUseBridge': '$value'. Expected a Boolean value." } - else -> throw IllegalArgumentException("Invalid advanced option name: '$param'. Accepted options: \"nativeFork\", \"nativeGCAfterIteration\", \"jvmForks\", \"jsUseBridge\".") + "jvmProfiler" -> { + require(value.toString() in ValidOptions.jvmProfilers) { + "Invalid value for 'jvmProfiler': '$value'. Accepted values: ${ValidOptions.jvmProfilers.joinToString(", ")}." + } + } + else -> throw IllegalArgumentException("Invalid advanced option name: '$param'. Accepted options: \"nativeFork\", \"nativeGCAfterIteration\", \"jvmForks\", \"jsUseBridge\", \"jvmProfiler\".") } } } @@ -244,6 +249,7 @@ private object ValidOptions { ) val modes = setOf("thrpt", "avgt", "Throughput", "AverageTime") val nativeForks = setOf("perBenchmark", "perIteration") + val jvmProfilers = setOf("stack", "gc", "cl", "comp", "perf", "perfnorm", "perfasm", "xperfasm", " dtraceasm") } internal val Gradle.isConfigurationCacheAvailable diff --git a/runtime/jvmMain/src/kotlinx/benchmark/jvm/JvmBenchmarkRunner.kt b/runtime/jvmMain/src/kotlinx/benchmark/jvm/JvmBenchmarkRunner.kt index d1b8a5ca..2d7cc4e6 100644 --- a/runtime/jvmMain/src/kotlinx/benchmark/jvm/JvmBenchmarkRunner.kt +++ b/runtime/jvmMain/src/kotlinx/benchmark/jvm/JvmBenchmarkRunner.kt @@ -56,6 +56,21 @@ fun main(args: Array) { } } + val profilerName = config.advanced["jvmProfiler"] + when (profilerName) { + "gc" -> jmhOptions.addProfiler("gc") + "stack" -> jmhOptions.addProfiler("stack") + "cl" -> jmhOptions.addProfiler("cl") + "comp" -> jmhOptions.addProfiler("comp") + "perf" -> jmhOptions.addProfiler("perf") + "perfnorm" -> jmhOptions.addProfiler("perfnorm") + "perfasm" -> jmhOptions.addProfiler("perfasm") + "xperfasm" -> jmhOptions.addProfiler("xperfasm") + "dtraceasm" -> jmhOptions.addProfiler("dtraceasm") + null -> {} + else -> throw IllegalArgumentException("Invalid value for 'jvmProfiler': $profilerName. Accepted values: gc, stack, cl, comp") + } + val reportFormat = ResultFormatType.valueOf(config.reportFormat.uppercase()) val reporter = BenchmarkProgress.create(config.traceFormat) val output = JmhOutputFormat(reporter, config.name)