diff --git a/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c b/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c index fa635619..0c5802df 100644 --- a/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c +++ b/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c @@ -1,599 +1,185 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) -// TODO: -// -// [ ] d2r_types alias size and byte size on __float80 typedef mismatch - -#if 0 // TODO(rjf): this uses concepts from the old parser - replace with p2r-style exemplar testing to catch regressions - -internal RDI_Parsed * -d2r_rdi_from_dwarf_writer(Arena *arena, DW_Writer *writer) +Test(d2r_regressions) { - Temp scratch = scratch_begin(&arena, 1); - - RDI_Parsed *rdi = push_array(arena, RDI_Parsed, 1); - - String8 raw_coff; + String8 radbin_path = test_build_exe_path(arena, s("radbin")); + String8 bin_paths[] = { - OBJ *obj = obj_alloc(0, Arch_x64); - OBJ_Section *text_section = obj_push_section(obj, str8_lit(".text"), OBJ_SectionFlag_Read|OBJ_SectionFlag_Exec|OBJ_SectionFlag_Load); - str8_serial_push_u8(obj->arena, &text_section->data, 0xc3); - obj_push_symbol(obj, str8_lit("main"), OBJ_SymbolScope_Global, OBJ_RefKind_Section, text_section); - dw_writer_emit_to_obj(writer, obj); - raw_coff = coff_from_obj(scratch.arena, obj); - obj_release(&obj); - } - - Assert(t_write_file(str8_lit("a.obj"), raw_coff)); - t_invoke(t_radlink_path(), str8_lit("/subsystem:console /out:a.exe /entry:main /DEBUG:FULL /opt:noref /opt:noicf a.obj"), max_U64); - Assert(g_last_exit_code == 0); - String8 exe = t_read_file(scratch.arena, str8_lit("a.exe")); - Assert(exe.size > 0); - - B32 was_pdb_deleted = delete_file_at_path(t_make_file_path(scratch.arena, str8_lit("a.pdb"))); - Assert(was_pdb_deleted); - - t_invoke(t_radbin_path(), str8_lit("-rdi a.exe -out:a.rdi"), max_U64); - if (g_last_exit_code != 0) { - t_errorf("ERROR: failed to invoke radbin: %S\n", g_errors); - goto exit; - } - - String8 raw_rdi = t_read_file(arena, str8_lit("a.rdi")); - Assert(raw_rdi.size > 0); - - RDI_ParseStatus rdi_parse_status = rdi_parse(raw_rdi.str, raw_rdi.size, rdi); - Assert(rdi_parse_status == RDI_ParseStatus_Good); - - exit:; - scratch_end(scratch); - return rdi; -} - -internal RDI_TypeNode * -d2rt_type_from_name(RDI_Parsed *rdi, RDI_ParsedNameMap *map, char *name) -{ - String8 s = str8_cstring(name); - RDI_NameMapNode *node = rdi_name_map_lookup(rdi, map, s.str, s.size); - - U32 id_count = 0; - U32 *ids = rdi_matches_from_map_node(rdi, node, &id_count); - - if (id_count == 1) { - return rdi_element_from_name_idx(rdi, TypeNodes, ids[0]); - } - return 0; -} - -TEST(d2r_types) -{ - DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); - { - dw_writer_tag_begin(writer, DW_TagKind_CompileUnit); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Producer, "Test"); - -#define DeclBaseType(tt, n, e, s) \ -DW_WriterTag *tt = dw_writer_tag_begin(writer, DW_TagKind_BaseType); \ -dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, s); \ -dw_writer_push_attrib_enum(writer, DW_AttribKind_Encoding, DW_ATE_##e); \ -dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, n); \ -dw_writer_tag_end(writer); - DeclBaseType(char_type, "char", SignedChar, 1); - DeclBaseType(unsigned_char_type, "unsigned char", UnsignedChar, 1); - DeclBaseType(char8_type, "char8_t", Utf, 1); - DeclBaseType(char16_type, "char16_t", Utf, 2); - DeclBaseType(char32_type, "char32_t", Utf, 4); - DeclBaseType(wchar_type, "wchar_t", Signed, 4); - DeclBaseType(bool_type, "_Bool", Boolean, 1); - DeclBaseType(short_type, "short", Signed, 2); - DeclBaseType(unsigned_short_type, "unsigned short", Unsigned, 2); - DeclBaseType(short_unsigned_int_type, "short unsigned int", Unsigned, 2); - DeclBaseType(short_int_type, "short int", Signed, 2); - DeclBaseType(unsigned_int_type, "unsigned int", Unsigned, 4); - DeclBaseType(int_type, "int", Signed, 4); - DeclBaseType(long_int_type, "long int", Signed, 8); - DeclBaseType(long_unsigned_int_type, "long unsigned int", Unsigned, 8); - DeclBaseType(long_long_int_type, "long long int", Signed, 8); - DeclBaseType(long_long_unsigned_int, "long long unsigned int", Unsigned, 8); - DeclBaseType(float_type, "float", Float, 4); - DeclBaseType(double_type, "double", Float, 8); - DeclBaseType(long_double_type, "long double", Float, 16); - DeclBaseType(int128_type, "__int128", Signed, 16); - DeclBaseType(uint128_type, "__int128 unsigned", Unsigned, 16); - DeclBaseType(float16_type, "_Float16", Float, 2); - DeclBaseType(bfloat16_type, "__bf16", Float, 2); - //DeclBaseType(float80_type, "__float80", Float, 16); - DeclBaseType(float128_type, "_float128", Float, 16); - DeclBaseType(complex_float_type, "complex float", ComplexFloat, 8); - DeclBaseType(complex_doulbe_type, "complex double", ComplexFloat, 16); - DeclBaseType(complex_long_double_type, "complex long double", ComplexFloat, 32); - DeclBaseType(decimal32_type, "_Decimal32", DecimalFloat, 4); - DeclBaseType(decimal64_type, "_Decimal64", DecimalFloat, 8); - DeclBaseType(decimal128_type, "_Decimal128", DecimalFloat, 16); -#undef DeclBaseType - -#define DeclStdint(n, a) \ -do { \ -dw_writer_tag_begin(writer, DW_TagKind_Typedef); \ -dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, n); \ -dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, a); \ -dw_writer_tag_end(writer); \ -} while (0) - DeclStdint("uint8_t", unsigned_char_type); - DeclStdint("uint16_t", unsigned_short_type); - DeclStdint("uint32_t", unsigned_int_type); - DeclStdint("uint64_t", long_unsigned_int_type); - DeclStdint("int8_t", char_type); - DeclStdint("int16_t", short_type); - DeclStdint("int32_t", int_type); - DeclStdint("int64_t", long_int_type); -#undef DeclStdInt - // TODO: @native_vector_support - // - // typedef int __attribute__((vector_size(32))) int256 - // typedef unsigned int __attribute__((vector_size(32))) uint256 - // typedef int __attribute__((vector_size(64))) int512 - // typedef unsigned int __attribute__((vector_size(64))) uint512 - // -#if 0 - dw_writer_tag_begin(writer, DW_TagKind_ArrayType); - dw_writer_push_attrib_flag(writer, DW_AttribKind_GNU_Vector, 1); - dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, int_type); - dw_writer_tag_begin(writer, DW_TagKind_SubrangeType); - dw_writer_push_attrib_uint(writer, DW_AttribKind_UpperBound, 15); - dw_writer_tag_end(writer); - dw_writer_tag_end(writer); -#endif - - dw_writer_tag_end(writer); - } - - RDI_Parsed *rdi = d2r_rdi_from_dwarf_writer(arena, writer); - RDI_NameMap *types_nm = rdi_element_from_name_idx(rdi, NameMaps, RDI_NameMapKind_Types); - T_Ok(types_nm); - - RDI_ParsedNameMap types_map = {0}; - rdi_parsed_from_name_map(rdi, types_nm, &types_map); - -#define TestBuiltinType(n, bs, r) \ -do { \ -RDI_TypeNode *alias = d2rt_type_from_name(rdi, &types_map, n); \ -T_Ok(alias); \ -T_Ok(alias->kind == RDI_TypeKind_Alias); \ -T_Ok(alias->flags == 0); \ -T_Ok(alias->byte_size == bs); \ -RDI_TypeNode *type = rdi_element_from_name_idx(rdi, TypeNodes, alias->user_defined.direct_type_idx); \ -T_Ok(type); \ -T_Ok(type->kind == RDI_TypeKind_##r); \ -T_Ok(type->flags == 0); \ -T_Ok(type->byte_size == alias->byte_size); \ -U64 expected_name_size = 0; \ -U8 *expected_name = rdi_string_from_type_kind(RDI_TypeKind_##r, &expected_name_size); \ -T_Ok(str8_match(str8_from_rdi_string_idx(rdi, type->built_in.name_string_idx), str8(expected_name, expected_name_size), 0)); \ -} while (0) - TestBuiltinType("char", 1, Char8); - TestBuiltinType("char8_t", 1, UChar8); - TestBuiltinType("char16_t", 2, UChar16); - TestBuiltinType("char32_t", 4, UChar32); - TestBuiltinType("unsigned char", 1, UChar8); - TestBuiltinType("wchar_t", 4, Char32); - TestBuiltinType("_Bool", 1, Bool); - TestBuiltinType("short", 2, S16); - TestBuiltinType("unsigned short", 2, U16); - TestBuiltinType("short unsigned int", 2, U16); - TestBuiltinType("short int", 2, S16); - TestBuiltinType("unsigned int", 4, U32); - TestBuiltinType("int", 4, S32); - TestBuiltinType("long int", 8, S64); - TestBuiltinType("long unsigned int", 8, U64); - TestBuiltinType("long long int", 8, S64); - TestBuiltinType("float", 4, F32); - TestBuiltinType("double", 8, F64); - TestBuiltinType("long double", 16, F128); - TestBuiltinType("__int128", 16, S128); - TestBuiltinType("__int128 unsigned", 16, U128); - TestBuiltinType("_Float16", 2, F16); - TestBuiltinType("__bf16", 2, BF16); - //TestBuiltinType("__float80", 16, F80); - TestBuiltinType("_float128", 16, F128); - TestBuiltinType("complex float", 8, ComplexF32); - TestBuiltinType("complex double", 16, ComplexF64); - TestBuiltinType("complex long double", 32, ComplexF128); - TestBuiltinType("_Decimal32", 4, Decimal32); - TestBuiltinType("_Decimal64", 8, Decimal64); - TestBuiltinType("_Decimal128", 16, Decimal128); -#undef TestBuiltinType - -#define TestStdint(n, s, t) \ -do { \ -RDI_TypeNode *td = d2rt_type_from_name(rdi, &types_map, n); \ -T_Ok(td); \ -T_Ok(td->kind == RDI_TypeKind_Alias); \ -T_Ok(td->flags == 0); \ -T_Ok(td->byte_size == s); \ -RDI_TypeNode *type = rdi_element_from_name_idx(rdi, TypeNodes, td->user_defined.direct_type_idx); \ -T_Ok(type); \ -T_Ok(type->kind == RDI_TypeKind_Alias); \ -T_Ok(type->flags == 0); \ -T_Ok(type->byte_size = td->byte_size); \ -T_Ok(str8_match(str8_from_rdi_string_idx(rdi, type->built_in.name_string_idx), str8_lit(t), 0)); \ -} while (0) - TestStdint("uint8_t", 1, "unsigned char"); - TestStdint("uint16_t", 2, "unsigned short"); - TestStdint("uint32_t", 4, "unsigned int"); - TestStdint("uint64_t", 8, "long unsigned int"); - TestStdint("int8_t", 1, "char"); - TestStdint("int16_t", 2, "short"); - TestStdint("int32_t", 4, "int"); - TestStdint("int64_t", 8, "long int"); -#undef TestStdint - - dw_writer_end(&writer); -} - -SKIP(d2r_line_table) -{ - DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); - String8 comp_dir = str8_lit("c:/DEVEL/"); - String8 comp_name = str8_lit("test.c"); - - DW_WriterFile *foo_file = dw_writer_new_file(writer, str8_lit("/mnt/C/Devel/foo.c")); - DW_WriterFile *comp_file = dw_writer_new_file(writer, str8f(arena, "%S%S", comp_dir, comp_name)); - - struct { - DW_WriterFile *file; U64 ln; U64 line_size; U64 voff; - } test_table[] = { - { comp_file, 6, 1 }, - { comp_file, 4, 2 }, - { comp_file, 2, 3 }, - { comp_file, 1, 4 }, - { comp_file, 8, 5 }, - { foo_file, 1, 3 }, - { foo_file, 100, 1 }, - { comp_file, max_U32 - 0x100, 10 }, + test_input_path(arena, ctx, s("raddbg_linux_dac63d6b/raddbg")), }; - U64 exe_base = coff_default_exe_base_from_machine(COFF_MachineType_X64); - U64 voff = 0x1000; - for EachElement(i, test_table) { - test_table[i].voff = voff; - dw_writer_line_emit(writer, test_table[i].file, test_table[i].ln, 0, exe_base + voff); - voff += test_table[i].line_size; - } - - // emit one past last line - dw_writer_line_emit(writer, - test_table[ArrayCount(test_table) - 1].file, - test_table[ArrayCount(test_table) - 1].ln, - 0, - exe_base + voff); - - dw_writer_tag_begin(writer, DW_TagKind_CompileUnit); - dw_writer_push_attrib_string(writer, DW_AttribKind_Producer, str8_lit("RAD DWARF WRITER")); - dw_writer_push_attrib_string(writer, DW_AttribKind_CompDir, comp_dir); - dw_writer_push_attrib_string(writer, DW_AttribKind_Name, comp_name); - dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, exe_base); - dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, exe_base + voff); - dw_writer_push_attrib_line_ptr(writer, DW_AttribKind_StmtList, 0); - dw_writer_tag_end(writer); - - d2r_rdi_from_dwarf_writer(arena, writer); - - for EachElement(i, test_table) { - for EachIndex(k, test_table[i].line_size) { - String8 cmdl = str8f(arena, "-voff2line -voff:0x%llx %S", test_table[i].voff + k, t_make_file_path(arena, str8_lit("a.rdi"))); - t_invoke_radbin((char *)cmdl.str); - - if (g_last_exit_code != 0) { - t_errorf("radbin exited with %llu on \"%S\"\n", (unsigned long long)g_last_exit_code, cmdl); - T_Ok(0); - } - - if ( ! t_match_linef(&g_output, "%S:%llu", test_table[i].file->path, test_table[i].ln)) { - t_errorf("ERROR: conversion produced unexpected output for \"radbin %S\"\n" \ - " Expected: %S:%llu\n" \ - " Got: %S\n", - cmdl, - test_table[i].file->path, (unsigned long long)test_table[i].ln, - g_output); - - if (g_verbose) { - t_invoke_radbin("-dump -only:line_tables a.rdi"); - t_errorf("\n================================================================================\n"); - t_errorf("RDI:\n%S\n", g_output); - - t_errorf("================================================================================\n"); - t_invoke_radbin("-dump -only:debug_line a.exe"); - t_errorf("DWARF:\n%S\n", g_output); - - g_output = str8_zero(); - } - - T_Ok(0); - } + // rjf: generate RDIs + { + ProcessList processes = {0}; + for EachElement(bin_idx, bin_paths) + { + String8 bin_path = bin_paths[bin_idx]; + String8 rdi_path = str8f(arena, "%S/%I64u.rdi", ctx->artifacts_path, bin_idx); + Process process = launch_cmd_line(str8f(arena, "%S --rdi --deterministic %S --out:%S", radbin_path, bin_path, rdi_path)); + process_list_push(arena, &processes, process); + } + for EachNode(n, ProcessNode, processes.first) + { + process_join(n->v, max_U64, 0); } } - dw_writer_end(&writer); + // rjf: generate dumps + { + ProcessList processes = {0}; + for EachElement(bin_idx, bin_paths) + { + String8 rdi_path = str8f(arena, "%S/%I64u.rdi", ctx->artifacts_path, bin_idx); + String8 dump_path = str8f(arena, "%S/current_%I64u", ctx->artifacts_path, bin_idx); + Process process = launch_cmd_line(str8f(arena, "%S --dump --deterministic %S --out:%S", radbin_path, rdi_path, dump_path)); + process_list_push(arena, &processes, process); + } + for EachNode(n, ProcessNode, processes.first) + { + process_join(n->v, max_U64, 0); + } + } + + // rjf: check against exemplars + { + for EachElement(bin_idx, bin_paths) + { + Temp scratch = scratch_begin(&arena, 1); + String8 dump_path_current = str8f(scratch.arena, "%S/current_%I64u", ctx->artifacts_path, bin_idx); + String8 dump_data_current = data_from_file_path(scratch.arena, dump_path_current); + String8 dump_path_exemplar = test_exemplar_path(scratch.arena, ctx, str8f(scratch.arena, "exemplar_%I64u", bin_idx)); + String8 dump_data_exemplar = data_from_file_path(scratch.arena, dump_path_exemplar); + B32 dump_matches = str8_match(dump_data_current, dump_data_exemplar, 0); + if(!dump_matches) + { + String8 diff_cmd = str8f(scratch.arena, "diff %S %S", + path_normalized_from_string(scratch.arena, dump_path_current), + path_normalized_from_string(scratch.arena, dump_path_exemplar)); + test_outf("Current log does not match exemplar; run `%S`\n", diff_cmd); + } + TestCheck(dump_matches); + scratch_end(scratch); + } + } } -SKIP(d2r_checksums) +Test(d2r_determinism) { - DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); - - B32 is_ok = 0; - - DW_WriterFile *foo_file = dw_writer_new_file(writer, str8_lit("/mnt/c/devel/foo.c")); - DW_WriterFile *comp_file = dw_writer_new_file(writer, str8_lit("/home/main.c")); - foo_file->md5 = *(U128 *)&(U8[16]){ 0x04, 0x70, 0xe9, 0x6b, 0xa3, 0x05, 0x2c, 0xc8, 0x2d, 0x70, 0xc5, 0xe9, 0x80, 0x8e, 0x8a, 0x4e, }; - comp_file->md5 = *(U128 *)&(U8[16]){ 0x82, 0x3c, 0xc6, 0x02, 0x82, 0x9e, 0xf6, 0xce, 0x53, 0xff, 0x5a, 0x93, 0xb6, 0x6e, 0x59, 0x38 }; - comp_file->time_stamp = 123; // convert must pick MD5 checksum over time stamp - - dw_writer_tag_begin(writer, DW_TagKind_CompileUnit); - dw_writer_push_attrib_string(writer, DW_AttribKind_Producer, str8_lit("RAD DWARF WRITER")); - dw_writer_push_attrib_string(writer, DW_AttribKind_CompDir, str8_chop_last_slash(comp_file->path)); - dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_skip_last_slash(comp_file->path)); - dw_writer_push_attrib_line_ptr(writer, DW_AttribKind_StmtList, 0); - dw_writer_tag_end(writer); - - RDI_Parsed *rdi = d2r_rdi_from_dwarf_writer(arena, writer); - U64 checksum_count = 0; - RDI_MD5 *checksums = rdi_table_from_name(rdi, MD5Checksums, &checksum_count); - if (checksum_count != writer->line.file_count + 1) { goto exit; } - - RDI_SourceFile *foo_src_file = rdi_source_file_from_normal_path_cstr(rdi, (char *)foo_file->path.str); - if (foo_src_file == 0) { - t_errorf("ERROR: RDI is missing file %S\n", foo_file->path); - goto exit; - } - if (foo_src_file->checksum_kind != RDI_ChecksumKind_MD5) { - t_errorf("ERROR: File %S has unexpected checksum value %u, expected %u\n", foo_file->path, foo_src_file->checksum_kind, RDI_ChecksumKind_MD5); - goto exit; - } - if (MemoryMatch(&foo_file->md5, &checksums[foo_src_file->checksum_idx], sizeof(U128)) != 1) { - t_errorf("ERROR mismatched checksum in file %S\n", foo_file->path); - goto exit; - } - - RDI_SourceFile *comp_src_file = rdi_source_file_from_normal_path_cstr(rdi, (char *)comp_file->path.str); - if (comp_src_file == 0) { - t_errorf("ERROR: RDI is missing file %S\n", comp_file->path); - goto exit; - } - if (comp_src_file->checksum_kind != RDI_ChecksumKind_MD5) { - t_errorf("ERROR: File %S has unexpected checksum value %u, expected %u\n", comp_file->path, comp_src_file->checksum_kind, RDI_ChecksumKind_MD5); - goto exit; - } - if (MemoryMatch(&comp_file->md5, &checksums[comp_src_file->checksum_idx], sizeof(U128)) != 1) { - t_errorf("ERROR mismatched checksum in file %S\n", comp_file->path); - goto exit; - } - - is_ok = 1; - exit:; - if ( ! is_ok) { - t_errorf("Converter repro: \"radbin -rdi %S\"\n", t_make_file_path(arena, str8_lit("a.exe"))); - t_invoke(t_radbin_path(), str8_lit("-dump -only:source_files a.rdi"), max_U64); - t_errorf("RDI:\n%S\n", g_output); - t_invoke(t_radbin_path(), str8_lit("-dump -only:debug_line a.exe"), max_U64); - t_errorf("DWARF:\n%S\n", g_output); - } - dw_writer_end(&writer); -} - -#if SUBPROGRAM_CONVERSION_TEST -TEST(d2r_subprogram) -{ - DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); - - U64 image_lo = coff_default_exe_base_from_machine(COFF_MachineType_X64); - U64 image_hi = image_lo + 0x10000; - U64 subprogram_lo = image_lo + 0x1000; - U64 subprogram_hi = subprogram_lo + 0x100; - U64 subprogram_entry_addr = subprogram_lo + 5; - String8 subprogram_link_name = str8_lit("@FOOBAR!"); - String8 subprogram_name = str8_lit("foobar"); - - dw_writer_tag_begin(writer, DW_TagKind_CompileUnit); - dw_writer_push_attrib_string (writer, DW_AttribKind_Producer, str8_lit("RAD DWARF WRITER")); - dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, image_lo); - dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, image_hi); - - DW_WriterTag *char_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType); - dw_writer_push_attrib_sint (writer, DW_AttribKind_ByteSize, 1); - dw_writer_push_attrib_sint (writer, DW_AttribKind_Encoding, DW_ATE_SignedChar); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "char"); - dw_writer_tag_end(writer); - - DW_WriterTag *char_ptr_type = dw_writer_tag_begin(writer, DW_TagKind_PointerType); - dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, 8); - dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, char_type); - dw_writer_tag_end(writer); - - DW_WriterTag *int_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType); - dw_writer_push_attrib_sint (writer, DW_AttribKind_ByteSize, 4); - dw_writer_push_attrib_sint (writer, DW_AttribKind_Encoding, DW_ATE_Signed); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "int"); - dw_writer_tag_end(writer); - - DW_WriterTag *int_ptr_type = dw_writer_tag_begin(writer, DW_TagKind_PointerType); - dw_writer_push_attrib_uint(writer, DW_AttribKind_ByteSize, 8); - dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, int_type); - dw_writer_tag_end(writer); - - dw_writer_tag_begin(writer, DW_TagKind_SubProgram); - dw_writer_push_attrib_enum (writer, DW_AttribKind_Accessibility, DW_AccessKind_Private); - dw_writer_push_attrib_enum (writer, DW_AttribKind_AddressClass, DW_AddrClassKind_None); - dw_writer_push_attrib_uint (writer, DW_AttribKind_Alignment, 32); - dw_writer_push_attrib_flag (writer, DW_AttribKind_Artificial, 1); - dw_writer_push_attrib_enum (writer, DW_AttribKind_CallingConvention, DW_CallingConventionKind_Program); - dw_writer_push_attrib_flag (writer, DW_AttribKind_Deleted, 1); - dw_writer_push_attrib_address(writer, DW_AttribKind_EntryPc, subprogram_entry_addr); - dw_writer_push_attrib_flag (writer, DW_AttribKind_Explicit, 1); - dw_writer_push_attrib_flag (writer, DW_AttribKind_External, 1); - dw_writer_push_attrib_exprv (writer, DW_AttribKind_FrameBase, DW_ExprEnc_Op(Reg7)); - dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, subprogram_hi); - dw_writer_push_attrib_enum (writer, DW_AttribKind_Inline, DW_Inl_DeclaredNotInlined); - dw_writer_push_attrib_string (writer, DW_AttribKind_LinkageName, subprogram_link_name); - dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, subprogram_lo); - dw_writer_push_attrib_flag (writer, DW_AttribKind_MainSubProgram, 1); - dw_writer_push_attrib_string (writer, DW_AttribKind_Name, subprogram_name); - dw_writer_push_attrib_flag (writer, DW_AttribKind_NoReturn, 1); - dw_writer_push_attrib_flag (writer, DW_AttribKind_Prototyped, 1); - dw_writer_push_attrib_flag (writer, DW_AttribKind_Pure, 1); - dw_writer_push_attrib_flag (writer, DW_AttribKind_Recursive, 1); - dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, int_type); - dw_writer_push_attrib_enum (writer, DW_AttribKind_Visibility, DW_Vis_Local); - // TODO: DW_AttribKind_ObjectPointer - // TODO: DW_AttribKind_Ranges - // TODO: DW_AttribKind_StartScope - dw_writer_tag_begin(writer, DW_TagKind_FormalParameter); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "a"); - dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, int_ptr_type); - dw_writer_push_attrib_exprv (writer, DW_AttribKind_Location, DW_ExprEnc_Op(Reg2)); // rcx - dw_writer_tag_end(writer); - - dw_writer_tag_begin(writer, DW_TagKind_FormalParameter); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "b"); - dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, char_ptr_type); - dw_writer_push_attrib_exprv (writer, DW_AttribKind_Location, DW_ExprEnc_Op(Reg5)); // rdi - dw_writer_tag_end(writer); - - dw_writer_tag_begin(writer, DW_TagKind_UnspecifiedParameters); - dw_writer_tag_end(writer); - dw_writer_tag_end(writer); - - // -------------------------------------------------------------------------------- - - DW_WriterTag *my_struct_type = dw_writer_tag_reserve(writer, DW_TagKind_StructureType); - - DW_WriterTag *const_my_struct_type = dw_writer_tag_begin(writer, DW_TagKind_ConstType); - dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, my_struct_type); - dw_writer_tag_end(writer); - - DW_WriterTag *my_struct_ptr_type = dw_writer_tag_begin(writer, DW_TagKind_PointerType); - dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, const_my_struct_type); - dw_writer_tag_end(writer); - - dw_writer_tag_begin_reserved(writer, my_struct_type); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "MyStructure"); - dw_writer_push_attrib_uint (writer, DW_AttribKind_ByteSize, 0x100); - - dw_writer_tag_begin(writer, DW_TagKind_SubProgram); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "MyMethod"); - dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, int_ptr_type); - - dw_writer_tag_begin(writer, DW_TagKind_FormalParameter); - dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, my_struct_ptr_type); - dw_writer_tag_end(writer); - dw_writer_tag_end(writer); - dw_writer_tag_end(writer); - - // -------------------------------------------------------------------------------- - - dw_writer_tag_end(writer); - - RDI_Parsed *rdi = d2r_rdi_from_dwarf_writer(arena, writer); - RDI_Procedure *proc = rdi_procedure_from_name_cstr(rdi, (char *)subprogram_name.str); - RDI_TypeNode *proc_type = rdi_element_from_name_idx(rdi, TypeNodes, proc->type_idx); - String8 proc_string = rdi_string_from_type(arena, rdi, proc, proc_type); - - dw_writer_end(&writer); -} -#endif - -TEST(d2r_general) -{ - DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); + U64 num_repeats_per_bin = 16; + String8 radbin_path = test_build_exe_path(arena, s("radbin")); + String8 bin_paths[] = { - dw_writer_tag_begin(writer, DW_TagKind_CompileUnit); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Producer, "Test"); - // declare char type - DW_WriterTag *char_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType); - dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, 1); - dw_writer_push_attrib_enum(writer, DW_AttribKind_Encoding, DW_ATE_SignedChar); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "char"); - dw_writer_tag_end(writer); - // declare function - dw_writer_tag_begin(writer, DW_TagKind_SubProgram); - dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, 0x140173f9); - dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, 0x14017474b); - dw_writer_push_attrib_flag(writer, DW_AttribKind_External, 1); - dw_writer_push_attrib_flag(writer, DW_AttribKind_Prototyped, 1); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "FooBar"); - // declare local - dw_writer_tag_begin(writer, DW_TagKind_Variable); - dw_writer_push_attrib_exprv(writer, DW_AttribKind_Location, DW_ExprEnc_Op(Reg7)); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "TestLocal"); - dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, char_type); - dw_writer_tag_end(writer); - // declare param - dw_writer_tag_begin(writer, DW_TagKind_FormalParameter); - dw_writer_push_attrib_exprv(writer, DW_AttribKind_Location, DW_ExprEnc_Op(Reg7)); - dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "TestParam"); - dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, char_type); - dw_writer_tag_end(writer); - dw_writer_tag_end(writer); - } - - RDI_Parsed *rdi = d2r_rdi_from_dwarf_writer(arena, writer); - - RDI_Symbol *proc = rdi_procedure_from_name_cstr(rdi, "FooBar"); - T_Ok(proc); - String8 proc_name = str8_from_rdi_string_idx(rdi, proc->name_string_idx); - T_Ok(str8_match(proc_name, str8_lit("FooBar"), 0)); - - RDI_Scope *root_scope = rdi_root_scope_from_procedure(rdi, proc); - T_Ok(root_scope); - T_Ok(root_scope->local_count == 2); - + test_input_path(arena, ctx, s("raddbg_linux_dac63d6b/raddbg")), + }; + for EachElement(bin_idx, bin_paths) { - RDI_Symbol *test_local = rdi_element_from_name_idx(rdi, LocalVariables, root_scope->local_first + 0); - T_Ok(test_local); - T_Ok((test_local->symbol_flags & RDI_SymbolFlag_IsParam) == 0); - String8 test_local_name = str8_from_rdi_string_idx(rdi, test_local->name_string_idx); - T_Ok(str8_match(test_local_name, str8_lit("TestLocal"), 0)); + // rjf: unpack paths, make output directory + String8 bin_path = bin_paths[bin_idx]; - RDI_TypeNode *test_local_type = rdi_element_from_name_idx(rdi, TypeNodes, test_local->type_idx); - T_Ok(test_local_type); - T_Ok(test_local_type->kind == RDI_TypeKind_Alias); - T_Ok(test_local_type->flags == 0); - String8 alias_name = str8_from_rdi_string_idx(rdi, test_local_type->user_defined.name_string_idx); - T_Ok(str8_match(alias_name, str8_lit("char"), 0)); + // rjf: generate all RDIs + String8List rdi_paths = {0}; + String8List dump_paths = {0}; + { + ProcessList processes = {0}; + for EachIndex(repeat_idx, num_repeats_per_bin) + { + String8 rdi_name = str8f(arena, "repeat_%I64u.rdi", repeat_idx); + String8 rdi_path = str8f(arena, "%S/%S", ctx->artifacts_path, rdi_name); + str8_list_push(arena, &rdi_paths, rdi_path); + String8 cmdl = str8f(arena, "%S --rdi --deterministic %S --out:%S", radbin_path, bin_path, rdi_path); + Process process = launch_cmd_line(cmdl); + TestCheck(!process_match(process_zero(), process)); + process_list_push(arena, &processes, process); + } + for EachNode(n, ProcessNode, processes.first) + { + process_join(n->v, max_U64, 0); + } + } - RDI_TypeNode *char_type = rdi_element_from_name_idx(rdi, TypeNodes, test_local_type->user_defined.direct_type_idx); - T_Ok(char_type); - T_Ok(char_type->kind == RDI_TypeKind_Char8); - T_Ok(char_type->flags == 0); - T_Ok(char_type->byte_size == 1); - String8 char_type_name = str8_from_rdi_string_idx(rdi, char_type->built_in.name_string_idx); - T_Ok(str8_match(char_type_name, str8_lit("Char8"), 0)); + // rjf: generate all dumps + { + ProcessList processes = {0}; + for EachNode(n, String8Node, rdi_paths.first) + { + String8 rdi_path = n->string; + String8 dump_path = str8f(arena, "%S.dump", rdi_path); + str8_list_push(arena, &dump_paths, dump_path); + Process process_handle = launch_cmd_linef("%S --dump --deterministic %S --out:%S", radbin_path, rdi_path, dump_path); + TestCheck(!process_match(process_zero(), process_handle)); + process_list_push(arena, &processes, process_handle); + } + for EachNode(n, ProcessNode, processes.first) + { + process_join(n->v, max_U64, 0); + } + } + + // rjf: gather all hashes/paths + U64 rdi_hashes_count = rdi_paths.node_count; + U128 *rdi_hashes = push_array(arena, U128, rdi_hashes_count); + String8 *rdi_paths_array = push_array(arena, String8, rdi_hashes_count); + U64 dump_hashes_count = dump_paths.node_count; + U128 *dump_hashes = push_array(arena, U128, dump_hashes_count); + String8 *dump_paths_array = push_array(arena, String8, dump_hashes_count); + { + U64 idx = 0; + for EachNode(n, String8Node, rdi_paths.first) + { + Temp scratch = scratch_begin(0, 0); + String8 rdi_path = n->string; + String8 path = rdi_path; + String8 data = data_from_file_path(scratch.arena, path); + TestCheck(data.size != 0); + rdi_hashes[idx] = u128_hash_from_str8(data); + rdi_paths_array[idx] = path; + scratch_end(scratch); + idx += 1; + } + } + { + U64 idx = 0; + for EachNode(n, String8Node, dump_paths.first) + { + Temp scratch = scratch_begin(0, 0); + String8 path = n->string; + String8 data = data_from_file_path(scratch.arena, path); + TestCheck(data.size != 0); + dump_hashes[idx] = u128_hash_from_str8(data); + dump_paths_array[idx] = path; + scratch_end(scratch); + idx += 1; + } + } + + // rjf: determine if all hashes match + U64 mismatch_num = 0; + for EachIndex(idx, rdi_hashes_count) + { + if(!u128_match(rdi_hashes[idx], rdi_hashes[0])) + { + mismatch_num = idx+1; + break; + } + } + for EachIndex(idx, dump_hashes_count) + { + if(!u128_match(dump_hashes[idx], dump_hashes[0])) + { + mismatch_num = idx+1; + break; + } + } + + // rjf: output bad case info + if(mismatch_num != 0) + { + U64 idx = mismatch_num-1; + test_outf(" bin[%I64u] \"%S\"\n", idx, bin_path); + test_outf(" rdi[%I64u] 0x%I64x:%I64x \"%S\"\n", idx, rdi_hashes[idx].u64[0], rdi_hashes[idx].u64[1], rdi_paths_array[idx]); + test_outf(" dump[%I64u] 0x%I64x:%I64x \"%S\"\n", idx, dump_hashes[idx].u64[0], dump_hashes[idx].u64[1], dump_paths_array[idx]); + } + TestCheck(mismatch_num == 0); } - - { - RDI_Symbol *test_local = rdi_element_from_name_idx(rdi, LocalVariables, root_scope->local_first + 1); - T_Ok(test_local); - T_Ok((test_local->symbol_flags & RDI_SymbolFlag_IsParam) != 0); - String8 test_local_name = str8_from_rdi_string_idx(rdi, test_local->name_string_idx); - T_Ok(str8_match(test_local_name, str8_lit("TestParam"), 0)); - - RDI_TypeNode *test_local_type = rdi_element_from_name_idx(rdi, TypeNodes, test_local->type_idx); - T_Ok(test_local_type); - T_Ok(test_local_type->kind == RDI_TypeKind_Alias); - T_Ok(test_local_type->flags == 0); - String8 alias_name = str8_from_rdi_string_idx(rdi, test_local_type->user_defined.name_string_idx); - T_Ok(str8_match(alias_name, str8_lit("char"), 0)); - - RDI_TypeNode *char_type = rdi_element_from_name_idx(rdi, TypeNodes, test_local_type->user_defined.direct_type_idx); - T_Ok(char_type); - T_Ok(char_type->kind == RDI_TypeKind_Char8); - T_Ok(char_type->flags == 0); - T_Ok(char_type->byte_size == 1); - String8 char_type_name = str8_from_rdi_string_idx(rdi, char_type->built_in.name_string_idx); - T_Ok(str8_match(char_type_name, str8_lit("Char8"), 0)); - } - - dw_writer_end(&writer); } -#endif