[kotlin-native][tests][lldb] adds possibility to run simple lldb scenarious with simulator

This commit is contained in:
Vasily Levchenko
2021-04-22 17:16:44 +02:00
committed by Space
parent ed3542cdf5
commit 6f2af740cb
6 changed files with 132 additions and 82 deletions
@@ -5,8 +5,7 @@
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompileNativeBinary
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.UtilsKt
import java.nio.file.Paths
@@ -5248,10 +5247,15 @@ project.tasks.named("test").configure {
}
project.tasks.register("debugger_test", Test.class) {
enabled = (target.family == Family.OSX) // KT-30366
enabled = (target.family.appleFamily) // KT-30366
testLogging { exceptionFormat = 'full' }
UtilsKt.dependsOnDist(it)
systemProperties = ['kotlin.native.home': kotlinNativeDist]
systemProperties = ['kotlin.native.home': kotlinNativeDist,
'kotlin.native.host': HostManager.@Companion.getHost(),
'kotlin.native.test.target': target,
'kotlin.native.test.debugger.simulator.enabled': findProperty("kotlin.native.test.debugger.simulator.enabled"),
'kotlin.native.test.debugger.simulator.delay': findProperty("kotlin.native.test.debugger.simulator.delay")]
outputs.upToDateWhen { false }
}
// Configure build for iOS device targets.
@@ -17,6 +17,7 @@ object DistProperties {
val konanc: Path = dist.resolve("bin/$konancDriver")
val cinterop: Path = dist.resolve("bin/$cinteropDriver")
val lldb: Path = Paths.get("lldb")
val xcrun: Path = Paths.get("xcrun")
val devToolsSecurity: Path = Paths.get("DevToolsSecurity")
val dwarfDump: Path = Paths.get("dwarfdump")
val swiftc: Path = Paths.get("swiftc")
@@ -5,14 +5,12 @@
package org.jetbrains.kotlin.native.test.debugger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.*
import org.jetbrains.kotlin.cli.bc.K2Native
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.StringReader
import java.lang.Thread.sleep
import java.nio.file.Files
import java.nio.file.Path
import java.util.concurrent.TimeUnit
@@ -25,13 +23,18 @@ class ToolDriver(
listOf("-output", output.toString(), source.toString(), *args).toTypedArray()
}
fun compile(output: Path, srcs:Array<Path>, vararg args: String) = compile(output, *args) {
listOf("-output", output.toString(), *srcs.map{it.toString()}.toTypedArray(), *args).toTypedArray()
fun compile(output: Path, srcs: Array<Path>, vararg args: String) = compile(output, *args) {
listOf("-output", output.toString(), *srcs.map { it.toString() }.toTypedArray(), *args).toTypedArray()
}
private fun crossPlatform(): Array<String> = if (targetIsHost())
emptyArray()
else
arrayOf("-target", target())
private fun compile(output: Path, vararg args: String, argsCalculator:() -> Array<String>) {
check(!Files.exists(output))
val allArgs = argsCalculator()
val allArgs = arrayOf(*crossPlatform(), *argsCalculator())
if (useInProcessCompiler) {
K2Native.main(allArgs)
@@ -54,11 +57,15 @@ class ToolDriver(
}
fun runLldb(program: Path, commands: List<String>): String {
val args = listOf("-b", "-o", "command script import \"${DistProperties.lldbPrettyPrinters}\"") +
commands.flatMap { listOf("-o", it) }
return subprocess(DistProperties.lldb, program.toString(), "-b", *args.toTypedArray())
.thrownIfFailed()
.stdout
val args = listOf("-b", *program.programOrAttach(), "-o", "command script import \"${DistProperties.lldbPrettyPrinters}\"",
*commands.flatMap { listOf("-o", it) }.toTypedArray())
if (!targetIsHost()) {
return subprocess(DistProperties.xcrun, "simctl", "spawn", "-w", "-s", "iPhone 11", program.toString()) {
sleep(simulatorDelay())
DistProperties.lldb to listOf(*args.toTypedArray(), "-o", "detach")
}.thrownIfFailed().stdout
}
return subprocess(DistProperties.lldb, *args.toTypedArray()).thrownIfFailed().stdout
}
fun runDwarfDump(program: Path, vararg args:String = emptyArray(), processor:List<DwarfTag>.()->Unit) {
@@ -73,6 +80,8 @@ class ToolDriver(
}
}
private fun Path.programOrAttach() = if (targetIsHost()) arrayOf(toString()) else arrayOf("-o", "process attach -n ${this.toString()}")
data class ProcessOutput(
val program: Path,
val process: Process,
@@ -94,7 +103,7 @@ data class ProcessOutput(
}
}
fun subprocess(program: Path, vararg args: String): ProcessOutput {
fun subprocess(program: Path, vararg args: String, action: (() -> Pair<Path, List<String>>)? = null): ProcessOutput {
val start = System.currentTimeMillis()
val process = ProcessBuilder(program.toString(), *args).start()
val out = GlobalScope.async(Dispatchers.IO) {
@@ -105,20 +114,26 @@ fun subprocess(program: Path, vararg args: String): ProcessOutput {
readStream(process, process.errorStream.buffered())
}
return runBlocking {
try {
val status = process.waitFor(5L, TimeUnit.MINUTES)
if (!status) {
out.cancel()
err.cancel()
error("$program timeouted")
}
}catch (e:Exception) {
val actionOutput = action?.let {
val p = it()
subprocess(p.first, *p.second.toTypedArray())
}
try {
val status = process.waitFor(5L, TimeUnit.MINUTES)
if (!status) {
out.cancel()
err.cancel()
error(e)
error("$program timeouted")
}
ProcessOutput(program, process, out.await(), err.await(), System.currentTimeMillis() - start)
}catch (e:Exception) {
out.cancel()
err.cancel()
error(e)
}
return actionOutput ?: runBlocking {
ProcessOutput(program, process, out.await(), err.await(), System.currentTimeMillis() - start)
}
}
@@ -3,6 +3,7 @@
* that can be found in the LICENSE file.
*/
import org.jetbrains.kotlin.native.test.debugger.lldbCommandRunOrContinue
import org.jetbrains.kotlin.native.test.debugger.lldbComplexTest
import org.jetbrains.kotlin.native.test.debugger.lldbTest
import org.junit.Test
@@ -20,7 +21,7 @@ class LldbTests {
> b main.kt:2
Breakpoint 1: [..]
> r
> ${lldbCommandRunOrContinue()}
Process [..] stopped
[..] stop reason = breakpoint 1.1
[..] at main.kt:2[..]
@@ -53,7 +54,7 @@ class LldbTests {
}
""", """
> b main.kt:7
> r
> ${lldbCommandRunOrContinue()}
> fr var
(char) a = '\x01'
(int) b = 2
@@ -76,7 +77,7 @@ class LldbTests {
}
""", """
> b main.kt:4
> r
> ${lldbCommandRunOrContinue()}
> fr var
(ObjHeader *) args = []
(ObjHeader *) point = [x: ..., y: ...]
@@ -98,7 +99,7 @@ class LldbTests {
data class Point(val x: Int, val y: Int)
""", """
> b main.kt:8
> r
> ${lldbCommandRunOrContinue()}
> fr var
(ObjHeader *) args = []
(ObjHeader *) xs = [..., ..., ...]
@@ -115,7 +116,7 @@ class LldbTests {
data class Point(val x: Int, val y: Int)
""", """
> b main.kt:3
> r
> ${lldbCommandRunOrContinue()}
> fr var xs
(ObjHeader *) xs = [..., ..., ...]
""")
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.native.test.debugger
import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.konan.target.hostTargetSuffix
import org.junit.Assert.fail
import java.io.IOException
import java.nio.file.Files
@@ -63,6 +64,11 @@ fun lldbTest(@Language("kotlin") programText: String, lldbSession: String) {
return
}
if (!targetIsHost() && !simulatorTestEnabled()) {
println("simulator tests disabled, check 'kotlin.native.test.debugger.simulator.enabled' property")
return
}
if (!isOsxDevToolsEnabled) {
println("""Development tools aren't available.
|Please consider to execute:
@@ -96,8 +102,8 @@ private val isOsxDevToolsEnabled: Boolean by lazy {
}
fun dwarfDumpTest(@Language("kotlin") programText: String, flags: List<String>, test:List<DwarfTag>.()->Unit) {
if (!haveDwarfDump) {
println("Skipping test: no dwarfdump")
if (!haveDwarfDump || !targetIsHost()) {
println("Skipping test")
return
}
@@ -155,8 +161,8 @@ class ToolDriverHelper(private val driver: ToolDriver, val root:Path) {
}
fun dwarfDumpComplexTest(test:ToolDriverHelper.()->Unit) {
if (!haveDwarfDump) {
println("Skipping test: no dwarfdump")
if (!haveDwarfDump || !targetIsHost()) {
println("Skipping test")
return
}
@@ -168,8 +174,8 @@ fun dwarfDumpComplexTest(test:ToolDriverHelper.()->Unit) {
}
fun lldbComplexTest(test:ToolDriverHelper.()->Unit) {
if (!haveLldb) {
println("Skipping test: no lldb")
if (!haveLldb || !targetIsHost()) {
println("Skipping test")
return
}
@@ -180,43 +186,6 @@ fun lldbComplexTest(test:ToolDriverHelper.()->Unit) {
}
}
private val haveDwarfDump: Boolean by lazy {
val version = try {
subprocess(DistProperties.dwarfDump, "--version")
.takeIf { it.process.exitValue() == 0 }
?.stdout
} catch (e: IOException) {
null
}
if (version == null) {
println("No LLDB found")
} else {
println("Using $version")
}
version != null
}
private val haveLldb: Boolean by lazy {
val lldbVersion = try {
subprocess(DistProperties.lldb, "-version")
.takeIf { it.process.exitValue() == 0 }
?.stdout
} catch (e: IOException) {
null
}
if (lldbVersion == null) {
println("No LLDB found")
} else {
println("Using $lldbVersion")
}
lldbVersion != null
}
private class LldbSessionSpecification private constructor(
val commands: List<String>,
val patterns: List<List<String>>
@@ -224,11 +193,17 @@ private class LldbSessionSpecification private constructor(
fun match(output: String) {
val blocks = output.split("""(?=\(lldb\))""".toRegex()).filterNot(String::isEmpty)
check(blocks[0].startsWith("(lldb) target create")) { "Missing block \"target create\". Got: ${blocks[0]}" }
check(blocks[1].startsWith("(lldb) command script import")) {
"Missing block \"command script import\". Got: ${blocks[0]}"
if (targetIsHost()) {
check(blocks[0].startsWith("(lldb) target create")) { "Missing block \"target create\". Got: ${blocks[0]}" }
check(blocks[1].startsWith("(lldb) command script import")) {
"Missing block \"command script import\". Got: ${blocks[0]}"
}
}
val responses = blocks.drop(2)
val responses = if (targetIsHost())
blocks.drop(2)
else
blocks.drop(2).dropLast(1)
val executedCommands = responses.map { it.lines().first() }
val bodies = responses.map { it.lines().drop(1) }
val responsesMatch = executedCommands.size == commands.size
@@ -238,8 +213,8 @@ private class LldbSessionSpecification private constructor(
val message = """
|Responses do not match commands.
|
|COMMANDS: |$commands
|RESPONSES: |$executedCommands
|COMMANDS: |$commands (${commands.size})
|RESPONSES: |$executedCommands (${executedCommands.size})
|
|FULL SESSION:
|$output
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2021 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.native.test.debugger
import java.io.IOException
internal fun targetIsHost() = System.getProperty("kotlin.native.host") == System.getProperty("kotlin.native.test.target")
internal fun simulatorTestEnabled() = System.getProperty("kotlin.native.test.debugger.simulator.enabled")?.toBoolean() ?: false
internal fun simulatorDelay() = System.getProperty("kotlin.native.test.debugger.simulator.delay")?.toLongOrNull() ?: 300
internal fun target() = System.getProperty("kotlin.native.test.target")
internal val haveDwarfDump: Boolean by lazy {
val version = try {
subprocess(DistProperties.dwarfDump, "--version")
.takeIf { it.process.exitValue() == 0 }
?.stdout
} catch (e: IOException) {
null
}
if (version == null) {
println("No LLDB found")
} else {
println("Using $version")
}
version != null
}
internal val haveLldb: Boolean by lazy {
val lldbVersion = try {
subprocess(DistProperties.lldb, "-version")
.takeIf { it.process.exitValue() == 0 }
?.stdout
} catch (e: IOException) {
null
}
if (lldbVersion == null) {
println("No LLDB found")
} else {
println("Using $lldbVersion")
}
lldbVersion != null
}
internal fun lldbCommandRunOrContinue() = if (targetIsHost()) "r" else "c"