[kotlin-native][tests][lldb] adds possibility to run simple lldb scenarious with simulator
This commit is contained in:
@@ -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.
|
||||
|
||||
+1
@@ -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")
|
||||
|
||||
+39
-24
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -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 = [..., ..., ...]
|
||||
""")
|
||||
|
||||
+24
-49
@@ -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
|
||||
|
||||
+54
@@ -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"
|
||||
Reference in New Issue
Block a user