[test][dwarfdump] infrostructure for parsing dwarfdump output

This commit is contained in:
Vasily Levchenko
2020-06-29 13:34:59 +02:00
parent a92e428289
commit 4041efb88f
4 changed files with 416 additions and 1 deletions
@@ -16,6 +16,7 @@ object DistProperties {
val konanc: Path = dist.resolve("bin/$konancDriver")
val lldb: Path = Paths.get("lldb")
val devToolsSecurity: Path = Paths.get("DevToolsSecurity")
val dwarfDump: Path = Paths.get("dwarfdump")
val lldbPrettyPrinters: Path = dist.resolve("tools/konan_lldb.py")
private fun requireProp(name: String): String
@@ -19,9 +19,14 @@ import java.nio.file.Path
import java.time.temporal.ChronoUnit
import java.util.concurrent.TimeUnit
/**
* TODO: This constructor looks weird in conjunction with [DistProperties]
*/
class ToolDriver(
private val konancDriver: Path,
private val lldb: Path,
private val dwarfDump: Path,
private val lldbPrettyPrinters: Path,
private val useInProcessCompiler: Boolean = false
) {
@@ -46,6 +51,11 @@ class ToolDriver(
.thrownIfFailed()
.stdout
}
fun runDwarfDump(program: Path, processor:List<DwarfTag>.()->Unit) {
val out = subprocess(dwarfDump, "${program}.dSYM/Contents/Resources/DWARF/${program.fileName}").takeIf { it.process.exitValue() == 0 }?.stdout ?: error("${program}.dSYM/Contents/Resources/DWARF/${program.fileName}")
DwarfUtilParser().parse(StringReader(out)).tags.toList().processor()
}
}
data class ProcessOutput(
@@ -0,0 +1,363 @@
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}):\\ {3}(.*)$")
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.appendln(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
}
}
@@ -78,7 +78,7 @@ fun lldbTest(@Language("kotlin") programText: String, lldbSession: String) {
val source = tmpdir.resolve("main.kt")
val output = tmpdir.resolve("program.kexe")
val driver = ToolDriver(DistProperties.konanc, DistProperties.lldb, DistProperties.lldbPrettyPrinters)
val driver = ToolDriver(DistProperties.konanc, DistProperties.lldb, DistProperties.dwarfDump, DistProperties.lldbPrettyPrinters)
Files.write(source, programText.trimIndent().toByteArray())
driver.compile(source, output, "-g")
val result = driver.runLldb(output, lldbSessionSpec.commands)
@@ -94,6 +94,47 @@ private val isOsxDevToolsEnabled: Boolean by lazy {
r.find(rawStatus.stdout)?.destructured?.component1() == "enabled"
}
fun dwarfDumpTest(@Language("kotlin") programText: String, flags: List<String>, test:List<DwarfTag>.()->Unit) {
if (!haveDwarfDump) {
println("Skipping test: no dwarfdump")
return
}
with(Files.createTempDirectory("dwarfdump_test")) {
toFile().deleteOnExit()
val source = resolve("main.kt")
val output = resolve("program.kexe")
val driver = ToolDriver(
DistProperties.konanc,
DistProperties.lldb,
DistProperties.dwarfDump,
DistProperties.lldbPrettyPrinters)
Files.write(source, programText.trimIndent().toByteArray())
driver.compile(source, output, "-g", *flags.toTypedArray())
driver.runDwarfDump(output, test)
}
}
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")