[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.gradle.plugin.tasks.KonanCompileNativeBinary
|
||||||
import org.jetbrains.kotlin.*
|
import org.jetbrains.kotlin.*
|
||||||
import org.jetbrains.kotlin.konan.target.Family
|
import org.jetbrains.kotlin.konan.target.*
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
|
||||||
import org.jetbrains.kotlin.UtilsKt
|
import org.jetbrains.kotlin.UtilsKt
|
||||||
|
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
@@ -5248,10 +5247,15 @@ project.tasks.named("test").configure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
project.tasks.register("debugger_test", Test.class) {
|
project.tasks.register("debugger_test", Test.class) {
|
||||||
enabled = (target.family == Family.OSX) // KT-30366
|
enabled = (target.family.appleFamily) // KT-30366
|
||||||
testLogging { exceptionFormat = 'full' }
|
testLogging { exceptionFormat = 'full' }
|
||||||
UtilsKt.dependsOnDist(it)
|
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.
|
// Configure build for iOS device targets.
|
||||||
|
|||||||
+1
@@ -17,6 +17,7 @@ object DistProperties {
|
|||||||
val konanc: Path = dist.resolve("bin/$konancDriver")
|
val konanc: Path = dist.resolve("bin/$konancDriver")
|
||||||
val cinterop: Path = dist.resolve("bin/$cinteropDriver")
|
val cinterop: Path = dist.resolve("bin/$cinteropDriver")
|
||||||
val lldb: Path = Paths.get("lldb")
|
val lldb: Path = Paths.get("lldb")
|
||||||
|
val xcrun: Path = Paths.get("xcrun")
|
||||||
val devToolsSecurity: Path = Paths.get("DevToolsSecurity")
|
val devToolsSecurity: Path = Paths.get("DevToolsSecurity")
|
||||||
val dwarfDump: Path = Paths.get("dwarfdump")
|
val dwarfDump: Path = Paths.get("dwarfdump")
|
||||||
val swiftc: Path = Paths.get("swiftc")
|
val swiftc: Path = Paths.get("swiftc")
|
||||||
|
|||||||
+39
-24
@@ -5,14 +5,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.test.debugger
|
package org.jetbrains.kotlin.native.test.debugger
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.jetbrains.kotlin.cli.bc.K2Native
|
import org.jetbrains.kotlin.cli.bc.K2Native
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.StringReader
|
import java.io.StringReader
|
||||||
|
import java.lang.Thread.sleep
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
@@ -25,13 +23,18 @@ class ToolDriver(
|
|||||||
listOf("-output", output.toString(), source.toString(), *args).toTypedArray()
|
listOf("-output", output.toString(), source.toString(), *args).toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun compile(output: Path, srcs:Array<Path>, vararg args: String) = compile(output, *args) {
|
fun compile(output: Path, srcs: Array<Path>, vararg args: String) = compile(output, *args) {
|
||||||
listOf("-output", output.toString(), *srcs.map{it.toString()}.toTypedArray(), *args).toTypedArray()
|
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>) {
|
private fun compile(output: Path, vararg args: String, argsCalculator:() -> Array<String>) {
|
||||||
check(!Files.exists(output))
|
check(!Files.exists(output))
|
||||||
val allArgs = argsCalculator()
|
val allArgs = arrayOf(*crossPlatform(), *argsCalculator())
|
||||||
|
|
||||||
if (useInProcessCompiler) {
|
if (useInProcessCompiler) {
|
||||||
K2Native.main(allArgs)
|
K2Native.main(allArgs)
|
||||||
@@ -54,11 +57,15 @@ class ToolDriver(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun runLldb(program: Path, commands: List<String>): String {
|
fun runLldb(program: Path, commands: List<String>): String {
|
||||||
val args = listOf("-b", "-o", "command script import \"${DistProperties.lldbPrettyPrinters}\"") +
|
val args = listOf("-b", *program.programOrAttach(), "-o", "command script import \"${DistProperties.lldbPrettyPrinters}\"",
|
||||||
commands.flatMap { listOf("-o", it) }
|
*commands.flatMap { listOf("-o", it) }.toTypedArray())
|
||||||
return subprocess(DistProperties.lldb, program.toString(), "-b", *args.toTypedArray())
|
if (!targetIsHost()) {
|
||||||
.thrownIfFailed()
|
return subprocess(DistProperties.xcrun, "simctl", "spawn", "-w", "-s", "iPhone 11", program.toString()) {
|
||||||
.stdout
|
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) {
|
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(
|
data class ProcessOutput(
|
||||||
val program: Path,
|
val program: Path,
|
||||||
val process: Process,
|
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 start = System.currentTimeMillis()
|
||||||
val process = ProcessBuilder(program.toString(), *args).start()
|
val process = ProcessBuilder(program.toString(), *args).start()
|
||||||
val out = GlobalScope.async(Dispatchers.IO) {
|
val out = GlobalScope.async(Dispatchers.IO) {
|
||||||
@@ -105,20 +114,26 @@ fun subprocess(program: Path, vararg args: String): ProcessOutput {
|
|||||||
readStream(process, process.errorStream.buffered())
|
readStream(process, process.errorStream.buffered())
|
||||||
}
|
}
|
||||||
|
|
||||||
return runBlocking {
|
val actionOutput = action?.let {
|
||||||
try {
|
val p = it()
|
||||||
val status = process.waitFor(5L, TimeUnit.MINUTES)
|
subprocess(p.first, *p.second.toTypedArray())
|
||||||
if (!status) {
|
}
|
||||||
out.cancel()
|
|
||||||
err.cancel()
|
try {
|
||||||
error("$program timeouted")
|
val status = process.waitFor(5L, TimeUnit.MINUTES)
|
||||||
}
|
if (!status) {
|
||||||
}catch (e:Exception) {
|
|
||||||
out.cancel()
|
out.cancel()
|
||||||
err.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.
|
* 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.lldbComplexTest
|
||||||
import org.jetbrains.kotlin.native.test.debugger.lldbTest
|
import org.jetbrains.kotlin.native.test.debugger.lldbTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -20,7 +21,7 @@ class LldbTests {
|
|||||||
> b main.kt:2
|
> b main.kt:2
|
||||||
Breakpoint 1: [..]
|
Breakpoint 1: [..]
|
||||||
|
|
||||||
> r
|
> ${lldbCommandRunOrContinue()}
|
||||||
Process [..] stopped
|
Process [..] stopped
|
||||||
[..] stop reason = breakpoint 1.1
|
[..] stop reason = breakpoint 1.1
|
||||||
[..] at main.kt:2[..]
|
[..] at main.kt:2[..]
|
||||||
@@ -53,7 +54,7 @@ class LldbTests {
|
|||||||
}
|
}
|
||||||
""", """
|
""", """
|
||||||
> b main.kt:7
|
> b main.kt:7
|
||||||
> r
|
> ${lldbCommandRunOrContinue()}
|
||||||
> fr var
|
> fr var
|
||||||
(char) a = '\x01'
|
(char) a = '\x01'
|
||||||
(int) b = 2
|
(int) b = 2
|
||||||
@@ -76,7 +77,7 @@ class LldbTests {
|
|||||||
}
|
}
|
||||||
""", """
|
""", """
|
||||||
> b main.kt:4
|
> b main.kt:4
|
||||||
> r
|
> ${lldbCommandRunOrContinue()}
|
||||||
> fr var
|
> fr var
|
||||||
(ObjHeader *) args = []
|
(ObjHeader *) args = []
|
||||||
(ObjHeader *) point = [x: ..., y: ...]
|
(ObjHeader *) point = [x: ..., y: ...]
|
||||||
@@ -98,7 +99,7 @@ class LldbTests {
|
|||||||
data class Point(val x: Int, val y: Int)
|
data class Point(val x: Int, val y: Int)
|
||||||
""", """
|
""", """
|
||||||
> b main.kt:8
|
> b main.kt:8
|
||||||
> r
|
> ${lldbCommandRunOrContinue()}
|
||||||
> fr var
|
> fr var
|
||||||
(ObjHeader *) args = []
|
(ObjHeader *) args = []
|
||||||
(ObjHeader *) xs = [..., ..., ...]
|
(ObjHeader *) xs = [..., ..., ...]
|
||||||
@@ -115,7 +116,7 @@ class LldbTests {
|
|||||||
data class Point(val x: Int, val y: Int)
|
data class Point(val x: Int, val y: Int)
|
||||||
""", """
|
""", """
|
||||||
> b main.kt:3
|
> b main.kt:3
|
||||||
> r
|
> ${lldbCommandRunOrContinue()}
|
||||||
> fr var xs
|
> fr var xs
|
||||||
(ObjHeader *) xs = [..., ..., ...]
|
(ObjHeader *) xs = [..., ..., ...]
|
||||||
""")
|
""")
|
||||||
|
|||||||
+24
-49
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.native.test.debugger
|
package org.jetbrains.kotlin.native.test.debugger
|
||||||
|
|
||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
|
import org.jetbrains.kotlin.konan.target.hostTargetSuffix
|
||||||
import org.junit.Assert.fail
|
import org.junit.Assert.fail
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@@ -63,6 +64,11 @@ fun lldbTest(@Language("kotlin") programText: String, lldbSession: String) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!targetIsHost() && !simulatorTestEnabled()) {
|
||||||
|
println("simulator tests disabled, check 'kotlin.native.test.debugger.simulator.enabled' property")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!isOsxDevToolsEnabled) {
|
if (!isOsxDevToolsEnabled) {
|
||||||
println("""Development tools aren't available.
|
println("""Development tools aren't available.
|
||||||
|Please consider to execute:
|
|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) {
|
fun dwarfDumpTest(@Language("kotlin") programText: String, flags: List<String>, test:List<DwarfTag>.()->Unit) {
|
||||||
if (!haveDwarfDump) {
|
if (!haveDwarfDump || !targetIsHost()) {
|
||||||
println("Skipping test: no dwarfdump")
|
println("Skipping test")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,8 +161,8 @@ class ToolDriverHelper(private val driver: ToolDriver, val root:Path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun dwarfDumpComplexTest(test:ToolDriverHelper.()->Unit) {
|
fun dwarfDumpComplexTest(test:ToolDriverHelper.()->Unit) {
|
||||||
if (!haveDwarfDump) {
|
if (!haveDwarfDump || !targetIsHost()) {
|
||||||
println("Skipping test: no dwarfdump")
|
println("Skipping test")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,8 +174,8 @@ fun dwarfDumpComplexTest(test:ToolDriverHelper.()->Unit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun lldbComplexTest(test:ToolDriverHelper.()->Unit) {
|
fun lldbComplexTest(test:ToolDriverHelper.()->Unit) {
|
||||||
if (!haveLldb) {
|
if (!haveLldb || !targetIsHost()) {
|
||||||
println("Skipping test: no lldb")
|
println("Skipping test")
|
||||||
return
|
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(
|
private class LldbSessionSpecification private constructor(
|
||||||
val commands: List<String>,
|
val commands: List<String>,
|
||||||
val patterns: List<List<String>>
|
val patterns: List<List<String>>
|
||||||
@@ -224,11 +193,17 @@ private class LldbSessionSpecification private constructor(
|
|||||||
|
|
||||||
fun match(output: String) {
|
fun match(output: String) {
|
||||||
val blocks = output.split("""(?=\(lldb\))""".toRegex()).filterNot(String::isEmpty)
|
val blocks = output.split("""(?=\(lldb\))""".toRegex()).filterNot(String::isEmpty)
|
||||||
check(blocks[0].startsWith("(lldb) target create")) { "Missing block \"target create\". Got: ${blocks[0]}" }
|
if (targetIsHost()) {
|
||||||
check(blocks[1].startsWith("(lldb) command script import")) {
|
check(blocks[0].startsWith("(lldb) target create")) { "Missing block \"target create\". Got: ${blocks[0]}" }
|
||||||
"Missing block \"command script import\". 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 executedCommands = responses.map { it.lines().first() }
|
||||||
val bodies = responses.map { it.lines().drop(1) }
|
val bodies = responses.map { it.lines().drop(1) }
|
||||||
val responsesMatch = executedCommands.size == commands.size
|
val responsesMatch = executedCommands.size == commands.size
|
||||||
@@ -238,8 +213,8 @@ private class LldbSessionSpecification private constructor(
|
|||||||
val message = """
|
val message = """
|
||||||
|Responses do not match commands.
|
|Responses do not match commands.
|
||||||
|
|
|
|
||||||
|COMMANDS: |$commands
|
|COMMANDS: |$commands (${commands.size})
|
||||||
|RESPONSES: |$executedCommands
|
|RESPONSES: |$executedCommands (${executedCommands.size})
|
||||||
|
|
|
|
||||||
|FULL SESSION:
|
|FULL SESSION:
|
||||||
|$output
|
|$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