From 6f2af740cb3f10a6cbe6e8c895cbb6bb189b70fd Mon Sep 17 00:00:00 2001 From: Vasily Levchenko Date: Thu, 22 Apr 2021 17:16:44 +0200 Subject: [PATCH] [kotlin-native][tests][lldb] adds possibility to run simple lldb scenarious with simulator --- .../backend.native/tests/build.gradle | 12 ++- .../native/test/debugger/DistProperties.kt | 1 + .../kotlin/native/test/debugger/Driver.kt | 63 ++++++++++------ .../kotlin/native/test/debugger/LldbTests.kt | 11 +-- .../kotlin/native/test/debugger/Matchers.kt | 73 ++++++------------- .../kotlin/native/test/debugger/Utils.kt | 54 ++++++++++++++ 6 files changed, 132 insertions(+), 82 deletions(-) create mode 100644 kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Utils.kt diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index c5c3552a9f2..9c59801f6eb 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -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. diff --git a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/DistProperties.kt b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/DistProperties.kt index ead2acee774..9d1de2ad171 100644 --- a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/DistProperties.kt +++ b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/DistProperties.kt @@ -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") diff --git a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Driver.kt b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Driver.kt index 6366ae74ff0..0efdb60534c 100644 --- a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Driver.kt +++ b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Driver.kt @@ -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, vararg args: String) = compile(output, *args) { - listOf("-output", output.toString(), *srcs.map{it.toString()}.toTypedArray(), *args).toTypedArray() + fun compile(output: Path, srcs: Array, vararg args: String) = compile(output, *args) { + listOf("-output", output.toString(), *srcs.map { it.toString() }.toTypedArray(), *args).toTypedArray() } + private fun crossPlatform(): Array = if (targetIsHost()) + emptyArray() + else + arrayOf("-target", target()) + private fun compile(output: Path, vararg args: String, argsCalculator:() -> Array) { 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 { - 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.()->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>)? = 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) } } diff --git a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/LldbTests.kt b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/LldbTests.kt index 0fe7088cb62..6787efe6d90 100644 --- a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/LldbTests.kt +++ b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/LldbTests.kt @@ -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 = [..., ..., ...] """) diff --git a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Matchers.kt b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Matchers.kt index 88a0df6bb0e..b5e22c60eb6 100644 --- a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Matchers.kt +++ b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Matchers.kt @@ -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, test:List.()->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, val patterns: List> @@ -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 diff --git a/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Utils.kt b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Utils.kt new file mode 100644 index 00000000000..d0cb6dff0c4 --- /dev/null +++ b/kotlin-native/backend.native/tests/debugger/src/test/kotlin/org/jetbrains/kotlin/native/test/debugger/Utils.kt @@ -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" \ No newline at end of file