Skip to content

Commit a26880a

Browse files
committed
Print test info from android logcat
1 parent 57b6e64 commit a26880a

File tree

2 files changed

+89
-31
lines changed

2 files changed

+89
-31
lines changed

plugin/main/src/kotlinx/benchmark/gradle/AndroidMultiplatformTasks.kt

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package kotlinx.benchmark.gradle
22

33
import kotlinx.benchmark.gradle.internal.KotlinxBenchmarkPluginInternalApi
44
import org.gradle.api.*
5+
import org.gradle.api.tasks.TaskProvider
56
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
67
import java.io.InputStream
78
import java.util.*
@@ -14,18 +15,18 @@ fun Project.processAndroidCompilation(target: KotlinJvmAndroidCompilation) {
1415
println("processAndroidCompilation: ${target.name}")
1516
val compilation = target.target.compilations.names.let(::println)
1617

17-
tasks.register("processAndroid${target.name.capitalize(Locale.getDefault())}Compilation", DefaultTask::class.java) {
18+
val generateSourcesTask = tasks.register("processAndroid${target.name.capitalize(Locale.getDefault())}Compilation", DefaultTask::class.java) {
1819
it.group = "benchmark"
1920
it.description = "Processes the Android compilation '${target.name}' for benchmarks"
2021
it.dependsOn("bundle${target.name.capitalize(Locale.getDefault())}Aar")
2122
it.doLast {
2223
unpackAndProcessAar(target) { classDescriptors ->
2324
generateBenchmarkSourceFiles(classDescriptors)
2425
}
25-
detectAndroidDevice()
26-
createAndroidBenchmarkExecTask()
2726
}
2827
}
28+
29+
createAndroidBenchmarkExecTask(target, generateSourcesTask)
2930
}
3031

3132
fun Project.detectAndroidDevice() {
@@ -47,43 +48,97 @@ fun Project.detectAndroidDevice() {
4748

4849

4950
// Use shell command to execute separate project gradle task
50-
fun Project.createAndroidBenchmarkExecTask() {
51-
// TODO: Project path needs to execute benchmark task
52-
val executeBenchmarkPath = "E:/Android/AndroidProjects/kotlin-qualification-task"
53-
// Using ./gradlew on Windows shows error:
54-
// CreateProcess error=193, %1 is not a valid Win32 application
55-
val osName = System.getProperty("os.name").toLowerCase(Locale.ROOT)
56-
val gradlewPath = "$executeBenchmarkPath/gradlew" + if (osName.contains("win")) ".bat" else ""
57-
val args = listOf("-p", executeBenchmarkPath, "connectedAndroidTest")
51+
fun Project.createAndroidBenchmarkExecTask(target: KotlinJvmAndroidCompilation, generateSourcesTask: TaskProvider<*>) {
52+
tasks.register("android${target.name.capitalize(Locale.getDefault())}Benchmark", DefaultTask::class.java) {
53+
it.group = "benchmark"
54+
it.description = "Processes the Android compilation '${target.name}' for benchmarks"
55+
it.dependsOn(generateSourcesTask)
56+
it.doLast {
57+
detectAndroidDevice()
5858

59-
try {
60-
println("Running command: $gradlewPath ${args.joinToString(" ")}")
59+
// TODO: Project path needs to execute benchmark task
60+
val executeBenchmarkPath = "E:/Android/AndroidProjects/kotlin-qualification-task"
61+
// Using ./gradlew on Windows shows error:
62+
// CreateProcess error=193, %1 is not a valid Win32 application
63+
val osName = System.getProperty("os.name").toLowerCase(Locale.ROOT)
64+
val gradlewPath = "$executeBenchmarkPath/gradlew" + if (osName.contains("win")) ".bat" else ""
65+
val args = listOf("-p", executeBenchmarkPath, "connectedAndroidTest")
66+
67+
try {
68+
println("Running command: $gradlewPath ${args.joinToString(" ")}")
69+
70+
val process = ProcessBuilder(gradlewPath, *args.toTypedArray())
71+
.redirectOutput(ProcessBuilder.Redirect.PIPE)
72+
.redirectError(ProcessBuilder.Redirect.PIPE)
73+
.start()
74+
75+
val outputGobbler = StreamGobbler(process.inputStream) { println(it) }
76+
val errorGobbler = StreamGobbler(process.errorStream) { System.err.println(it) }
77+
78+
outputGobbler.start()
79+
errorGobbler.start()
80+
81+
clearLogcat()
82+
val exitCode = process.waitFor(10, TimeUnit.MINUTES)
83+
captureLogcatOutput()
84+
if (!exitCode || process.exitValue() != 0) {
85+
println("Android benchmark task failed with exit code ${process.exitValue()}")
86+
} else {
87+
println("Benchmark for Android target finished.")
88+
}
89+
} catch (e: Exception) {
90+
e.printStackTrace()
91+
throw GradleException("Failed to execute benchmark task", e)
92+
}
93+
}
94+
}
95+
}
6196

62-
val process = ProcessBuilder(gradlewPath, *args.toTypedArray())
63-
.redirectOutput(ProcessBuilder.Redirect.PIPE)
64-
.redirectError(ProcessBuilder.Redirect.PIPE)
97+
private fun captureLogcatOutput() {
98+
try {
99+
val logcatProcess = ProcessBuilder("adb", "logcat", "-v", "time")
100+
.redirectErrorStream(true)
65101
.start()
66102

67-
val outputGobbler = StreamGobbler(process.inputStream) { println(it) }
68-
val errorGobbler = StreamGobbler(process.errorStream) { System.err.println(it) }
103+
val logcatGobbler = StreamGobbler(logcatProcess.inputStream) { line ->
104+
when {
105+
line.contains("Iteration") -> println(line.substring(line.indexOf("Iteration")))
106+
line.contains("run finished") -> println(line.substring(line.indexOf("run finished")))
107+
}
108+
}
69109

70-
outputGobbler.start()
71-
errorGobbler.start()
110+
logcatGobbler.start()
72111

73-
val exitCode = process.waitFor(10, TimeUnit.MINUTES)
74-
if (!exitCode || process.exitValue() != 0) {
75-
println("Android benchmark task failed with exit code ${process.exitValue()}")
76-
} else {
77-
println("Benchmark for Android target finished.")
112+
if (!logcatProcess.waitFor(10, TimeUnit.SECONDS)) {
113+
logcatProcess.destroy()
78114
}
115+
116+
logcatGobbler.join()
117+
} catch (e: Exception) {
118+
e.printStackTrace()
119+
throw GradleException("Failed to capture logcat output", e)
120+
}
121+
}
122+
123+
private fun clearLogcat() {
124+
try {
125+
ProcessBuilder("adb", "logcat", "-c")
126+
.redirectErrorStream(true)
127+
.start()
128+
.waitFor(5, TimeUnit.SECONDS)
79129
} catch (e: Exception) {
80130
e.printStackTrace()
81-
throw GradleException("Failed to execute benchmark task", e)
131+
throw GradleException("Failed to clear logcat", e)
82132
}
83133
}
84134

85-
class StreamGobbler(private val inputStream: InputStream, private val consumer: (String) -> Unit) : Thread() {
135+
class StreamGobbler(
136+
private val inputStream: InputStream,
137+
private val consumer: (String) -> Unit
138+
) : Thread() {
86139
override fun run() {
87-
inputStream.bufferedReader().lines().forEach(consumer)
140+
inputStream.bufferedReader().useLines { lines ->
141+
lines.forEach { consumer(it) }
142+
}
88143
}
89144
}

plugin/main/src/kotlinx/benchmark/gradle/AndroidSourceGenerator.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.squareup.kotlinpoet.*
44
import kotlinx.benchmark.gradle.internal.KotlinxBenchmarkPluginInternalApi
55
import org.gradle.api.*
66
import java.io.File
7+
import java.util.*
78

89
@KotlinxBenchmarkPluginInternalApi
910
fun Project.generateBenchmarkSourceFiles(
@@ -61,7 +62,7 @@ private fun generateDescriptorFile(descriptor: ClassAnnotationsDescriptor, andro
6162

6263
private fun addBenchmarkMethods(typeSpecBuilder: TypeSpec.Builder, descriptor: ClassAnnotationsDescriptor) {
6364
val className = "${descriptor.packageName}.${descriptor.name}"
64-
val propertyName = descriptor.name.decapitalize()
65+
val propertyName = descriptor.name.decapitalize(Locale.getDefault())
6566

6667
typeSpecBuilder.addProperty(
6768
PropertySpec.builder(propertyName, ClassName.bestGuess(className))
@@ -81,7 +82,6 @@ private fun addBenchmarkMethods(typeSpecBuilder: TypeSpec.Builder, descriptor: C
8182
method.annotations.any { it.name == "kotlinx.benchmark.Setup" || it.name == "kotlinx.benchmark.TearDown" } -> {
8283
generateNonMeasurableMethod(descriptor, method, propertyName, typeSpecBuilder)
8384
}
84-
8585
else -> {
8686
generateMeasurableMethod(descriptor, method, propertyName, typeSpecBuilder)
8787
}
@@ -95,7 +95,6 @@ private fun generateMeasurableMethod(
9595
propertyName: String,
9696
typeSpecBuilder: TypeSpec.Builder
9797
) {
98-
9998
val measurementIterations = descriptor.annotations
10099
.find { it.name == "kotlinx.benchmark.Measurement" }
101100
?.parameters?.get("iterations") as? Int ?: 5
@@ -118,6 +117,10 @@ private fun generateMeasurableMethod(
118117
.beginControlFlow("while (state.keepRunning())")
119118
.addStatement("$propertyName.${method.name}()")
120119
.endControlFlow()
120+
.addStatement("val measurementResult = state.getMeasurementTimeNs()")
121+
.beginControlFlow("measurementResult.forEachIndexed { index, time ->")
122+
.addStatement("println(\"Iteration \${index + 1}: \$time ns\")")
123+
.endControlFlow()
121124
typeSpecBuilder.addFunction(methodSpecBuilder.build())
122125
}
123126

0 commit comments

Comments
 (0)