Skip to content

Commit c8d0e30

Browse files
committed
added --java-home and --jvm-args CLI arguments to run the coverage and prioritization commands (and the underlying Java calls) with different Java versions than the one bencher is run with
1 parent 06601fb commit c8d0e30

File tree

21 files changed

+377
-241
lines changed

21 files changed

+377
-241
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ Its features are:
1818
* Java version 17 (set `$JAVA_HOME`)
1919
* For command line interface (CLI) usage:
2020
* Create fat JAR with `./gradlew shadowJar`
21-
* Created JAR is `build/libs/bencher-0.4.1-all.jar`, in the following referred to as `bencher.jar`
21+
* Created JAR is `build/libs/bencher-0.5.0-all.jar`, in the following referred to as `bencher.jar`
2222
* For API usage:
2323
* Run `./gradlew publishToMavenLocal`
2424
* Use in your project with
2525
groupID = `ch.uzh.ifi.seal`,
2626
artifactID = `bencher`,
2727
and
28-
version = `0.4.1`.
28+
version = `0.5.0`.
2929

3030

3131

build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ plugins {
2525
}
2626

2727
group = "ch.uzh.ifi.seal"
28-
version = "0.4.1"
28+
version = "0.5.0"
2929

3030
application {
3131
// applicationDefaultJvmArgs = listOf("-Xms6G", "-Xmx8G")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package ch.uzh.ifi.seal.bencher
2+
3+
data class JavaSettings(
4+
val home: String?,
5+
val jvmArgs: String?,
6+
) {
7+
fun homePair(): Pair<String, String>? =
8+
home?.let { Pair(JAVA_HOME, it) }
9+
10+
companion object {
11+
const val JAVA_HOME = "JAVA_HOME"
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package ch.uzh.ifi.seal.bencher
2+
3+
fun <K, V> mapOfNotNull(pair: Pair<K, V>?): Map<K, V> {
4+
return pair?.let { mapOf(it) } ?: emptyMap()
5+
}

src/main/kotlin/ch/uzh/ifi/seal/bencher/StringExtensions.kt

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ fun String.runCommand(
1313
timeout: Duration,
1414
env: Map<String, String> = mapOf()
1515
): Triple<Boolean, String?, String?> {
16-
1716
try {
1817
val parts = this.split("\\s".toRegex())
1918
val procBuilder = ProcessBuilder(*parts.toTypedArray())

src/main/kotlin/ch/uzh/ifi/seal/bencher/analysis/coverage/dyn/AbstractDynamicCoverage.kt

+86-60
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ package ch.uzh.ifi.seal.bencher.analysis.coverage.dyn
22

33
import arrow.core.Either
44
import arrow.core.getOrElse
5-
import ch.uzh.ifi.seal.bencher.Benchmark
6-
import ch.uzh.ifi.seal.bencher.Method
5+
import ch.uzh.ifi.seal.bencher.*
76
import ch.uzh.ifi.seal.bencher.analysis.JarHelper
87
import ch.uzh.ifi.seal.bencher.analysis.coverage.CoverageExecutor
98
import ch.uzh.ifi.seal.bencher.analysis.coverage.Coverages
109
import ch.uzh.ifi.seal.bencher.analysis.coverage.computation.*
1110
import ch.uzh.ifi.seal.bencher.analysis.finder.MethodFinder
12-
import ch.uzh.ifi.seal.bencher.runCommand
1311
import org.apache.logging.log4j.LogManager
1412
import org.apache.logging.log4j.Logger
1513
import java.io.File
@@ -22,10 +20,13 @@ import java.time.LocalDateTime
2220

2321
abstract class AbstractDynamicCoverage(
2422
private val benchmarkFinder: MethodFinder<Benchmark>,
23+
private val javaSettings: JavaSettings,
2524
private val oneCoverageForParameterizedBenchmarks: Boolean = true,
26-
private val timeOut: Duration = Duration.ofMinutes(10)
25+
private val timeOut: Duration = Duration.ofMinutes(10),
2726
) : CoverageExecutor {
2827

28+
private val env: Map<String, String> = mapOfNotNull(javaSettings.homePair())
29+
2930
override fun get(jar: Path): Either<String, Coverages> {
3031
val ebs = benchmarkFinder.all()
3132
val bs: List<Benchmark> = ebs.getOrElse {
@@ -54,7 +55,7 @@ abstract class AbstractDynamicCoverage(
5455
private fun coverages(jar: Path, b: Benchmark): Either<String, List<Pair<Benchmark, Coverage>>> {
5556
val bs = b.parameterizedBenchmarks()
5657

57-
val p = Files.createTempDirectory(tmpDirPrefix)
58+
val p = Files.createTempDirectory(TMP_DIR_PREFIX)
5859
val tmpDir = File(p.toUri())
5960

6061
val total = bs.size
@@ -80,35 +81,37 @@ abstract class AbstractDynamicCoverage(
8081
}
8182

8283
private fun replaceFrom(b: Benchmark, rs: Coverage): Coverage =
83-
b.toPlainMethod().let { pb ->
84-
Coverage(
85-
of = b,
86-
unitResults = rs.all().mapNotNull {
87-
when (it) {
88-
is NotCovered -> null
89-
is PossiblyCovered -> CUF.possiblyCovered(
90-
of = pb,
91-
unit = it.unit,
92-
level = it.level,
93-
probability = it.probability
94-
)
95-
is Covered -> CUF.covered(
96-
of = pb,
97-
unit = it.unit,
98-
level = it.level
99-
)
100-
}
101-
}.toSet()
102-
)
103-
}
84+
b.toPlainMethod().let { pb ->
85+
Coverage(
86+
of = b,
87+
unitResults = rs.all().mapNotNull {
88+
when (it) {
89+
is NotCovered -> null
90+
is PossiblyCovered -> CUF.possiblyCovered(
91+
of = pb,
92+
unit = it.unit,
93+
level = it.level,
94+
probability = it.probability,
95+
)
96+
is Covered -> CUF.covered(
97+
of = pb,
98+
unit = it.unit,
99+
level = it.level,
100+
)
101+
}
102+
}.toSet(),
103+
)
104+
}
104105

105106
private fun coverageBench(jar: Path, i: Int, total: Int, tmpDir: File, b: Benchmark): Pair<Benchmark, Coverage>? {
106107
val cs = cmdStr(jar, b)
107108

109+
log.info(cs)
110+
108111
log.debug("Param bench $b: ${i + 1}/$total; '$cs'")
109112

110113
val l = logTimesParam(b, i, total, "coverage for parameterized benchmark")
111-
val ers = exec(cs, jar, tmpDir, b)
114+
val ers = exec(cs, env, jar, tmpDir, b)
112115
return try {
113116
ers
114117
.mapLeft {
@@ -125,14 +128,14 @@ abstract class AbstractDynamicCoverage(
125128
}
126129

127130
private fun coverageParam(jar: Path, total: Int, tmpDir: File, bs: List<Benchmark>): List<Pair<Benchmark, Coverage>> =
128-
bs.mapIndexedNotNull { i, pb -> coverageBench(jar, i, total, tmpDir, pb) }
131+
bs.mapIndexedNotNull { i, pb -> coverageBench(jar, i, total, tmpDir, pb) }
129132

130133
private fun logTimesParam(b: Benchmark, i: Int, total: Int, text: String): () -> Unit =
131-
if (total <= 1) {
132-
{}
133-
} else {
134-
logTimes(b, i, total, text)
135-
}
134+
if (total <= 1) {
135+
{}
136+
} else {
137+
logTimes(b, i, total, text)
138+
}
136139

137140
private fun logTimes(b: Benchmark, i: Int, total: Int, text: String): () -> Unit {
138141
log.info("start $text $b (${i + 1}/$total)")
@@ -143,17 +146,17 @@ abstract class AbstractDynamicCoverage(
143146
}
144147
}
145148

146-
private fun exec(cmd: String, jar: Path, dir: File, b: Benchmark): Either<String, Coverage> {
147-
val (ok, out, err) = cmd.runCommand(dir, timeOut)
149+
private fun exec(cmd: String, env: Map<String, String>, jar: Path, dir: File, b: Benchmark): Either<String, Coverage> {
150+
val (ok, out, err) = cmd.runCommand(dir, timeOut, env)
148151
if (!ok) {
149152
return Either.Left("Execution of '$cmd' did not finish within $timeOut")
150153
}
151154

152-
if (out != null && out.isNotBlank()) {
155+
if (!out.isNullOrBlank()) {
153156
log.debug("Process out: $out")
154157
}
155158

156-
if (err != null && err.isNotBlank()) {
159+
if (!err.isNullOrBlank()) {
157160
log.debug("Process err: $err")
158161
}
159162

@@ -215,15 +218,45 @@ abstract class AbstractDynamicCoverage(
215218
}
216219
}
217220

218-
private fun cmdStr(jar: Path, b: Benchmark): String =
219-
if (b.jmhParams.isEmpty()) {
220-
String.format(cmd, jar, jvmArgs(b), benchName(b))
221-
} else {
222-
String.format(cmdParam, jar, jvmArgs(b), jmhParams(b), benchName(b))
223-
}
221+
// For use in shell (e.g., bash) the constructed command string needs escaping of
222+
// (1) dollar-signs ($) and
223+
// (2) double-quotes (")
224+
// around the -jvmArgs argument and the JMH benchmark regex
225+
private fun cmdStr(jar: Path, b: Benchmark): String {
226+
val cmdList = mutableListOf<String>()
227+
// java
228+
cmdList.add(JAVA_CMD)
229+
230+
// JVM arguments to java command
231+
javaSettings.jvmArgs?.let { cmdList.add(it) }
232+
233+
// -jar jar
234+
cmdList.add(JAR_ARG)
235+
cmdList.add(jar.toString())
236+
237+
// -jvmArgs=jvmArgs(b)
238+
val jmhJvmArgs = jvmArgs(b)
239+
javaSettings.jvmArgs?.let { cmdList.add(String.format(JMH_JVM_ARGS, it)) } // add JVM arguments also to JMH fork JVMs
240+
if (jmhJvmArgs.isNotEmpty()) {
241+
cmdList.add(String.format(JMH_JVM_ARGS, jmhJvmArgs))
242+
}
243+
244+
// benchmark parameters
245+
if (b.jmhParams.isNotEmpty()) {
246+
cmdList.add(jmhParams(b))
247+
}
248+
249+
// benchmark
250+
cmdList.add(String.format(JMH_BENCH, benchName(b)))
251+
252+
// JMH arguments
253+
cmdList.add(JMH_ARGS)
254+
255+
return cmdList.joinToString(" ")
256+
}
224257

225258
private fun jmhParams(b: Benchmark): String =
226-
b.jmhParams.joinToString(separator = " ") { "-p ${it.first}=${it.second}" }
259+
b.jmhParams.joinToString(separator = " ") { "-p ${it.first}=${it.second}" }
227260

228261
private fun benchName(b: Benchmark): String = "${b.clazz.replace("$", ".")}.${b.name}"
229262

@@ -237,21 +270,14 @@ abstract class AbstractDynamicCoverage(
237270

238271

239272
companion object {
240-
val log: Logger = LogManager.getLogger(AbstractDynamicCoverage::class.java.canonicalName)
241-
242-
// arguments:
243-
// For use in shell (e.g., bash) the constructed command string needs escaping of
244-
// (1) dollar-signs ($) and
245-
// (2) double-quotes (")
246-
// around the -jvmArgs argument and the JMH benchmark regex
247-
// 1. benchmark jar path
248-
// 2. JVM args string
249-
// 3. JMH parameter string (required for cmdParam and optional for cmd)
250-
// 4. JMH benchmark
251-
private const val baseCmd = "java -jar %s -jvmArgs=%s -bm ss -wi 0 -i 1 -f 1 -r 1 -w 1"
252-
private const val cmd = "$baseCmd ^%s\$"
253-
private const val cmdParam = "$baseCmd %s ^%s\$"
254-
255-
private const val tmpDirPrefix = "bencher-dyn-cov-"
273+
private val log: Logger = LogManager.getLogger(AbstractDynamicCoverage::class.java.canonicalName)
274+
275+
private const val JAVA_CMD = "java"
276+
private const val JAR_ARG = "-jar"
277+
private const val JMH_JVM_ARGS = "-jvmArgs=%s"
278+
private const val JMH_ARGS = "-bm ss -wi 0 -i 1 -f 1 -r 1 -w 1"
279+
private const val JMH_BENCH = "^%s\$"
280+
281+
private const val TMP_DIR_PREFIX = "bencher-dyn-cov-"
256282
}
257283
}

0 commit comments

Comments
 (0)