[K/N][test] Remove old lldb tests that were replaced in the new infra

These tests were replaced by the 842a66c3 and now located
in :native:native.tests project

Merge-request: KT-MR-9256
Merged-by: Pavel Punegov <Pavel.Punegov@jetbrains.com>
This commit is contained in:
Pavel Punegov
2023-03-20 13:56:42 +00:00
committed by Space Team
parent 2233164296
commit 56cd550e27
8 changed files with 0 additions and 1781 deletions
@@ -6193,11 +6193,6 @@ sourceSets {
srcDir 'extensions/nop/src/main/kotlin'
}
}
test {
kotlin {
srcDir 'debugger/src/test/kotlin'
}
}
}
compileNopPluginKotlin {
@@ -6625,25 +6620,6 @@ dependencies {
api DependenciesKt.commonDependency(project, "junit")
}
project.tasks.named("test").configure {
// Don't run this task as it will try to execute debugger tests too
// that is not desired as they are platform-specific.
enabled = false
}
project.tasks.register("debugger_test", Test.class) {
enabled = (target.family.appleFamily) // KT-30366
&& !isAggressiveGC // No need to test with different GC schedulers
testLogging { exceptionFormat = 'full' }
UtilsKt.dependsOnDist(it)
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.
if (UtilsKt.supportsRunningTestsOnDevice(target) && !UtilsKt.getCompileOnlyTests(project)) {
project.tasks
@@ -1,28 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.native.test.debugger
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.HostManager
import java.nio.file.Path
import java.nio.file.Paths
object DistProperties {
private val dist: Path = Paths.get(requireProp("kotlin.native.home"))
private val konancDriver = if (HostManager.host.family == Family.MINGW) "konanc.bat" else "konanc"
private val cinteropDriver = if (HostManager.host.family == Family.MINGW) "cinterop.bat" else "cinterop"
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")
val lldbPrettyPrinters: Path = dist.resolve("tools/konan_lldb.py")
private fun requireProp(name: String): String
= System.getProperty(name) ?: error("Property `$name` is not defined")
}
@@ -1,153 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.native.test.debugger
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
class ToolDriver(
private val useInProcessCompiler: Boolean = false
) {
fun compile(source: Path, output: Path, vararg args: String) = compile(output, *args) {
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()
}
private fun crossPlatform(): Array<String> = if (targetIsHost())
emptyArray()
else
arrayOf("-target", target())
@Suppress("UNUSED_PARAMETER")
private fun compile(output: Path, vararg args: String, argsCalculator:() -> Array<String>) {
check(!Files.exists(output))
val allArgs = arrayOf(*crossPlatform(), *argsCalculator())
if (useInProcessCompiler) {
K2Native.main(allArgs)
} else {
subprocess(DistProperties.konanc, *allArgs).thrownIfFailed()
}
check(Files.exists(output)) {
"Compiler has not produced an output at $output"
}
}
fun cinterop(defFile:Path, output: Path, pkg: String, vararg args: String) {
val allArgs = listOf("-o", output.toString(), "-def", defFile.toString(), "-pkg", pkg, *args).toTypedArray()
//TODO: do we need in process cinterop?
subprocess(DistProperties.cinterop, *allArgs).thrownIfFailed()
check(Files.exists(output)) {
"Compiler has not produced an output at $output"
}
}
fun runLldb(program: Path, commands: List<String>): String {
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) {
val dwarfProcess = subprocess(DistProperties.dwarfDump, *args, "${program}.dSYM/Contents/Resources/DWARF/${program.fileName}")
val out = dwarfProcess.takeIf { it.process.exitValue() == 0 }?.stdout ?: error(dwarfProcess.stderr)
DwarfUtilParser().parse(StringReader(out)).tags.toList().processor()
}
fun swiftc(output: Path, swiftSrc: Path, vararg args: String): String {
val swiftProcess = subprocess(DistProperties.swiftc, "-o", output.toString(), swiftSrc.toString(), *args)
return swiftProcess.takeIf { it.process.exitValue() == 0 }?.stdout ?: error(swiftProcess.stderr)
}
}
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,
val stdout: String,
val stderr: String,
val durationMs: Long
) {
fun thrownIfFailed(): ProcessOutput {
fun renderStdStream(name: String, text: String): String =
if (text.isBlank()) "$name is empty" else "$name:\n$text"
check(process.exitValue() == 0) {
"""$program exited with non-zero value: ${process.exitValue()}
|${renderStdStream("stdout", stdout)}
|${renderStdStream("stderr", stderr)}
""".trimMargin()
}
return this
}
}
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) {
readStream(process, process.inputStream.buffered())
}
val err = GlobalScope.async(Dispatchers.IO) {
readStream(process, process.errorStream.buffered())
}
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("$program timeouted")
}
}catch (e:Exception) {
out.cancel()
err.cancel()
error(e)
}
return actionOutput ?: runBlocking {
ProcessOutput(program, process, out.await(), err.await(), System.currentTimeMillis() - start)
}
}
private fun readStream(process: Process, stream: InputStream): String {
var size = 4096
val buffer = ByteArray(size) { 0 }
val sunk = ByteArrayOutputStream()
while (true) {
size = stream.read(buffer, 0, buffer.size)
if (size < 0 && !process.isAlive)
break
if (size > 0)
sunk.write(buffer, 0, size)
}
return String(sunk.toByteArray())
}
@@ -1,363 +0,0 @@
package org.jetbrains.kotlin.native.test.debugger
import java.io.File
import java.io.Reader
import java.lang.StringBuilder
sealed class DwarfTag(val tag: Tag) {
enum class Tag(val value: Int) {
DW_TAG_array_type(0x0001),
DW_TAG_class_type(0x0002),
DW_TAG_entry_point(0x0003),
DW_TAG_enumeration_type(0x0004),
DW_TAG_formal_parameter(0x0005),
DW_TAG_imported_declaration(0x0008),
DW_TAG_label(0x000a),
DW_TAG_lexical_block(0x000b),
DW_TAG_member(0x000d),
DW_TAG_pointer_type(0x000f),
DW_TAG_reference_type(0x0010),
DW_TAG_compile_unit(0x0011),
DW_TAG_string_type(0x0012),
DW_TAG_structure_type(0x0013),
DW_TAG_subroutine_type(0x0015),
DW_TAG_typedef(0x0016),
DW_TAG_union_type(0x0017),
DW_TAG_unspecified_parameters(0x0018),
DW_TAG_variant(0x0019),
DW_TAG_common_block(0x001a),
DW_TAG_common_inclusion(0x001b),
DW_TAG_inheritance(0x001c),
DW_TAG_inlined_subroutine(0x001d),
DW_TAG_module(0x001e),
DW_TAG_ptr_to_member_type(0x001f),
DW_TAG_set_type(0x0020),
DW_TAG_subrange_type(0x0021),
DW_TAG_with_stmt(0x0022),
DW_TAG_access_declaration(0x0023),
DW_TAG_base_type(0x0024),
DW_TAG_catch_block(0x0025),
DW_TAG_const_type(0x0026),
DW_TAG_constant(0x0027),
DW_TAG_enumerator(0x0028),
DW_TAG_file_type(0x0029),
DW_TAG_friend(0x002a),
DW_TAG_namelist(0x002b),
DW_TAG_namelist_item(0x002c),
DW_TAG_packed_type(0x002d),
DW_TAG_subprogram(0x002e),
DW_TAG_template_type_parameter(0x002f),
DW_TAG_template_value_parameter(0x0030),
DW_TAG_thrown_type(0x0031),
DW_TAG_try_block(0x0032),
DW_TAG_variant_part(0x0033),
DW_TAG_variable(0x0034),
DW_TAG_volatile_type(0x0035),
DW_TAG_dwarf_procedure(0x0036),
DW_TAG_restrict_type(0x0037),
DW_TAG_interface_type(0x0038),
DW_TAG_namespace(0x0039),
DW_TAG_imported_module(0x003a),
DW_TAG_unspecified_type(0x003b),
DW_TAG_partial_unit(0x003c),
DW_TAG_imported_unit(0x003d),
DW_TAG_condition(0x003f),
DW_TAG_shared_type(0x0040),
DW_TAG_type_unit(0x0041),
DW_TAG_rvalue_reference_type(0x0042),
DW_TAG_template_alias(0x0043),
DW_TAG_coarray_type(0x0044),
DW_TAG_generic_subrange(0x0045),
DW_TAG_dynamic_type(0x0046),
DW_TAG_MIPS_loop(0x4081),
DW_TAG_format_label(0x4101),
DW_TAG_function_template(0x4102),
DW_TAG_class_template(0x4103),
DW_TAG_GNU_template_template_param(0x4106),
DW_TAG_GNU_template_parameter_pack(0x4107),
DW_TAG_GNU_formal_parameter_pack(0x4108),
DW_TAG_APPLE_property(0x4200),
DW_TAG_BORLAND_property(0xb000),
DW_TAG_BORLAND_Delphi_string(0xb001),
DW_TAG_BORLAND_Delphi_dynamic_array(0xb002),
DW_TAG_BORLAND_Delphi_set(0xb003),
DW_TAG_BORLAND_Delphi_variant(0xb004),
}
val attributes = mutableMapOf<DwarfAttribute.Attribute, DwarfAttribute>()
operator fun DwarfAttribute.unaryPlus() { attributes[this.attribute] = this }
companion object {
fun by(name: String) = by(Tag.valueOf(name))
fun by(tag: Tag) = when(tag) {
Tag.DW_TAG_subprogram -> DwarfTagSubprogram()
else -> DwarfTagDefault(tag)
}
}
}
class DwarfTagDefault(tag: DwarfTag.Tag): DwarfTag(tag)
class DwarfTagSubprogram: DwarfTag(Tag.DW_TAG_subprogram) {
val name: String
get() = attributes[DwarfAttribute.Attribute.DW_AT_name]!!.rvString
val path: String?
get() = attributes[DwarfAttribute.Attribute.DW_AT_decl_file]?.rvString
val file: File?
get() = path?.run { File(this) }
}
class DwarfAttribute(val attribute: Attribute, val rawValue: String) {
enum class Attribute(val value: Int) {
DW_AT_sibling(0x01),
DW_AT_location(0x02),
DW_AT_name(0x03),
DW_AT_ordering(0x09),
DW_AT_byte_size(0x0b),
DW_AT_bit_offset(0x0c),
DW_AT_bit_size(0x0d),
DW_AT_stmt_list(0x10),
DW_AT_low_pc(0x11),
DW_AT_high_pc(0x12),
DW_AT_language(0x13),
DW_AT_discr(0x15),
DW_AT_discr_value(0x16),
DW_AT_visibility(0x17),
DW_AT_import(0x18),
DW_AT_string_length(0x19),
DW_AT_common_reference(0x1a),
DW_AT_comp_dir(0x1b),
DW_AT_const_value(0x1c),
DW_AT_containing_type(0x1d),
DW_AT_default_value(0x1e),
DW_AT_inline(0x20),
DW_AT_is_optional(0x21),
DW_AT_lower_bound(0x22),
DW_AT_producer(0x25),
DW_AT_prototyped(0x27),
DW_AT_return_addr(0x2a),
DW_AT_start_scope(0x2c),
DW_AT_bit_stride(0x2e),
DW_AT_upper_bound(0x2f),
DW_AT_abstract_origin(0x31),
DW_AT_accessibility(0x32),
DW_AT_address_class(0x33),
DW_AT_artificial(0x34),
DW_AT_base_types(0x35),
DW_AT_calling_convention(0x36),
DW_AT_count(0x37),
DW_AT_data_member_location(0x38),
DW_AT_decl_column(0x39),
DW_AT_decl_file(0x3a),
DW_AT_decl_line(0x3b),
DW_AT_declaration(0x3c),
DW_AT_discr_list(0x3d),
DW_AT_encoding(0x3e),
DW_AT_external(0x3f),
DW_AT_frame_base(0x40),
DW_AT_friend(0x41),
DW_AT_identifier_case(0x42),
DW_AT_macro_info(0x43),
DW_AT_namelist_item(0x44),
DW_AT_priority(0x45),
DW_AT_segment(0x46),
DW_AT_specification(0x47),
DW_AT_static_link(0x48),
DW_AT_type(0x49),
DW_AT_use_location(0x4a),
DW_AT_variable_parameter(0x4b),
DW_AT_virtuality(0x4c),
DW_AT_vtable_elem_location(0x4d),
DW_AT_allocated(0x4e),
DW_AT_associated(0x4f),
DW_AT_data_location(0x50),
DW_AT_byte_stride(0x51),
DW_AT_entry_pc(0x52),
DW_AT_use_UTF8(0x53),
DW_AT_extension(0x54),
DW_AT_ranges(0x55),
DW_AT_trampoline(0x56),
DW_AT_call_column(0x57),
DW_AT_call_file(0x58),
DW_AT_call_line(0x59),
DW_AT_description(0x5a),
DW_AT_binary_scale(0x5b),
DW_AT_decimal_scale(0x5c),
DW_AT_small(0x5d),
DW_AT_decimal_sign(0x5e),
DW_AT_digit_count(0x5f),
DW_AT_picture_string(0x60),
DW_AT_mutable(0x61),
DW_AT_threads_scaled(0x62),
DW_AT_explicit(0x63),
DW_AT_object_pointer(0x64),
DW_AT_endianity(0x65),
DW_AT_elemental(0x66),
DW_AT_pure(0x67),
DW_AT_recursive(0x68),
DW_AT_signature(0x69),
DW_AT_main_subprogram(0x6a),
DW_AT_data_bit_offset(0x6b),
DW_AT_const_expr(0x6c),
DW_AT_enum_class(0x6d),
DW_AT_linkage_name(0x6e),
DW_AT_string_length_bit_size(0x6f),
DW_AT_string_length_byte_size(0x70),
DW_AT_rank(0x71),
DW_AT_str_offsets_base(0x72),
DW_AT_addr_base(0x73),
DW_AT_rnglists_base(0x74),
DW_AT_dwo_id(0x75), ///< Retracted from DWARF v5.
DW_AT_dwo_name(0x76),
DW_AT_reference(0x77),
DW_AT_rvalue_reference(0x78),
DW_AT_macros(0x79),
DW_AT_call_all_calls(0x7a),
DW_AT_call_all_source_calls(0x7b),
DW_AT_call_all_tail_calls(0x7c),
DW_AT_call_return_pc(0x7d),
DW_AT_call_value(0x7e),
DW_AT_call_origin(0x7f),
DW_AT_call_parameter(0x80),
DW_AT_call_pc(0x81),
DW_AT_call_tail_call(0x82),
DW_AT_call_target(0x83),
DW_AT_call_target_clobbered(0x84),
DW_AT_call_data_location(0x85),
DW_AT_call_data_value(0x86),
DW_AT_noreturn(0x87),
DW_AT_alignment(0x88),
DW_AT_export_symbols(0x89),
DW_AT_deleted(0x8a),
DW_AT_defaulted(0x8b),
DW_AT_loclists_base(0x8c),
DW_AT_MIPS_loop_begin(0x2002),
DW_AT_MIPS_tail_loop_begin(0x2003),
DW_AT_MIPS_epilog_begin(0x2004),
DW_AT_MIPS_loop_unroll_factor(0x2005),
DW_AT_MIPS_software_pipeline_depth(0x2006),
DW_AT_MIPS_linkage_name(0x2007),
DW_AT_MIPS_stride(0x2008),
DW_AT_MIPS_abstract_name(0x2009),
DW_AT_MIPS_clone_origin(0x200a),
DW_AT_MIPS_has_inlines(0x200b),
DW_AT_MIPS_stride_byte(0x200c),
DW_AT_MIPS_stride_elem(0x200d),
DW_AT_MIPS_ptr_dopetype(0x200e),
DW_AT_MIPS_allocatable_dopetype(0x200f),
DW_AT_MIPS_assumed_shape_dopetype(0x2010),
DW_AT_MIPS_assumed_size(0x2011),
DW_AT_sf_names(0x2101),
DW_AT_src_info(0x2102),
DW_AT_mac_info(0x2103),
DW_AT_src_coords(0x2104),
DW_AT_body_begin(0x2105),
DW_AT_body_end(0x2106),
DW_AT_GNU_vector(0x2107),
DW_AT_GNU_template_name(0x2110),
DW_AT_GNU_odr_signature(0x210f),
DW_AT_GNU_call_site_value(0x2111),
DW_AT_GNU_all_call_sites(0x2117),
DW_AT_GNU_macros(0x2119),
DW_AT_GNU_dwo_name(0x2130),
DW_AT_GNU_dwo_id(0x2131),
DW_AT_GNU_ranges_base(0x2132),
DW_AT_GNU_addr_base(0x2133),
DW_AT_GNU_pubnames(0x2134),
DW_AT_GNU_pubtypes(0x2135),
DW_AT_GNU_discriminator(0x2136),
DW_AT_BORLAND_property_read(0x3b11),
DW_AT_BORLAND_property_write(0x3b12),
DW_AT_BORLAND_property_implements(0x3b13),
DW_AT_BORLAND_property_index(0x3b14),
DW_AT_BORLAND_property_default(0x3b15),
DW_AT_BORLAND_Delphi_unit(0x3b20),
DW_AT_BORLAND_Delphi_class(0x3b21),
DW_AT_BORLAND_Delphi_record(0x3b22),
DW_AT_BORLAND_Delphi_metaclass(0x3b23),
DW_AT_BORLAND_Delphi_constructor(0x3b24),
DW_AT_BORLAND_Delphi_destructor(0x3b25),
DW_AT_BORLAND_Delphi_anonymous_method(0x3b26),
DW_AT_BORLAND_Delphi_interface(0x3b27),
DW_AT_BORLAND_Delphi_ABI(0x3b28),
DW_AT_BORLAND_Delphi_return(0x3b29),
DW_AT_BORLAND_Delphi_frameptr(0x3b30),
DW_AT_BORLAND_closure(0x3b31),
DW_AT_LLVM_include_path(0x3e00),
DW_AT_LLVM_config_macros(0x3e01),
DW_AT_LLVM_isysroot(0x3e02),
DW_AT_APPLE_optimized(0x3fe1),
DW_AT_APPLE_flags(0x3fe2),
DW_AT_APPLE_isa(0x3fe3),
DW_AT_APPLE_block(0x3fe4),
DW_AT_APPLE_major_runtime_vers(0x3fe5),
DW_AT_APPLE_runtime_class(0x3fe6),
DW_AT_APPLE_omit_frame_ptr(0x3fe7),
DW_AT_APPLE_property_name(0x3fe8),
DW_AT_APPLE_property_getter(0x3fe9),
DW_AT_APPLE_property_setter(0x3fea),
DW_AT_APPLE_property_attribute(0x3feb),
DW_AT_APPLE_objc_complete_type(0x3fec),
DW_AT_APPLE_property(0x3fed),
}
companion object {
fun by(name: String, rawValue: String): DwarfAttribute = by(Attribute.valueOf(name), rawValue)
fun by(attribute: Attribute, rawValue: String) = DwarfAttribute(attribute, rawValue)
}
}
val DwarfAttribute.rvString: String
get() = rawValue.trim().substring(1, rawValue.length - 2)
class DwarfUtilParser() {
val tags = mutableListOf<DwarfTag>()
var currentTag: DwarfTag? = null
var currentAttribute: DwarfAttribute.Attribute? = null
val currentAttributePayload = StringBuilder()
companion object {
val tagRegexp = Regex("^(0x[0-9a-f]{8}):\\ +([^\\ .]*)$")
val attributeRegexp = Regex("^\\s+(DW_AT_[a-zA-Z0-9_]*)\\s+\\((.*)\\)$")
}
fun tag(tag: DwarfTag) {
appendCurrentAttribute()
tags.add(tag)
currentTag = tag
currentAttribute = null
}
private fun appendCurrentAttribute() {
currentTag ?: return
with(currentTag!!) {
currentAttribute ?: return
+DwarfAttribute.by(currentAttribute!!, currentAttributePayload.toString())
currentAttributePayload.clear()
}
}
fun attribute(attribute: DwarfAttribute.Attribute, payload: String) {
appendCurrentAttribute()
currentAttribute = attribute
currentAttributePayload.appendLine(payload)
}
fun parse(reader: Reader): DwarfUtilParser {
reader.forEachLine { line ->
tagRegexp.find(line)?.apply {
val str = this.destructured.component2().trim()
if (str == "NULL")
return@forEachLine
tag(DwarfTag.by(str))
return@forEachLine
}
attributeRegexp.find(line)?.apply {
attribute(DwarfAttribute.Attribute.valueOf(destructured.component1()), destructured.component2())
return@forEachLine
}
currentTag ?: return@forEachLine
currentAttributePayload.appendLine(line)
}
return this
}
}
@@ -1,73 +0,0 @@
package org.jetbrains.kotlin.native.test.debugger
import org.junit.Assert.*
import org.junit.Ignore
import org.junit.Test
class DwarfTests {
@Test
fun `prefix test`() = dwarfDumpTest("""
fun main(args: Array<String>) {
val xs = intArrayOf(3, 5, 8)
return
}
data class Point(val x: Int, val y: Int)
""".trimIndent(), listOf("-Xdebug-prefix-map=${System.getProperty("user.home")}=/xxx")){
val map = flatMap( fun (it: DwarfTag): List<DwarfAttribute> { return it.attributes.values.toList()})
.filter { it.attribute == DwarfAttribute.Attribute.DW_AT_decl_file }.map { it.rvString }
assertNotSame(0, map.size)
}
/**
* TODO: to enable this test it's required to fix issue with poisonig call site with wrong file owner ship of lambda
* passed as parameter to inline function.
*/
@Test
fun `address of VolatileLambda lookup`() = dwarfDumpComplexTest {
val callbackLibrary = """
---
int callback(int (*f)(int)) {
return 42 + f(0xdeadbeef);
}
""".trimIndent().cinterop("callback", "callback")
val trapLibrary = """
---
void trap() {
__builtin_trap();
}
""".trimIndent().cinterop("trap", "trap")
val poisonLibrary = """
package poison
import trap.*
import callback.*
import kotlinx.cinterop.staticCFunction
inline fun execute():Int {
return callback(staticCFunction{
a ->
trap()
2 * a
})
}
""".trimIndent().library("poison", "-l", callbackLibrary.toString(), "-l", trapLibrary.toString())
val binary = """
import poison.*
fun main() {
execute()
}
""".trimIndent().binary("poisoned", "-g", "-l", poisonLibrary.toString(), "-l", callbackLibrary.toString(), "-l", trapLibrary.toString())
binary.dwarfDumpLookup("kfun:main\$lambda$0#internal") {
assertFalse(this.isEmpty())
val subprogram = single { it.tag == DwarfTag.Tag.DW_TAG_subprogram } as DwarfTagSubprogram
assertEquals(subprogram.file!!.name, "poison.kt")
}
}
}
@@ -1,755 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* 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.lldbCheckLineNumbers
import org.jetbrains.kotlin.native.test.debugger.lldbTest
import org.junit.Test
class LldbTests {
@Test
fun `can step through code`() = lldbTest("""
fun main(args: Array<String>) {
var x = 1
var y = 2
var z = x + y
println(z)
}
""", """
> b main.kt:2
Breakpoint 1: [..]
> ${lldbCommandRunOrContinue()}
Process [..] stopped
[..] stop reason = breakpoint 1.1
[..] at main.kt:2[..]
> n
Process [..] stopped
[..] stop reason = step over
[..] at main.kt:3[..]
> n
Process [..] stopped
[..] stop reason = step over
[..] at main.kt:4[..]
> n
Process [..] stopped
[..] stop reason = step over
[..] at main.kt:5[..]
> q
""")
@Test
fun `can inspect values of primitive types`() = lldbTest("""
fun main(args: Array<String>) {
var a: Byte = 1
var b: Int = 2
var c: Long = -3
var d: Char = 'c'
var e: Boolean = true
return
}
""", """
> b main.kt:7
> ${lldbCommandRunOrContinue()}
> fr var
(char) a = '\x01'
(int) b = 2
(long) c = -3
(short) d = 99
(bool) e = true
> q
""")
@Test
fun `can inspect classes`() = lldbTest("""
fun main(args: Array<String>) {
val point = Point(1, 2)
val person = Person()
return
}
data class Point(val x: Int, val y: Int)
class Person {
override fun toString() = "John Doe"
}
""", """
> b main.kt:4
> ${lldbCommandRunOrContinue()}
> fr var
(ObjHeader *) args = []
(ObjHeader *) point = [x: ..., y: ...]
(ObjHeader *) person = []
> q
""")
@Test
fun `can inspect arrays`() = lldbTest("""
fun main(args: Array<String>) {
val xs = IntArray(3)
xs[0] = 1
xs[1] = 2
xs[2] = 3
val ys: Array<Any?> = arrayOfNulls(2)
ys[0] = Point(1, 2)
return
}
data class Point(val x: Int, val y: Int)
""", """
> b main.kt:8
> ${lldbCommandRunOrContinue()}
> fr var
(ObjHeader *) args = []
(ObjHeader *) xs = [..., ..., ...]
(ObjHeader *) ys = [..., ...]
> q
""")
@Test
fun `can inspect array children`() = lldbTest("""
fun main(args: Array<String>) {
val xs = intArrayOf(3, 5, 8)
return
}
data class Point(val x: Int, val y: Int)
""", """
> b main.kt:3
> ${lldbCommandRunOrContinue()}
> fr var xs
(ObjHeader *) xs = [..., ..., ...]
> q
""")
@Test
fun `can inspect catch parameter`() = lldbTest("""
fun main() {
try {
throw Exception("message 1")
} catch (e1: Throwable) {
println(e1.message)
}
try {
throwError()
} catch (e2: Throwable) {
println(e2.message)
}
}
fun throwError() {
throw Error("message 2")
}
""", """
> b main.kt:5
> ${lldbCommandRunOrContinue()}
> fr var
(ObjHeader *) e1 = [..]
> b main.kt:11
> c
> fr var
(ObjHeader *) e2 = [..]
> q
""")
@Test
fun `swift with kotlin static framework`() = lldbComplexTest {
val aKtSrc = """
fun a() = "a"
""".feedOutput("a.kt")
val bKtSrc = """
fun b() = "b"
""".feedOutput("b.kt")
arrayOf(aKtSrc, bKtSrc).framework("AandBFramework", "-g", "-Xstatic-framework")
val swiftSrc = """
import AandBFramework
print(AKt.a())
print(BKt.b())
""".feedOutput("test.swift")
val application = swiftc("application", swiftSrc, "-F", root.toString())
"""
> b kfun:#b(){}kotlin.String
Breakpoint 1: where = [..]`kfun:#b(){}kotlin.String [..] at b.kt:1:1, [..]
> b kfun:#a(){}kotlin.String
Breakpoint 2: where = [..]`kfun:#a(){}kotlin.String [..] at a.kt:1:1, [..]
> q
""".trimIndent().lldb(application)
}
@Test
fun `kt33055`() = lldbComplexTest {
val kt33055 = """
|fun question(subject: String, names: Array<String> = emptyArray()): String {
| return buildString { // breakpoint here
| append("${"$"}subject?") // actual stop
| for (name in names)
| append(" ${"$"}name?")
| }
|}
|
|fun main(args: Array<String>) {
| println(question("Subject", args))
|}
""".trimMargin().binary("kt33055", "-g", "-Xg-generate-debug-trampoline=enable")
"""
> b 2
Breakpoint 1: where = [..]`kfun:#question(kotlin.String;kotlin.Array<kotlin.String>){}kotlin.String [..] at kt33055.kt:2:12, [..]
> q
""".trimIndent().lldb(kt33055)
}
@Test
fun `kt33364`() = lldbComplexTest {
val kt33364 = """
|fun main() {
| val param = 3
|
| //breakpoint here (line: 4, breakpoint is set to 5th line)
| when(param) {
| 1 -> print("A")
| 2 -> print("B")
| else -> print("C")
| }
|
| // breakpoint here (line: 11, breakpoint is set to 12th line)
| when {
| param == 1 -> print("A")
| param == 2 -> print("B")
| else -> print("C")
| }
|}
""".trimMargin().binary("kt33364", "-g", "-Xg-generate-debug-trampoline=enable")
"""
> b 5
Breakpoint 1: where = [..]kfun:#main(){} [..] at kt33364.kt:5:[..]
> b 11
Breakpoint 2: where = [..]kfun:#main(){} [..] at kt33364.kt:12:[..]
> q
""".trimIndent().lldb(kt33364)
}
@Test
fun `kt42208`() = lldbComplexTest {
val kt42208One = """
fun main() {
foo()()
}
""".feedOutput("kt42208-1.kt")
val kt42208Two = """
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
inline fun foo() = {
throw Error()
}
""".feedOutput("kt42208-2.kt")
val binary = arrayOf(kt42208One, kt42208Two).binary("kt42208", "-g")
"""
> b ThrowException
> ${lldbCommandRunOrContinue()}
> bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: [..] kt42208.kexe`ThrowException
frame #1: [..] kt42208.kexe`kfun:main${'$'}lambda${'$'}0#internal at kt42208-2.kt:3:18
frame #2: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.invoke#internal(_this=[..]) at kt42208-1.kt:2:5
frame #3: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.${'$'}<bridge-UNN>invoke(_this=[..]){}kotlin.Nothing#internal at kt42208-1.kt:2:5
frame #4: [..] kt42208.kexe`kfun:#main(){} at kt42208-1.kt:2:5
frame #5: [..] kt42208.kexe`Konan_start(args=[..]) at kt42208-1.kt:1:1
frame #6: [..]
frame #7: [..]
> q
""".trimIndent().lldb(binary)
}
@Test
fun `kt42208 with variable`() = lldbComplexTest {
val kt42208One = """
fun main() {
val a = foo()
a()
a()
a()
}
""".feedOutput("kt42208-1.kt")
val kt42208Two = """
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
class A
val list = mutableListOf<A>()
inline fun foo() = { ->
list.add(A())
}
""".feedOutput("kt42208-2.kt")
val binary = arrayOf(kt42208One, kt42208Two).binary("kt42208", "-g")
"""
> b kt42208-2.kt:5
> ${lldbCommandRunOrContinue()}
> bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: [..] kt42208.kexe`kfun:main${'$'}lambda${'$'}0#internal at kt42208-2.kt:5:5
frame #1: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.invoke#internal(_this=[..]) at kt42208-1.kt:2:5
frame #2: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.${'$'}<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean#internal at kt42208-1.kt:2:5
frame #3: [..] kt42208.kexe`kfun:#main(){} at kt42208-1.kt:3:5
frame #4: [..] kt42208.kexe`Konan_start(args=[..]) at kt42208-1.kt:1:1
frame #5: [..]
> c
> bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: [..] kt42208.kexe`kfun:main${'$'}lambda${'$'}0#internal at kt42208-2.kt:5:5
frame #1: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.invoke#internal(_this=[..]) at kt42208-1.kt:2:5
frame #2: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.${'$'}<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean#internal at kt42208-1.kt:2:5
frame #3: [..] kt42208.kexe`kfun:#main(){} at kt42208-1.kt:4:5
frame #4: [..] kt42208.kexe`Konan_start(args=[..]) at kt42208-1.kt:1:1
> c
> bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: [..] kt42208.kexe`kfun:main${'$'}lambda${'$'}0#internal at kt42208-2.kt:5:5
frame #1: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.invoke#internal(_this=[..]) at kt42208-1.kt:2:5
frame #2: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.${'$'}<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean#internal at kt42208-1.kt:2:5
frame #3: [..] kt42208.kexe`kfun:#main(){} at kt42208-1.kt:5:5
frame #4: [..] kt42208.kexe`Konan_start(args=[..]) at kt42208-1.kt:1:1
> q
""".trimIndent().lldb(binary)
}
@Test
fun `kt42208 with passing lambda to another function`() = lldbComplexTest {
val kt42208One = """
fun main() {
val a = foo()
bar(a)
}
""".feedOutput("kt42208-1.kt")
val kt42208Two = """
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
class A
val list = mutableListOf<A>()
inline fun foo() = { ->
list.add(A())
}
""".feedOutput("kt42208-2.kt")
val kt42208Three = """
fun bar(v:(()->Unit)) {
v()
}
""".feedOutput("kt42208-3.kt")
val binary = arrayOf(kt42208One, kt42208Two, kt42208Three).binary("kt42208", "-g", "-XXLanguage:+UnitConversionsOnArbitraryExpressions")
"""
> b kt42208-2.kt:5
> ${lldbCommandRunOrContinue()}
> bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: [..] kt42208.kexe`kfun:main${'$'}lambda${'$'}0#internal at kt42208-2.kt:5:5
frame #1: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.invoke#internal(_this=[..]) at kt42208-1.kt:2:5
frame #2: [..] kt42208.kexe`kfun:${'$'}main${'$'}lambda${'$'}0${'$'}FUNCTION_REFERENCE${'$'}0.${'$'}<bridge-BNN>invoke(_this=[..]){}kotlin.Boolean#internal at kt42208-1.kt:2:5
frame #3: [..] kt42208.kexe`kfun:#bar(v=[]){} at kt42208-3.kt:2:4
frame #4: [..] kt42208.kexe`kfun:#main(){} at kt42208-1.kt:3:5
frame #5: [..] kt42208.kexe`Konan_start(args=[]) at kt42208-1.kt:1:1
> c
> q
""".trimIndent().lldb(binary)
}
@Test
fun `kt47198`() = lldbComplexTest {
val kt47198 = """
fun foo(a:Int) = print("a: ${'$'}a")
fun main() {
foo(33)
}
""".feedOutput("kt47198.kt")
val binary = arrayOf(kt47198).binary("kt47198", "-g")
"""
> b 1
Breakpoint 1: where = kt47198.kexe`kfun:#foo(kotlin.Int){} [..] at kt47198.kt:1:1, [..]
> ${lldbCommandRunOrContinue()}
> fr v
(int) a = 33
> q
""".trimIndent().lldb(binary)
}
@Test
fun `kt47198 with body`() = lldbComplexTest {
val kt47198 = """
fun foo(a:Int){
print("a: ${'$'}a")
}
fun main() {
foo(33)
}
""".feedOutput("kt47198.kt")
val binary = arrayOf(kt47198).binary("kt47198", "-g")
"""
> b 1
Breakpoint 1: where = kt47198.kexe`kfun:#foo(kotlin.Int){} [..] at kt47198.kt:1:[..]
> ${lldbCommandRunOrContinue()}
> fr v
(int) a = 33
> q
""".trimIndent().lldb(binary)
}
@Test
fun `lldb line numbers are valid in source`() {
// Whitespace is important, since the character offsets are what defines source lines
lldbCheckLineNumbers(mapOf(
"main.kt" to """
fun main() {
inliner {
println("1")
}
inliner {
println("2")
}
}
""", "inliner.kt" to """
${" ".repeat(1000)}
inline fun inliner(block: ()->Unit) {
block()
}
"""), "main.kt:2", 15)
}
@Test
fun `works in native thread state`() = lldbComplexTest {
// This test checks that K/N runtime debug interface properly handles the cases when a calling thread is in native state.
// So we need to stop on a breakpoint in native code, but inspect Kotlin code.
// To achieve that, we set breakpoint on `write` function, and call `println` to trigger it.
// The `test` function is recursive -- this little trick helps us to switch to frame 10 in the debugger,
// and be sure that it is in `test` function regardless of how the inline works for println and its callees.
val program = """
fun main() {
test(10)
}
fun test(n: Int) {
val myData = MyData(1, longArrayOf(2, 3), "four", arrayOf("five", "six"))
val i = 7
val la = longArrayOf(8, 9)
val s = "ten"
val a = arrayOf("eleven")
if (n > 0) test(n - 1)
println("Hello")
}
class MyData(val i: Int, val la: LongArray, val s: String, val a: Array<String>)
""".trimIndent().binary("nativestate", "-g", "-Xbinary=runtimeAssertionsMode=panic")
// Now we can just stop on `write`, switch frame to Kotlin code and try using different debug interface functions, both directly
// and indirectly (through konan_lldb.py, which integrates the built-in lldb capabilities like `frame variable` command with
// K/N debug interface).
"""
> b write
Breakpoint 1: [..]
> ${lldbCommandRunOrContinue()}
[..] stop reason = breakpoint [..]
> frame select 10
-> 12 if (n > 0) test(n - 1)
> frame variable
(int) i = 7
(ObjHeader *) myData = [la: ..., s: ..., a: ..., i: ...]
(ObjHeader *) la = [..., ...]
(ObjHeader *) s = [..]
(ObjHeader *) a = [...]
> expression -- (int32_t)Konan_DebugPrint(s)
ten(int32_t) [..] = 0
> expression -- (int32_t)Konan_DebugPrint(la)
[8, 9](int32_t) [..] = 0
> expression -- (int32_t)Konan_DebugPrint(myData)
MyData@[..](int32_t) [..] = 0
> script lldb.frame.FindVariable("myData").GetChildMemberWithName("i").Dereference().GetValue()
'1'
> q
""".trimIndent().lldb(program)
}
@Test
fun `inline function arguments as expressions are visible`() = lldbTest("""
inline fun foo(
p1: Int, p2: Int, p3: Int, p4: Int, p5: Int, p6: Int, p7: Int,
f: (Int, Int, Int, Int, Int, Int, Int) -> Unit
) {
println()
f(p1, p2, p3, p4, p5, p6, p7)
}
fun bar() = 3
inline fun baz() = 3
fun getCondition() = true
fun getNull(): Int? = null
const val X = 10
fun main() {
val tmp = bar()
foo(
1,
tmp,
tmp + 2,
bar(),
X,
if (getCondition()) 1 else 2,
when {
tmp >= 0 -> 1
tmp < 0 -> 2
else -> 0
}
) { p1, p2, p3, p4, p5, p6, p7 ->
println(p1 + p2 + p3 + p4 + p5 + p6 + p7)
}
foo(
{ 2 + 2 }(),
baz(),
listOf(1, 2, 3).filter { it > 2 }.sum(),
getNull()?.let { it + 1 } ?: 0,
try { bar() } finally { baz() },
"".length,
object : Any() {
override fun hashCode(): Int {
return 1
}
}.hashCode()
) { p1, p2, p3, p4, p5, p6, p7 ->
println(p1 + p2 + p3 + p4 + p5 + p6 + p7)
}
}
""", """
> b main.kt:5
> b main.kt:31
> b main.kt:47
> ${lldbCommandRunOrContinue()}
> v
(int) p1 = 1
(int) p2 = 3
(int) p3 = 5
(int) p4 = 3
(int) p5 = 10
(int) p6 = 1
(int) p7 = 1
> c
> v
(int) p1 = 1
(int) p2 = 3
(int) p3 = 5
(int) p4 = 3
(int) p5 = 10
(int) p6 = 1
(int) p7 = 1
> c
> v
(int) p1 = 4
(int) p2 = 3
(int) p3 = 3
(int) p4 = 0
(int) p5 = 3
(int) p6 = 0
(int) p7 = 1
> c
> v
(int) p1 = 4
(int) p2 = 3
(int) p3 = 3
(int) p4 = 0
(int) p5 = 3
(int) p6 = 0
(int) p7 = 1
> q
""")
@Test
fun `inline lambda anonymous argument is visible`() = lldbTest("""
fun main() {
val list = listOf(1)
list.filter {
it > 2
}
list.map {
it * 2
}
list.find {
it == 1
}
}
""", """
> b main.kt:4
> b main.kt:8
> b main.kt:12
> ${lldbCommandRunOrContinue()}
> v
(int) it = 1
> c
> v
(int) it = 1
> c
> v
(int) it = 1
> q
""")
@Test
fun `inline function arguments of various types are visible`() = lldbTest("""
class A
object B
data class C(val x: Int)
interface I
inline fun foo(a: A, b: B, c: C, i: I, f: (A, B, C, I) -> Unit) {
println()
f(a, b, c, i)
}
fun main() {
val a = A()
val b = B
val c = C(0)
val i = object : I {}
foo(a, b, c, i) { pa, pb, pc, pi ->
println(pa)
println(pb)
println(pc)
println(pi)
}
foo(A(), B, C(0), object : I {}) { pa, pb, pc, pi ->
println(pa)
println(pb)
println(pc)
println(pi)
}
}
""", """
> b main.kt:7
> b main.kt:17
> b main.kt:24
> ${lldbCommandRunOrContinue()}
> v
(ObjHeader *) a = []
(ObjHeader *) b = []
(ObjHeader *) c = [x: ...]
(ObjHeader *) i = []
> c
> v
(ObjHeader *) pa = []
(ObjHeader *) pb = []
(ObjHeader *) pc = [x: ...]
(ObjHeader *) pi = []
> c
> v
(ObjHeader *) a = []
(ObjHeader *) b = []
(ObjHeader *) c = [x: ...]
(ObjHeader *) i = []
> c
> v
(ObjHeader *) pa = []
(ObjHeader *) pb = []
(ObjHeader *) pc = [x: ...]
(ObjHeader *) pi = []
> q
""")
@Test
fun `inline function default parameters are visible`() = lldbTest("""
inline fun foo(x: Int = 0, y: String = "STRING", z: Any? = null) {
println(x)
println(y)
println(z)
}
fun main() {
foo()
foo(1)
foo(1, "TEST_STRING")
foo(1, "TEST_STRING", Any())
}
""", """
> b main.kt:2
> ${lldbCommandRunOrContinue()}
> v
(int) x = 0
(ObjHeader *) y = STRING
(ObjHeader *) z = NULL
> c
> v
(int) x = 1
(ObjHeader *) y = STRING
(ObjHeader *) z = NULL
> c
> v
(int) x = 1
(ObjHeader *) y = TEST_STRING
(ObjHeader *) z = NULL
> c
> v
(int) x = 1
(ObjHeader *) y = TEST_STRING
(ObjHeader *) z = []
> q
""")
@Test
fun `inline function extension receiver is visible`() = lldbTest("""
class A {
inline fun String.foo(f: String.() -> Unit) {
println()
f()
}
fun bar() {
"TEST".foo {
println()
}
}
}
fun main() {
A().bar()
}
""", """
> b main.kt:3
> b main.kt:9
> ${lldbCommandRunOrContinue()}
> v
(ObjHeader *) _this = []
(ObjHeader *) __this = TEST
> c
> v
(ObjHeader *) ${"$"}this${"$"}foo = TEST
> q
""")
}
@@ -1,331 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.native.test.debugger
import org.intellij.lang.annotations.Language
import org.junit.Assert.fail
import java.nio.file.Files
import java.nio.file.Path
/**
* An integration test for debug info.
*
* It works by compiling a given [programText] with debug info,
* then launching lldb, feeding it commands from [lldbSession]
* and matching the output.
*
* [lldbSession] specifies both lldb commands and expected output
* using a DLS, which looks like this:
*
* > b main.kt:5
* Breakpoint 1: [..]
* > r
* Process [..] stopped
* [..] at main.kt:5, [..] stop reason = breakpoint [..]
* > fr var
* (int) a = 92
* (int) b = 2
*
* It consists of blocks of the form
*
* > lldb command
* response line pattern
* another pattern
*
* Command after `>` is passed to lldb exactly. The output of the command
* is then matched against a set of patterns. Matching is done line by line:
* for every pattern, there must be a matching line in the output, but you don't
* have to specify a pattern for every line. In particular, it's possible not to
* specify any patterns at all:
*
* > b main.kt:2
* > r
* > n
* [..] at main.kt:3, [..] stop reason = step over
*
* The patterns themselves are simple. The only special symbol is `[..]` which
* means arbitrary substring, which can help match random data, timings, and OS-dependent output.
* For example, to match
*
* Current executable set to '/tmp/debugger_test7458723719928260513/program.kexe' (x86_64).
*
* one writes
*
* Current executable set to [..]program.kexe[..]
*/
fun lldbTest(@Language("kotlin") programText: String, lldbSession: String) {
lldbReasonToAbort()?.let {
println(it)
return
}
val lldbSessionSpec = LldbSessionSpecification.parse(lldbSession)
val tmpdir = Files.createTempDirectory("debugger_test")
tmpdir.toFile().deleteOnExit()
val source = tmpdir.resolve("main.kt")
val output = tmpdir.resolve("program.kexe")
val driver = ToolDriver()
Files.write(source, programText.trimIndent().toByteArray())
driver.compile(source, output, "-g")
val result = driver.runLldb(output, lldbSessionSpec.commands)
lldbSessionSpec.match(result)
}
fun lldbReasonToAbort() = when {
!haveLldb ->
"Skipping test: no LLDB"
!targetIsHost() && !simulatorTestEnabled() ->
"simulator tests disabled, check 'kotlin.native.test.debugger.simulator.enabled' property"
!isOsxDevToolsEnabled ->
"""Development tools aren't available.
|Please consider to execute:
| ${DistProperties.devToolsSecurity} -enable
|or
| csrutil disable
|to run lldb tests""".trimMargin()
else -> null
}
/**
* Another integration test for debug info.
*
* It works by compiling a given set of [src] files with debug info, then
* launching lldb, running to the given [breakpoint] and "step in" [steps] times.
* It then checks that none of the reached break points correspond to blank
* lines in the given source files.
*/
fun lldbCheckLineNumbers(src: Map<String, String>, breakpoint: String, steps: Int) {
lldbReasonToAbort()?.let {
println(it)
return
}
val tmpdir = Files.createTempDirectory("debugger_test")
tmpdir.toFile().deleteOnExit()
val source = src.map { (filename, content) ->
val path = tmpdir.resolve(filename)
Files.write(path, content.trimIndent().toByteArray())
path
}.toTypedArray()
val output = tmpdir.resolve("program.kexe")
val driver = ToolDriver()
driver.compile(output, source, "-g")
val commands = listOf("b ${breakpoint}", "r") + (1..steps).map { "s" } + listOf("q")
val result = driver.runLldb(output, commands)
val noCodeLine = Regex("^\\s*(//.*)?$")
val validSourceBreaks = src.flatMap { (filename, content) ->
content.lines().withIndex()
.filterNot { noCodeLine.matches(it.value) }
.map{ "$filename:${it.index}"}
}.toSet()
Regex("(${src.keys.joinToString("|")}):\\d+").findAll(result).forEach {
check(it.value in validSourceBreaks, { "${it.value} is not a meaningful debug stop" })
}
}
private val isOsxDevToolsEnabled: Boolean by lazy {
//TODO: add OSX checks.
val rawStatus = subprocess(DistProperties.devToolsSecurity, "-status")
println("> status: $rawStatus")
val r = Regex("^.*\\ (enabled|disabled).$")
r.find(rawStatus.stdout)?.destructured?.component1() == "enabled"
}
fun dwarfDumpTest(@Language("kotlin") programText: String, flags: List<String>, test:List<DwarfTag>.()->Unit) {
if (!haveDwarfDump || !targetIsHost()) {
println("Skipping test")
return
}
with(Files.createTempDirectory("dwarfdump_test")) {
toFile().deleteOnExit()
val source = resolve("main.kt")
val output = resolve("program.kexe")
val driver = ToolDriver()
Files.write(source, programText.trimIndent().toByteArray())
driver.compile(source, output, "-g", *flags.toTypedArray())
driver.runDwarfDump(output, processor = test)
}
}
class ToolDriverHelper(private val driver: ToolDriver, val root:Path) {
fun String.cinterop(pkg:String, output: String):Path {
val def = feedOutput("$output.def")
val lib = root.resolve("$output.klib")
driver.cinterop(def, lib, pkg)
return lib
}
fun String.library(output: String, vararg flags:String) = feedOutput("$output.kt").compile(root.resolve("$output.klib"), "-p", "library", *flags)
fun String.binary(output: String, vararg flags:String)= feedOutput("$output.kt").compile(root.resolve("$output.kexe"), *flags)
private fun Path.compile(output: Path, vararg flags:String) = output.also{ driver.compile(source = this, it, *flags) }
fun Path.dwarfDumpLookup(address: Long, parser:List<DwarfTag>.() -> Unit) = driver.runDwarfDump(this, "-lookup", address.toString(), processor = parser)
fun Path.dwarfDumpLookup(name: String, parser:List<DwarfTag>.() -> Unit) = driver.runDwarfDump(this, "-find", name, processor = parser)
fun String.feedOutput(output: String) = root.resolve(output).also {
Files.write(it, this.trimIndent().toByteArray())
}
fun Array<Path>.framework(name:String, vararg args:String = emptyArray()):Path = root.resolve("$name.framework").also {
driver.compile(it, this, "-produce", "framework", *args)
}
fun Array<Path>.binary(name:String, vararg args:String = emptyArray()):Path = root.resolve("$name.kexe").also {
driver.compile(it, this, *args)
}
fun swiftc(output: String, swiftSrc: Path, vararg args: String) = root.resolve(output).also {
driver.swiftc(it, swiftSrc, *args, "-Xlinker", "-rpath", "-Xlinker", "@executable_path")
}
fun String.lldb(program:Path) {
val lldbSessionSpec = LldbSessionSpecification.parse(this)
val result = driver.runLldb(program, lldbSessionSpec.commands)
lldbSessionSpec.match(result)
}
}
fun dwarfDumpComplexTest(test:ToolDriverHelper.()->Unit) {
if (!haveDwarfDump || !targetIsHost()) {
println("Skipping test")
return
}
with(Files.createTempDirectory("dwarfdump_test_complex")) {
toFile().deleteOnExit()
ToolDriverHelper(ToolDriver(), this).test()
}
}
fun lldbComplexTest(test:ToolDriverHelper.()->Unit) {
if (!haveLldb || !targetIsHost()) {
println("Skipping test")
return
}
with(Files.createTempDirectory("lldb_test_complex")) {
toFile().deleteOnExit()
ToolDriverHelper(ToolDriver(), this).test()
}
}
private class LldbSessionSpecification private constructor(
val commands: List<String>,
val patterns: List<List<String>>
) {
fun match(output: String) {
val blocks = output.split("""(?=\(lldb\))""".toRegex()).filterNot(String::isEmpty)
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 = 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
&& commands.zip(executedCommands).all { (cmd, h) -> h == "(lldb) $cmd" }
if (!responsesMatch) {
val message = """
|Responses do not match commands.
|
|COMMANDS: |$commands (${commands.size})
|RESPONSES: |$executedCommands (${executedCommands.size})
|
|FULL SESSION:
|$output
""".trimMargin()
fail(message)
}
for ((patternBody, command) in patterns.zip(bodies).zip(executedCommands)) {
val (pattern, body) = patternBody
val mismatch = findMismatch(pattern, body)
if (mismatch != null) {
val message = """
|Wrong LLDB output.
|
|COMMAND: $command
|PATTERN: $mismatch
|OUTPUT:
|${body.joinToString("\n")}
|
|FULL SESSION:
|$output
""".trimMargin()
fail(message)
}
}
}
private fun findMismatch(patterns: List<String>, actualLines: List<String>): String? {
val indices = mutableListOf<Int>()
for (pattern in patterns) {
val idx = actualLines.indexOfFirst { match(pattern, it) }
if (idx == -1) {
return pattern
}
indices += idx
}
check(indices == indices.sorted())
return null
}
private fun match(pattern: String, line: String): Boolean {
val chunks = pattern.split("""\s*\[\.\.]\s*""".toRegex())
.filter { it.isNotBlank() }
.map { it.trim() }
check(chunks.isNotEmpty())
val trimmedLine = line.trim()
val indices = chunks.map { trimmedLine.indexOf(it) }
if (indices.any { it == -1 } || indices != indices.sorted()) return false
if (!(trimmedLine.startsWith(chunks.first()) || pattern.startsWith("[..]"))) return false
if (!(trimmedLine.endsWith(chunks.last()) || pattern.endsWith("[..]"))) return false
return true
}
companion object {
fun parse(spec: String): LldbSessionSpecification {
val blocks = spec.trimIndent()
.split("(?=^>)".toRegex(RegexOption.MULTILINE))
.filterNot(String::isEmpty)
for (cmd in blocks) {
check(cmd.startsWith(">")) { "Invalid lldb session specification: $cmd" }
}
val commands = blocks.map { it.lines().first().substring(1).trim() }
val patterns = blocks.map { it.lines().drop(1).filter { it.isNotBlank() } }
return LldbSessionSpecification(commands, patterns)
}
}
}
@@ -1,54 +0,0 @@
/*
* 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"