rdi_from_dwarf: determinism / regression tests

This commit is contained in:
Ryan Fleury
2026-06-21 20:56:46 -07:00
parent dac63d6bef
commit 73e9f3c9da
+166 -580
View File
@@ -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