From 4e72553006e33db3aeb8eecf68637ae61f852205 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Sat, 23 May 2026 13:44:33 -0700 Subject: [PATCH] promote test-definition concepts from torture to base layer; reorganize tests to be nested in their respective layer; eliminate 't_group', just use containing layer --- project.4coder | 2 +- project.raddbg | 7 +- src/base/base_context_cracking.h | 4 + src/base/base_core.h | 15 + src/base/base_inc.c | 1 + src/base/base_inc.h | 1 + src/base/base_strings.c | 8 + src/base/base_test.c | 2 + src/base/base_test.h | 85 ++ .../tests/base_tests.c} | 117 ++- .../tests/dwarf_tests.c} | 753 +++++++++--------- .../tests/linker_tests.c} | 34 +- src/linux/base/linux_base.c | 21 +- .../tests/mdesk_tests.c} | 7 +- .../old_test_debuggees}/basic_stepping_test.c | 0 .../old_test_debuggees}/gnu_sysv_hash.c | 0 .../old_test_debuggees}/mule.cpp | 0 .../old_test_debuggees}/step_into_for_goto.c | 0 .../step_into_lazy_module_func.c | 0 .../step_into_static_module_func.c | 74 +- .../tests/raddbg_tests.c} | 371 +++++++-- .../tests/rdi_from_dwarf_tests.c} | 0 .../tests/rdi_from_pdb_tests.c} | 0 src/torture/torture.c | 288 ++++--- src/torture/torture.h | 83 +- src/torture/torture_dbg.h | 205 ----- src/torture/torture_main.c | 19 +- src/torture/torture_radlink.h | 28 - src/win32/base/win32_base.c | 10 + 29 files changed, 1090 insertions(+), 1045 deletions(-) create mode 100644 src/base/base_test.c create mode 100644 src/base/base_test.h rename src/{torture/torture_base.c => base/tests/base_tests.c} (97%) rename src/{torture/torture_dwarf.c => dwarf/tests/dwarf_tests.c} (56%) rename src/{torture/torture_radlink.c => linker/tests/linker_tests.c} (99%) rename src/{torture/torture_md.c => mdesk/tests/mdesk_tests.c} (96%) rename src/{torture/dbg_tests => mule/old_test_debuggees}/basic_stepping_test.c (100%) rename src/{torture/dbg_tests => mule/old_test_debuggees}/gnu_sysv_hash.c (100%) rename src/{torture/dbg_tests => mule/old_test_debuggees}/mule.cpp (100%) rename src/{torture/dbg_tests => mule/old_test_debuggees}/step_into_for_goto.c (100%) rename src/{torture/dbg_tests => mule/old_test_debuggees}/step_into_lazy_module_func.c (100%) rename src/{torture/dbg_tests => mule/old_test_debuggees}/step_into_static_module_func.c (95%) rename src/{torture/torture_dbg.c => raddbg/tests/raddbg_tests.c} (83%) rename src/{torture/torture_d2r.c => rdi_from_dwarf/tests/rdi_from_dwarf_tests.c} (100%) rename src/{torture/torture_p2r.c => rdi_from_pdb/tests/rdi_from_pdb_tests.c} (100%) delete mode 100644 src/torture/torture_dbg.h delete mode 100644 src/torture/torture_radlink.h diff --git a/project.4coder b/project.4coder index e793b63f..24737b61 100644 --- a/project.4coder +++ b/project.4coder @@ -49,7 +49,7 @@ commands = // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, // .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, - .f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, + .f1 = { .win = "raddbg_stable --ipc kill_all && build torture", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, //- rjf: [raddbg wsl] // .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, diff --git a/project.raddbg b/project.raddbg index e3cfcebd..97eceeb8 100644 --- a/project.raddbg +++ b/project.raddbg @@ -6,7 +6,6 @@ target: executable: "build/raddbg.exe" working_directory: build arguments: "--user:raddbg_test.user --project:raddbg_test.project" - enabled: 1 debug_subprocesses: 0 } target: @@ -38,3 +37,9 @@ target: working_directory: build arguments: "--rdi raddbg.pdb" } +target: +{ + executable: "build/torture.exe" + working_directory: "build/" + enabled: 1 +} diff --git a/src/base/base_context_cracking.h b/src/base/base_context_cracking.h index 2a31c48d..87b32b45 100644 --- a/src/base/base_context_cracking.h +++ b/src/base/base_context_cracking.h @@ -138,6 +138,10 @@ # define BUILD_DEBUG 1 #endif +#if !defined(BUILD_TESTS) +# define BUILD_TESTS 0 +#endif + #if !defined(BUILD_SUPPLEMENTARY_UNIT) # define BUILD_SUPPLEMENTARY_UNIT 0 #endif diff --git a/src/base/base_core.h b/src/base/base_core.h index 73d265c5..2bad9a82 100644 --- a/src/base/base_core.h +++ b/src/base/base_core.h @@ -670,6 +670,16 @@ Compiler; # define Compiler_CURRENT Compiler_Null #endif +typedef enum Linker +{ + Linker_Null, + Linker_radlink, + Linker_msvc, + Linker_lld, + Linker_COUNT +} +Linker; + //////////////////////////////// //~ rjf: Access Flags @@ -1076,6 +1086,11 @@ internal DateTime date_time_from_dense_time(DenseTime time); internal DateTime date_time_from_micro_seconds(U64 time); internal DateTime date_time_from_unix_time(U64 unix_time); +//////////////////////////////// +//~ rjf: @per_os_impl Debugger Attachment Checking + +internal B32 debugger_is_attached(void); + //////////////////////////////// //~ rjf: @per_os_impl Platform Time Functions diff --git a/src/base/base_inc.c b/src/base/base_inc.c index 683806aa..ab242f81 100644 --- a/src/base/base_inc.c +++ b/src/base/base_inc.c @@ -25,6 +25,7 @@ #include "base_markup.c" #include "base_meta.c" #include "base_log.c" +#include "base_test.c" #include "base_entry_point.c" #if OS_WINDOWS diff --git a/src/base/base_inc.h b/src/base/base_inc.h index adaac62d..bc8d568d 100644 --- a/src/base/base_inc.h +++ b/src/base/base_inc.h @@ -27,6 +27,7 @@ #include "base_markup.h" #include "base_meta.h" #include "base_log.h" +#include "base_test.h" #include "base_entry_point.h" #if OS_WINDOWS diff --git a/src/base/base_strings.c b/src/base/base_strings.c index 70d538d6..45c8a7a3 100644 --- a/src/base/base_strings.c +++ b/src/base/base_strings.c @@ -423,6 +423,10 @@ str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags { needle_first_char_adjusted = upper_from_char(needle_first_char_adjusted); } + if(adjusted_flags & StringMatchFlag_SlashInsensitive) + { + needle_first_char_adjusted = correct_slash_from_char(needle_first_char_adjusted); + } for(;p < stop_p; p += 1) { U8 haystack_char_adjusted = *p; @@ -430,6 +434,10 @@ str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags { haystack_char_adjusted = upper_from_char(haystack_char_adjusted); } + if(adjusted_flags & StringMatchFlag_SlashInsensitive) + { + haystack_char_adjusted = correct_slash_from_char(haystack_char_adjusted); + } if(haystack_char_adjusted == needle_first_char_adjusted) { if(str8_match(str8_range(p + 1, string_opl), needle_tail, adjusted_flags)) diff --git a/src/base/base_test.c b/src/base/base_test.c new file mode 100644 index 00000000..ae447aab --- /dev/null +++ b/src/base/base_test.c @@ -0,0 +1,2 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) diff --git a/src/base/base_test.h b/src/base/base_test.h new file mode 100644 index 00000000..54f49913 --- /dev/null +++ b/src/base/base_test.h @@ -0,0 +1,85 @@ +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_TEST_H +#define BASE_TEST_H + +typedef enum TestStatus +{ + TestStatus_Fail, + TestStatus_Crash, + TestStatus_Pass, + TestStatus_Skip, + TestStatus_COUNT +} +TestStatus; + +typedef struct TestResult TestResult; +struct TestResult +{ + TestStatus status; + char *fail_file; + int fail_line; + char *fail_cond; +}; + +#define TEST_FUNCTION_SIG(name) void name(Arena *arena, String8 user_data, TestResult *result_out, String8List *test_out) +#define TEST_FUNCTION_DEF(name) TEST_FUNCTION_SIG(test__##name) +typedef TEST_FUNCTION_SIG(TestFunctionType); + +typedef struct TestInfo TestInfo; +struct TestInfo +{ + String8 layer; + String8 label; + int decl_line; + B32 skip; + TestFunctionType *test_fn; +}; + +#if BUILD_TESTS +global U64 test_infos_count = 0; +global TestInfo test_infos[0xffff] = {0}; +#define AddTest(name, file_name, line, skip_, ...) \ +TEST_FUNCTION_DEF(name);\ +__VA_ARGS__ void add_test__##name(void)\ +{\ +String8 file = str8_lit(file_name);\ +U64 src_pos = str8_find_needle(file, 0, s("src/"), StringMatchFlag_SlashInsensitive);\ +String8 layer_folder = str8_skip(file, src_pos+4);\ +U64 layer_slash_pos = str8_find_needle(layer_folder, 0, s("/"), StringMatchFlag_SlashInsensitive);\ +String8 layer_name = str8_prefix(layer_folder, layer_slash_pos);\ +test_infos[test_infos_count].layer = layer_name;\ +test_infos[test_infos_count].label = str8_lit(#name);\ +test_infos[test_infos_count].decl_line = (line);\ +test_infos[test_infos_count].skip = (skip_);\ +test_infos[test_infos_count].test_fn = test__##name;\ +test_infos_count += 1;\ +} +# if COMPILER_MSVC +# pragma section(".CRT$XCU", read) +# define DeclareTest(name, skip) \ +AddTest(name, __FILE__, __LINE__, (skip))\ +__declspec(allocate(".CRT$XCU")) void (*add_test_ptr__##name)(void) = add_test__##name;\ +__pragma(comment(linker, "/include:" Stringify(add_test_ptr__##name))) +# elif COMPILER_GCC || COMPILER_CLANG +# define DeclareTest(name, skip) AddTest(name, __FILE__, __LINE__, (skip), __attribute__((constructor))) +# else +# error DeclareTest not defined for this compiler. +# endif +#else +# define DeclareTest(name, skip) +#endif + +#define Test(name) DeclareTest(name, 0)\ +TEST_FUNCTION_DEF(name) +#define SkippedTest(name) DeclareTest(name, 1)\ +TEST_FUNCTION_DEF(name) + +#define TestCheck(c) do { if (!(c)) {\ +*result_out = (TestResult){ .fail_file = __FILE__, .fail_line = __LINE__, .fail_cond = Stringify(c) };\ +if(debugger_is_attached()) { Trap(); }\ +return;\ +} } while(0) + +#endif // BASE_TEST_H diff --git a/src/torture/torture_base.c b/src/base/tests/base_tests.c similarity index 97% rename from src/torture/torture_base.c rename to src/base/tests/base_tests.c index 988378d8..2e8c5e6a 100644 --- a/src/torture/torture_base.c +++ b/src/base/tests/base_tests.c @@ -4,7 +4,7 @@ TEST(str8_list_substr) { String8List zero_list = {0}; - + { String8List list = {0}; str8_list_pushf(arena, &list, "a"); @@ -14,7 +14,7 @@ TEST(str8_list_substr) String8 result = str8_list_join(arena, &list, 0); T_Ok(str8_match(result, str8_lit("abc"), 0)); } - + { String8List list = {0}; str8_list_pushf(arena, &list, "a"); @@ -24,9 +24,9 @@ TEST(str8_list_substr) String8 result = str8_list_join(arena, &list, 0); T_Ok(str8_match(result, str8_lit("abc"), 0)); } - + { - + String8List list = {0}; str8_list_pushf(arena, &list, "a"); str8_list_pushf(arena, &list, "bcd"); @@ -34,9 +34,9 @@ TEST(str8_list_substr) String8 result = str8_list_join(arena, &sub, 0); T_Ok(str8_match(result, str8_lit("c"), 0)); } - + { - + String8List list = {0}; str8_list_pushf(arena, &list, "a"); str8_list_pushf(arena, &list, "bcd"); @@ -44,7 +44,7 @@ TEST(str8_list_substr) String8 result = str8_list_join(arena, &sub, 0); T_Ok(str8_match(result, str8_lit("b"), 0)); } - + { String8List list = {0}; str8_list_pushf(arena, &list, "ab"); @@ -54,14 +54,14 @@ TEST(str8_list_substr) String8 result = str8_list_join(arena, &sub, 0); T_Ok(str8_match(result, str8_lit("bcde"), 0)); } - + { String8List list = {0}; str8_list_pushf(arena, &list, "abc"); - + String8List zero = str8_list_substr(arena, list, r1u64(0, 0)); T_Ok(MemoryMatchStruct(&zero, &zero_list)); - + String8List out_of_bounds_range = str8_list_substr(arena, list, r1u64(max_U64/2, max_U64)); T_Ok(MemoryMatchStruct(&out_of_bounds_range, &zero_list)); } @@ -84,7 +84,7 @@ TEST(bit_array) T_Ok(r == expected_r); if (r) { T_Ok(idx == expected_idx); - + } } } @@ -112,11 +112,11 @@ TEST(match_wildcard) T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?"), 0) == 0); T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?*"), 0) == 0); T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit(""), 0) == 0); - + // exact T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("a"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), 0) == 0); - + // ? T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("?"), 0) == 0); @@ -126,7 +126,7 @@ TEST(match_wildcard) T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("??"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a?c"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("???"), 0) == 0); - + // * T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*"), 0) == 1); @@ -140,32 +140,32 @@ TEST(match_wildcard) T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a**c"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*b*c"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*a*d*"), 0) == 0); - + T_Ok(str8_match_wildcard(str8_lit("abcd"), str8_lit("a*d"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("abefcdgiescdfimde"), str8_lit("ab*cd?i*de"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("mississippi"), str8_lit("m*iss*ppi"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*b"), 0) == 0); T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("aa"), 0) == 0); T_Ok(str8_match_wildcard(str8_lit("aa"), str8_lit("a"), 0) == 0); - + // case insensitive T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("A"), StringMatchFlag_CaseInsensitive) == 1); T_Ok(str8_match_wildcard(str8_lit("FooBar"), str8_lit("foobar"), StringMatchFlag_CaseInsensitive) == 1); T_Ok(str8_match_wildcard(str8_lit("Foobar"), str8_lit("foo*"), StringMatchFlag_CaseInsensitive) == 1); - + // right side sloppy T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("ab"), StringMatchFlag_RightSideSloppy) == 1); T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit(""), StringMatchFlag_RightSideSloppy) == 1); T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("a"), StringMatchFlag_RightSideSloppy) == 0); - + // slash insensitive T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), 0) == 0); T_Ok(str8_match_wildcard(str8_lit("a/b"), str8_lit("a\\b"), StringMatchFlag_SlashInsensitive) == 1); T_Ok(str8_match_wildcard(str8_lit("a/b/c"), str8_lit("a\\*\\c"), StringMatchFlag_SlashInsensitive) == 1); - + // combined T_Ok(str8_match_wildcard(str8_lit("Ab\\Cde"), str8_lit("ab/*e"), StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive) == 1); - + T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*?*"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("*?*"), 0) == 1); T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*?*"), 0) == 0); @@ -178,17 +178,17 @@ TEST(hash_map) { { HashMap hm = {0}; - + U64 test_count = 256; for EachIndex(i, test_count) { hash_map_push_u64_u64(arena, &hm, i, i*2); } - + // test tombstone reuse for (U64 i = 10; i < test_count; ++i) { HashMapNode *test_node = hash_map_search(&hm, hash_map_hasher(str8_struct(&i)), (HashMapKey){ .key_u64 = i }, hash_map_match_u64); T_Ok(hash_map_purge_u64(&hm, i) == 1); T_Ok(hash_map_push_u64_u64(arena, &hm, i, 10) == test_node); } - + // delete some items and after search them for (U64 i = 0; i < 10; ++i) { T_Ok(hash_map_purge_u64(&hm, i)); @@ -197,37 +197,37 @@ TEST(hash_map) for (U64 i = 0; i < 10; ++i) { T_Ok(hash_map_search_u64_raw(&hm, i) == 0); } - + // extract pairs and test sorting HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm); sort_hash_map_key_value_u64(pairs, hm.count); for (U64 i = 1; i < hm.count; ++i) { T_Ok(pairs[i-1].key.key_u64 < pairs[i].key.key_u64); } - + // did purge put all the nodes on free list? hash_map_purge(&hm); U64 free_list_count = 0; for (HashMapNode *n = hm.free_list; n != 0; n = n->next) { free_list_count += 1; } T_Ok(free_list_count == test_count); } - + // test search through tombstones { HashMap hm = {0}; - + U64 hash = 12345; - + hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 1 }, .value = { .value_u64 = 10 } }, hash_map_match_u64); hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 2 }, .value = { .value_u64 = 20 } }, hash_map_match_u64); hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = 3 }, .value = { .value_u64 = 30 } }, hash_map_match_u64); - + T_Ok(hash_map_purge_u64(&hm, 1)); - + T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 2 }, hash_map_match_u64)->v.value.value_u64 == 20); T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = 3 }, hash_map_match_u64)->v.value.value_u64 == 30); } - + // interleaved purges { HashMap hm = {0}; @@ -235,107 +235,106 @@ TEST(hash_map) for EachIndex(i, 256) { hash_map_push_u64_u64(arena, &hm, i, round); } for (U64 i = 0; i < 256; i += 2) { hash_map_purge_u64(&hm, i); } for (U64 i = 0; i < 256; i += 2) { hash_map_push_u64_u64(arena, &hm, i, round + 1); } - + T_Ok(hm.count == 256); T_Ok(hm.tombstone_count == 0); } - + for EachIndex(i, 100) { T_Ok(hash_map_purge_u64(&hm, 123123123 + i) == 0); } } - + // empty hash map test { HashMap hm = {0}; - + T_Ok(hash_map_search_u64_raw(&hm, 123) == 0); T_Ok(hash_map_purge_u64(&hm, 123) == 0); - + HashMapKeyValue *pairs = key_value_from_hash_map(arena, &hm); T_Ok(pairs == 0 || hm.count == 0); - + hash_map_purge(&hm); T_Ok(hm.count == 0); T_Ok(hm.tombstone_count == 0); } - + // heavy collision { HashMap hm = {0}; - + U64 hash = 12345; U64 count = 1024; - + for EachIndex(i, count) { hash_map_push(arena, &hm, hash, (HashMapKeyValue){ .key = { .key_u64 = i }, .value = { .value_u64 = i*3 } }, hash_map_match_u64); } - + for EachIndex(i, count) { T_Ok(hash_map_search(&hm, hash, (HashMapKey){ .key_u64 = i }, hash_map_match_u64)->v.value.value_u64 == i*3); } } - + // duplicate key test { HashMap hm = {0}; - + U64 a = 1; U64 b = 2; - + HashMapNode *n0 = hash_map_push_u64_u64(arena, &hm, 1, a); HashMapNode *n1 = hash_map_push_u64_u64(arena, &hm, 1, b); - + T_Ok(hm.count == 1); T_Ok(n0 == n1); U64 test = *hash_map_search_u64_u64(&hm, 1); T_Ok(test != b); } - + // test paths { HashMap hm = {0}; - + U64 foo = 0; U64 bar = 1; - + hash_map_push_path_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo); hash_map_push_path_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo); hash_map_push_path_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo); - + hash_map_push_path_raw(arena, &hm, str8_lit("/mnt/devel"), &bar); hash_map_push_path_raw(arena, &hm, str8_lit("/MNT/devel"), &bar); - + T_Ok(hm.count == 2); - + T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:/devel/test")) == &foo); T_Ok(hash_map_search_path_raw(&hm, str8_lit("c:\\devel\\TEST")) == &foo); T_Ok(hash_map_search_path_raw(&hm, str8_lit("/mnt/devel")) == &bar); T_Ok(hash_map_search_path_raw(&hm, str8_lit("/MNT/DEVEL")) == &bar); } - + // test strings { HashMap hm = {0}; - + U64 foo = 0; U64 bar = 1; - + hash_map_push_string_raw(arena, &hm, str8_lit("c:/DEVEL/test"), &foo); hash_map_push_string_raw(arena, &hm, str8_lit("c:\\DEVEL\\test"), &foo); hash_map_push_string_raw(arena, &hm, str8_lit("c:/devel\\test"), &foo); - + hash_map_push_string_raw(arena, &hm, str8_lit("/mnt/devel"), &bar); hash_map_push_string_raw(arena, &hm, str8_lit("/MNT/devel"), &bar); - + T_Ok(hm.count == 5); - + T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/DEVEL/test")) == &foo); T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:\\DEVEL\\test")) == &foo); T_Ok(hash_map_search_string_raw(&hm, str8_lit("c:/devel\\test")) == &foo); - + T_Ok(hash_map_search_string_raw(&hm, str8_lit("/mnt/devel")) == &bar); T_Ok(hash_map_search_string_raw(&hm, str8_lit("/MNT/devel")) == &bar); } } - diff --git a/src/torture/torture_dwarf.c b/src/dwarf/tests/dwarf_tests.c similarity index 56% rename from src/torture/torture_dwarf.c rename to src/dwarf/tests/dwarf_tests.c index 0649731a..8681db21 100644 --- a/src/torture/torture_dwarf.c +++ b/src/dwarf/tests/dwarf_tests.c @@ -43,23 +43,23 @@ t_dw_test_sleb128(U64 v, U64 expected_length) return is_ok; } -TEST(test_leb128) +Test(test_leb128) { - T_Ok(t_dw_test_uleb128(0, 1)); - T_Ok(t_dw_test_sleb128(0, 1)); - T_Ok(t_dw_test_sleb128(-1, 1)); + TestCheck(t_dw_test_uleb128(0, 1)); + TestCheck(t_dw_test_sleb128(0, 1)); + TestCheck(t_dw_test_sleb128(-1, 1)); - T_Ok(t_dw_test_uleb128(max_U64, 10)); - T_Ok(t_dw_test_sleb128(min_S64, 10)); - T_Ok(t_dw_test_sleb128(max_S64, 10)); + TestCheck(t_dw_test_uleb128(max_U64, 10)); + TestCheck(t_dw_test_sleb128(min_S64, 10)); + TestCheck(t_dw_test_sleb128(max_S64, 10)); - T_Ok(t_dw_test_uleb128(0xDEADBEEFCAFEBABE, 10)); + TestCheck(t_dw_test_uleb128(0xDEADBEEFCAFEBABE, 10)); for EachIndex(i, 64) { - T_Ok(t_dw_test_uleb128((1ull << i), 1 + (i / 7))); + TestCheck(t_dw_test_uleb128((1ull << i), 1 + (i / 7))); } for EachIndex(i, 64) { - T_Ok(t_dw_test_sleb128((1ull << i), 1 + (i + 1) / 7)); + TestCheck(t_dw_test_sleb128((1ull << i), 1 + (i + 1) / 7)); } } @@ -132,7 +132,7 @@ dw_input_from_writer(Arena *arena, DW_Writer *writer) return input; } -TEST(dwarf_32bit) +Test(dwarf_32bit) { 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); @@ -148,19 +148,19 @@ TEST(dwarf_32bit) Rng1U64 range = unit_ranges.v[range_idx]; U32 first_four_bytes = 0; - T_Ok(str8_deserial_read_struct(input.sec[sec_idx].data, range.min, &first_four_bytes) == sizeof(first_four_bytes)); - T_Ok(first_four_bytes + 4 == dim_1u64(range)); + TestCheck(str8_deserial_read_struct(input.sec[sec_idx].data, range.min, &first_four_bytes) == sizeof(first_four_bytes)); + TestCheck(first_four_bytes + 4 == dim_1u64(range)); U32 unit_length = 0; - T_Ok(str8_deserial_read_struct(input.sec[sec_idx].data, range.min, &unit_length) == sizeof(U32)); - T_Ok(unit_length + 4 == dim_1u64(range)); + TestCheck(str8_deserial_read_struct(input.sec[sec_idx].data, range.min, &unit_length) == sizeof(U32)); + TestCheck(unit_length + 4 == dim_1u64(range)); } } dw_writer_end(&writer); } -TEST(dwarf_64bit) +Test(dwarf_64bit) { DW_Writer *writer = dw_writer_begin(DW_Format_64Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); dw_writer_tag_begin(writer, DW_TagKind_CompileUnit); @@ -176,19 +176,19 @@ TEST(dwarf_64bit) Rng1U64 range = unit_ranges.v[range_idx]; U32 first_four_bytes = 0; - T_Ok(str8_deserial_read_struct(input.sec[sec_idx].data, range.min, &first_four_bytes) == sizeof(first_four_bytes)); - T_Ok(first_four_bytes == max_U32); + TestCheck(str8_deserial_read_struct(input.sec[sec_idx].data, range.min, &first_four_bytes) == sizeof(first_four_bytes)); + TestCheck(first_four_bytes == max_U32); U64 unit_length = 0; - T_Ok(str8_deserial_read_struct(input.sec[sec_idx].data, range.min + sizeof(U32), &unit_length) == sizeof(U64)); - T_Ok(unit_length + 12 == dim_1u64(range)); + TestCheck(str8_deserial_read_struct(input.sec[sec_idx].data, range.min + sizeof(U32), &unit_length) == sizeof(U64)); + TestCheck(unit_length + 12 == dim_1u64(range)); } } dw_writer_end(&writer); } -TEST(dwarf_line_opcodes) +Test(dwarf_line_opcodes) { DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); String8 comp_dir = str8_lit("c:/devel/"); @@ -238,126 +238,126 @@ TEST(dwarf_line_opcodes) DW_CompUnit cu = dw_cu_from_info_off(arena, &input, (DW_ListUnitInput){0}, cu_ranges.v[0].min, 0); DW_LineVM *line_vm = dw_line_vm_init(&input, &cu); - T_Ok(line_vm->header.dir_table.count == 2); - T_Ok(line_vm->header.file_table.count == 2); + TestCheck(line_vm->header.dir_table.count == 2); + TestCheck(line_vm->header.file_table.count == 2); - T_Ok(str8_match(line_vm->header.dir_table.v[0], str8_lit("c:/devel"), 0)); - T_Ok(str8_match(line_vm->header.dir_table.v[1], str8_lit("d:/foobar"), 0)); + TestCheck(str8_match(line_vm->header.dir_table.v[0], str8_lit("c:/devel"), 0)); + TestCheck(str8_match(line_vm->header.dir_table.v[1], str8_lit("d:/foobar"), 0)); DW_LineFile *file_reader = &line_vm->header.file_table.v[0]; - T_Ok(str8_match(file_reader->path, comp_name, 0)); - T_Ok(file_reader->dir_idx == 0); - T_Ok(file_reader->time_stamp == file->time_stamp); - T_Ok(u128_match(file_reader->md5, file->md5)); - T_Ok(str8_match(file_reader->source, file->source, 0)); + TestCheck(str8_match(file_reader->path, comp_name, 0)); + TestCheck(file_reader->dir_idx == 0); + TestCheck(file_reader->time_stamp == file->time_stamp); + TestCheck(u128_match(file_reader->md5, file->md5)); + TestCheck(str8_match(file_reader->source, file->source, 0)); DW_LineFile *file2_reader = &line_vm->header.file_table.v[1]; - T_Ok(str8_match(file2_reader->path, str8_lit("qwe.c"), 0)); - T_Ok(file2_reader->dir_idx == 1); - T_Ok(file2_reader->time_stamp == file2->time_stamp); - T_Ok(u128_match(file2_reader->md5, file2->md5)); - T_Ok(str8_match(file2_reader->source, file2->source, 0)); + TestCheck(str8_match(file2_reader->path, str8_lit("qwe.c"), 0)); + TestCheck(file2_reader->dir_idx == 1); + TestCheck(file2_reader->time_stamp == file2->time_stamp); + TestCheck(u128_match(file2_reader->md5, file2->md5)); + TestCheck(str8_match(file2_reader->source, file2->source, 0)); - T_Ok(line_vm->new_line == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_Copy); - T_Ok(line_vm->new_line == 1); - T_Ok(line_vm->state.discriminator == 0); - T_Ok(line_vm->state.basic_block == 0); - T_Ok(line_vm->state.prologue_end == 0); - T_Ok(line_vm->state.epilogue_begin == 0); + TestCheck(line_vm->new_line == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_Copy); + TestCheck(line_vm->new_line == 1); + TestCheck(line_vm->state.discriminator == 0); + TestCheck(line_vm->state.basic_block == 0); + TestCheck(line_vm->state.prologue_end == 0); + TestCheck(line_vm->state.epilogue_begin == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvancePc); - T_Ok(line_vm->state.address == 7); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvancePc); + TestCheck(line_vm->state.address == 7); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvancePc); - T_Ok(line_vm->state.address == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvancePc); + TestCheck(line_vm->state.address == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 5); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 5); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 1); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 1); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_SetFile); - T_Ok(line_vm->state.file_index == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_SetFile); + TestCheck(line_vm->state.file_index == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_SetColumn); - T_Ok(line_vm->state.column == max_U64); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_SetColumn); + TestCheck(line_vm->state.column == max_U64); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_SetColumn); - T_Ok(line_vm->state.column == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_SetColumn); + TestCheck(line_vm->state.column == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_NegateStmt); - T_Ok(line_vm->state.is_stmt == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_NegateStmt); + TestCheck(line_vm->state.is_stmt == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_NegateStmt); - T_Ok(line_vm->state.is_stmt == 1); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_NegateStmt); + TestCheck(line_vm->state.is_stmt == 1); - T_Ok(line_vm->state.basic_block == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_SetBasicBlock); - T_Ok(line_vm->state.basic_block == 1); + TestCheck(line_vm->state.basic_block == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_SetBasicBlock); + TestCheck(line_vm->state.basic_block == 1); { U64 addr_before = line_vm->state.address; U64 const_advance = (0xffu - line_vm->header.opcode_base) / line_vm->header.line_range; - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(!line_vm->new_line); - T_Ok(line_vm->state.address == addr_before + const_advance); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(!line_vm->new_line); + TestCheck(line_vm->state.address == addr_before + const_advance); } - T_Ok(line_vm->state.address == 0x11); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_FixedAdvancePc); - T_Ok(line_vm->state.address == max_U16 + 0x11); + TestCheck(line_vm->state.address == 0x11); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_FixedAdvancePc); + TestCheck(line_vm->state.address == max_U16 + 0x11); - T_Ok(line_vm->state.prologue_end == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_SetPrologueEnd); - T_Ok(line_vm->state.prologue_end == 1); + TestCheck(line_vm->state.prologue_end == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_SetPrologueEnd); + TestCheck(line_vm->state.prologue_end == 1); - T_Ok(line_vm->state.epilogue_begin == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_SetEpilogueBegin); - T_Ok(line_vm->state.epilogue_begin == 1); + TestCheck(line_vm->state.epilogue_begin == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_SetEpilogueBegin); + TestCheck(line_vm->state.epilogue_begin == 1); - T_Ok(line_vm->state.isa == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_SetIsa); - T_Ok(line_vm->state.isa == max_U64); + TestCheck(line_vm->state.isa == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_SetIsa); + TestCheck(line_vm->state.isa == max_U64); - T_Ok(line_vm->state.address != address); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); - T_Ok(line_vm->ext_opcode == DW_ExtOpcode_SetAddress); - T_Ok(line_vm->state.address == address); + TestCheck(line_vm->state.address != address); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); + TestCheck(line_vm->ext_opcode == DW_ExtOpcode_SetAddress); + TestCheck(line_vm->state.address == address); - T_Ok(line_vm->state.discriminator == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); - T_Ok(line_vm->ext_opcode == DW_ExtOpcode_SetDiscriminator); - T_Ok(line_vm->state.discriminator == 2); + TestCheck(line_vm->state.discriminator == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); + TestCheck(line_vm->ext_opcode == DW_ExtOpcode_SetDiscriminator); + TestCheck(line_vm->state.discriminator == 2); - T_Ok(!line_vm->state.end_sequence); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); - T_Ok(line_vm->ext_opcode == DW_ExtOpcode_EndSequence); - T_Ok(line_vm->state.end_sequence); + TestCheck(!line_vm->state.end_sequence); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); + TestCheck(line_vm->ext_opcode == DW_ExtOpcode_EndSequence); + TestCheck(line_vm->state.end_sequence); dw_writer_end(&writer); } -TEST(dwarf_line_emit) +Test(dwarf_line_emit) { DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); String8 comp_dir = str8_lit("c:/devel/"); @@ -416,25 +416,25 @@ TEST(dwarf_line_emit) // check init sequence { - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_SetFile); - T_Ok(line_vm->state.file_index == 0); - T_Ok(line_vm->new_line == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_SetFile); + TestCheck(line_vm->state.file_index == 0); + TestCheck(line_vm->new_line == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvancePc); - T_Ok(line_vm->state.address == 1000); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvancePc); + TestCheck(line_vm->state.address == 1000); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->new_line == 0); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->new_line == 0); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_Copy); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->state.address == 1000); - T_Ok(line_vm->new_line == 1); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_Copy); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->state.address == 1000); + TestCheck(line_vm->new_line == 1); } { @@ -442,122 +442,122 @@ TEST(dwarf_line_emit) for EachIndex(i, abs_s64(writer->line.line_base) + 1) { pc += 1; - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == 0x20 - i); - T_Ok(line_vm->state.line == 10000 - i); - T_Ok(line_vm->state.address == pc); - T_Ok(line_vm->new_line == 1); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == 0x20 - i); + TestCheck(line_vm->state.line == 10000 - i); + TestCheck(line_vm->state.address == pc); + TestCheck(line_vm->new_line == 1); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->state.address == pc); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->state.address == pc); } // line window underflow check { pc += 1; - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvancePc); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->state.address == pc); - T_Ok(!line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvancePc); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->state.address == pc); + TestCheck(!line_vm->new_line); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 9994); - T_Ok(line_vm->state.address == pc); - T_Ok(!line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 9994); + TestCheck(line_vm->state.address == pc); + TestCheck(!line_vm->new_line); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_Copy); - T_Ok(line_vm->state.line == 9994); - T_Ok(line_vm->state.address == pc); - T_Ok(line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_Copy); + TestCheck(line_vm->state.line == 9994); + TestCheck(line_vm->state.address == pc); + TestCheck(line_vm->new_line); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->state.address == pc); - T_Ok(!line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->state.address == pc); + TestCheck(!line_vm->new_line); } for EachIndex(i, writer->line.line_range + writer->line.line_base) { pc += 1; - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == 0x20 + i); - T_Ok(line_vm->state.line == 10000 + i); - T_Ok(line_vm->state.address == pc); - T_Ok(line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == 0x20 + i); + TestCheck(line_vm->state.line == 10000 + i); + TestCheck(line_vm->state.address == pc); + TestCheck(line_vm->new_line); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->state.address == pc); - T_Ok(!line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->state.address == pc); + TestCheck(!line_vm->new_line); } // line window overflow check { pc += 1; - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvancePc); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->state.address == pc); - T_Ok(!line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvancePc); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->state.address == pc); + TestCheck(!line_vm->new_line); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 10009); - T_Ok(line_vm->state.address == pc); - T_Ok(!line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 10009); + TestCheck(line_vm->state.address == pc); + TestCheck(!line_vm->new_line); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_Copy); - T_Ok(line_vm->state.address == pc); - T_Ok(line_vm->state.line == 10009); - T_Ok(line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_Copy); + TestCheck(line_vm->state.address == pc); + TestCheck(line_vm->state.line == 10009); + TestCheck(line_vm->new_line); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_AdvanceLine); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->state.address == pc); - T_Ok(!line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_AdvanceLine); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->state.address == pc); + TestCheck(!line_vm->new_line); } - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); - T_Ok(line_vm->ext_opcode == DW_ExtOpcode_EndSequence); - T_Ok(line_vm->state.line == 10000); - T_Ok(line_vm->state.address == pc); - T_Ok(line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); + TestCheck(line_vm->ext_opcode == DW_ExtOpcode_EndSequence); + TestCheck(line_vm->state.line == 10000); + TestCheck(line_vm->state.address == pc); + TestCheck(line_vm->new_line); } { - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(dw_line_vm_step(line_vm)); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(dw_line_vm_step(line_vm)); for EachIndex(i, line_addr_count/*first line is a noop*/-1) { - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->header.opcode_base < line_vm->opcode); - T_Ok(line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->header.opcode_base < line_vm->opcode); + TestCheck(line_vm->new_line); } - T_Ok(dw_line_vm_step(line_vm)); - T_Ok(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); - T_Ok(line_vm->ext_opcode == DW_ExtOpcode_EndSequence); - T_Ok(line_vm->state.line == 1586); - T_Ok(line_vm->state.address == 1161); - T_Ok(line_vm->new_line); + TestCheck(dw_line_vm_step(line_vm)); + TestCheck(line_vm->opcode == DW_StdOpcode_ExtendedOpcode); + TestCheck(line_vm->ext_opcode == DW_ExtOpcode_EndSequence); + TestCheck(line_vm->state.line == 1586); + TestCheck(line_vm->state.address == 1161); + TestCheck(line_vm->new_line); } } -TEST(dwarf_writer) +Test(dwarf_writer) { DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64); // info @@ -627,237 +627,237 @@ TEST(dwarf_writer) // validate the writer - T_Ok(writer->current == 0); - T_Ok(writer->format == DW_Format_32Bit); - T_Ok(writer->cu_kind == DW_CompUnitKind_Compile); - T_Ok(writer->version == DW_Version_5); - T_Ok(writer->address_size == 8); - T_Ok(writer->abbrev_base_info_off == 8); - T_Ok(writer->fixups.count == 0); - T_Ok(writer->abbrev_id_map->count == 7); + TestCheck(writer->current == 0); + TestCheck(writer->format == DW_Format_32Bit); + TestCheck(writer->cu_kind == DW_CompUnitKind_Compile); + TestCheck(writer->version == DW_Version_5); + TestCheck(writer->address_size == 8); + TestCheck(writer->abbrev_base_info_off == 8); + TestCheck(writer->fixups.count == 0); + TestCheck(writer->abbrev_id_map->count == 7); DW_WriterTag *comp_unit_tag = writer->root; { - T_Ok(comp_unit_tag->kind == DW_TagKind_CompileUnit); - T_Ok(comp_unit_tag->next == 0); - T_Ok(comp_unit_tag->parent == 0); - T_Ok(comp_unit_tag->attrib_count == 5); - T_Ok(comp_unit_tag->abbrev_id == 1); - T_Ok(comp_unit_tag->info_off == 0xc); + TestCheck(comp_unit_tag->kind == DW_TagKind_CompileUnit); + TestCheck(comp_unit_tag->next == 0); + TestCheck(comp_unit_tag->parent == 0); + TestCheck(comp_unit_tag->attrib_count == 5); + TestCheck(comp_unit_tag->abbrev_id == 1); + TestCheck(comp_unit_tag->info_off == 0xc); DW_WriterAttrib *producer_attrib = comp_unit_tag->first_attrib; - T_Ok(producer_attrib->kind == DW_AttribKind_Producer); - T_Ok(producer_attrib->form.reader.kind == DW_Form_String); - T_Ok(str8_match(producer_attrib->form.writer.string, str8_lit("RAD DWARF WRITER"), 0)); + TestCheck(producer_attrib->kind == DW_AttribKind_Producer); + TestCheck(producer_attrib->form.reader.kind == DW_Form_String); + TestCheck(str8_match(producer_attrib->form.writer.string, str8_lit("RAD DWARF WRITER"), 0)); DW_WriterAttrib *language_attrib = producer_attrib->next; - T_Ok(language_attrib->kind == DW_AttribKind_Language); - T_Ok(language_attrib->form.reader.kind == DW_Form_Data1); - T_Ok(language_attrib->form.reader.data.size == 1); - T_Ok(*(U8 *)language_attrib->form.reader.data.str == DW_Language_C99); + TestCheck(language_attrib->kind == DW_AttribKind_Language); + TestCheck(language_attrib->form.reader.kind == DW_Form_Data1); + TestCheck(language_attrib->form.reader.data.size == 1); + TestCheck(*(U8 *)language_attrib->form.reader.data.str == DW_Language_C99); DW_WriterAttrib *use_utf8_attrib = language_attrib->next; - T_Ok(use_utf8_attrib->kind == DW_AttribKind_UseUtf8); - T_Ok(use_utf8_attrib->form.reader.kind == DW_Form_Flag); - T_Ok(use_utf8_attrib->form.reader.flag == 1); + TestCheck(use_utf8_attrib->kind == DW_AttribKind_UseUtf8); + TestCheck(use_utf8_attrib->form.reader.kind == DW_Form_Flag); + TestCheck(use_utf8_attrib->form.reader.flag == 1); } DW_WriterTag *char_type_tag = comp_unit_tag->first_child; { - T_Ok(char_type_tag->kind == DW_TagKind_BaseType); - T_Ok(char_type_tag->next != 0); - T_Ok(char_type_tag->parent == comp_unit_tag); - T_Ok(char_type_tag->attrib_count == 3); - T_Ok(char_type_tag->abbrev_id == 2); - T_Ok(char_type_tag->info_off == 0x30); - T_Ok(char_type_tag->first_attrib != char_type_tag->last_attrib); + TestCheck(char_type_tag->kind == DW_TagKind_BaseType); + TestCheck(char_type_tag->next != 0); + TestCheck(char_type_tag->parent == comp_unit_tag); + TestCheck(char_type_tag->attrib_count == 3); + TestCheck(char_type_tag->abbrev_id == 2); + TestCheck(char_type_tag->info_off == 0x30); + TestCheck(char_type_tag->first_attrib != char_type_tag->last_attrib); DW_WriterAttrib *byte_size_attrib = char_type_tag->first_attrib; - T_Ok(byte_size_attrib->kind == DW_AttribKind_ByteSize); - T_Ok(byte_size_attrib->form.reader.kind == DW_Form_Data1); - T_Ok(byte_size_attrib->form.reader.data.size == 1); - T_Ok(*(U8 *)byte_size_attrib->form.reader.data.str == 1); + TestCheck(byte_size_attrib->kind == DW_AttribKind_ByteSize); + TestCheck(byte_size_attrib->form.reader.kind == DW_Form_Data1); + TestCheck(byte_size_attrib->form.reader.data.size == 1); + TestCheck(*(U8 *)byte_size_attrib->form.reader.data.str == 1); DW_WriterAttrib *encoding_attrib = byte_size_attrib->next; - T_Ok(encoding_attrib->kind == DW_AttribKind_Encoding); - T_Ok(encoding_attrib->form.reader.kind == DW_Form_Data1); - T_Ok(encoding_attrib->form.reader.data.size == 1); - T_Ok(*(U8 *)encoding_attrib->form.reader.data.str == DW_ATE_SignedChar); + TestCheck(encoding_attrib->kind == DW_AttribKind_Encoding); + TestCheck(encoding_attrib->form.reader.kind == DW_Form_Data1); + TestCheck(encoding_attrib->form.reader.data.size == 1); + TestCheck(*(U8 *)encoding_attrib->form.reader.data.str == DW_ATE_SignedChar); DW_WriterAttrib *name_attrib = encoding_attrib->next; - T_Ok(name_attrib->kind == DW_AttribKind_Name); - T_Ok(name_attrib->form.reader.kind == DW_Form_String); - T_Ok(str8_match(name_attrib->form.reader.string, str8_lit("char"), 0)); + TestCheck(name_attrib->kind == DW_AttribKind_Name); + TestCheck(name_attrib->form.reader.kind == DW_Form_String); + TestCheck(str8_match(name_attrib->form.reader.string, str8_lit("char"), 0)); } DW_WriterTag *const_type_tag = char_type_tag->next; { - T_Ok(const_type_tag->kind == DW_TagKind_ConstType); - T_Ok(const_type_tag->next != 0); - T_Ok(const_type_tag->parent == comp_unit_tag); - T_Ok(const_type_tag->attrib_count == 1); - T_Ok(const_type_tag->abbrev_id == 3); - T_Ok(const_type_tag->info_off == 0x38); - T_Ok(const_type_tag->first_attrib && const_type_tag->first_attrib == const_type_tag->last_attrib); + TestCheck(const_type_tag->kind == DW_TagKind_ConstType); + TestCheck(const_type_tag->next != 0); + TestCheck(const_type_tag->parent == comp_unit_tag); + TestCheck(const_type_tag->attrib_count == 1); + TestCheck(const_type_tag->abbrev_id == 3); + TestCheck(const_type_tag->info_off == 0x38); + TestCheck(const_type_tag->first_attrib && const_type_tag->first_attrib == const_type_tag->last_attrib); DW_WriterAttrib *type_attrib = const_type_tag->first_attrib; - T_Ok(type_attrib->kind == DW_AttribKind_Type); - T_Ok(type_attrib->form.writer.kind == DW_WriterFormKind_Ref); - T_Ok(type_attrib->form.writer.ref == char_type_tag); + TestCheck(type_attrib->kind == DW_AttribKind_Type); + TestCheck(type_attrib->form.writer.kind == DW_WriterFormKind_Ref); + TestCheck(type_attrib->form.writer.ref == char_type_tag); } DW_WriterTag *dup_char_type_tag = const_type_tag->next; { - T_Ok(dup_char_type_tag->kind == DW_TagKind_BaseType); - T_Ok(dup_char_type_tag->next != 0); - T_Ok(dup_char_type_tag->parent == comp_unit_tag); - T_Ok(dup_char_type_tag->attrib_count == 3); - T_Ok(dup_char_type_tag->abbrev_id == 2); - T_Ok(dup_char_type_tag->info_off == 0x3a); - T_Ok(dup_char_type_tag->first_attrib != dup_char_type_tag->last_attrib); + TestCheck(dup_char_type_tag->kind == DW_TagKind_BaseType); + TestCheck(dup_char_type_tag->next != 0); + TestCheck(dup_char_type_tag->parent == comp_unit_tag); + TestCheck(dup_char_type_tag->attrib_count == 3); + TestCheck(dup_char_type_tag->abbrev_id == 2); + TestCheck(dup_char_type_tag->info_off == 0x3a); + TestCheck(dup_char_type_tag->first_attrib != dup_char_type_tag->last_attrib); DW_WriterAttrib *byte_size_attrib = dup_char_type_tag->first_attrib; - T_Ok(byte_size_attrib->kind == DW_AttribKind_ByteSize); - T_Ok(byte_size_attrib->form.reader.kind == DW_Form_Data1); - T_Ok(byte_size_attrib->form.reader.data.size == 1); - T_Ok(*(U8 *)byte_size_attrib->form.reader.data.str == 1); + TestCheck(byte_size_attrib->kind == DW_AttribKind_ByteSize); + TestCheck(byte_size_attrib->form.reader.kind == DW_Form_Data1); + TestCheck(byte_size_attrib->form.reader.data.size == 1); + TestCheck(*(U8 *)byte_size_attrib->form.reader.data.str == 1); DW_WriterAttrib *encoding_attrib = byte_size_attrib->next; - T_Ok(encoding_attrib->kind == DW_AttribKind_Encoding); - T_Ok(encoding_attrib->form.reader.kind == DW_Form_Data1); - T_Ok(encoding_attrib->form.reader.data.size == 1); - T_Ok(*(U8 *)encoding_attrib->form.reader.data.str == DW_ATE_SignedChar); + TestCheck(encoding_attrib->kind == DW_AttribKind_Encoding); + TestCheck(encoding_attrib->form.reader.kind == DW_Form_Data1); + TestCheck(encoding_attrib->form.reader.data.size == 1); + TestCheck(*(U8 *)encoding_attrib->form.reader.data.str == DW_ATE_SignedChar); DW_WriterAttrib *name_attrib = encoding_attrib->next; - T_Ok(name_attrib->kind == DW_AttribKind_Name); - T_Ok(name_attrib->form.reader.kind == DW_Form_String); - T_Ok(str8_match(name_attrib->form.reader.string, str8_lit("char"), 0)); + TestCheck(name_attrib->kind == DW_AttribKind_Name); + TestCheck(name_attrib->form.reader.kind == DW_Form_String); + TestCheck(str8_match(name_attrib->form.reader.string, str8_lit("char"), 0)); } DW_WriterTag *simple_struct_tag = dup_char_type_tag->next; { - T_Ok(simple_struct_tag->kind == DW_TagKind_StructureType); - T_Ok(simple_struct_tag->next != 0); - T_Ok(simple_struct_tag->parent == comp_unit_tag); - T_Ok(simple_struct_tag->attrib_count == 2); - T_Ok(simple_struct_tag->abbrev_id == 4); - T_Ok(simple_struct_tag->info_off == 0x42); + TestCheck(simple_struct_tag->kind == DW_TagKind_StructureType); + TestCheck(simple_struct_tag->next != 0); + TestCheck(simple_struct_tag->parent == comp_unit_tag); + TestCheck(simple_struct_tag->attrib_count == 2); + TestCheck(simple_struct_tag->abbrev_id == 4); + TestCheck(simple_struct_tag->info_off == 0x42); DW_WriterAttrib *simple_struct_name = simple_struct_tag->first_attrib; - T_Ok(simple_struct_name->kind == DW_AttribKind_Name); - T_Ok(simple_struct_name->form.reader.kind == DW_Form_String); - T_Ok(str8_match(simple_struct_name->form.reader.string, str8_lit("FooBar"), 0)); + TestCheck(simple_struct_name->kind == DW_AttribKind_Name); + TestCheck(simple_struct_name->form.reader.kind == DW_Form_String); + TestCheck(str8_match(simple_struct_name->form.reader.string, str8_lit("FooBar"), 0)); DW_WriterTag *m0_tag = simple_struct_tag->first_child; { - T_Ok(m0_tag->kind == DW_TagKind_Member); - T_Ok(m0_tag->parent == simple_struct_tag); - T_Ok(m0_tag->attrib_count == 3); - T_Ok(m0_tag->info_off == 0x4b); - T_Ok(m0_tag->abbrev_id == 5); + TestCheck(m0_tag->kind == DW_TagKind_Member); + TestCheck(m0_tag->parent == simple_struct_tag); + TestCheck(m0_tag->attrib_count == 3); + TestCheck(m0_tag->info_off == 0x4b); + TestCheck(m0_tag->abbrev_id == 5); DW_WriterAttrib *name = m0_tag->first_attrib; - T_Ok(name->kind == DW_AttribKind_Name); - T_Ok(name->form.reader.kind == DW_Form_String); - T_Ok(str8_match(name->form.reader.string, str8_lit("m0"), 0)); + TestCheck(name->kind == DW_AttribKind_Name); + TestCheck(name->form.reader.kind == DW_Form_String); + TestCheck(str8_match(name->form.reader.string, str8_lit("m0"), 0)); DW_WriterAttrib *type = name->next; - T_Ok(type->kind == DW_AttribKind_Type); - T_Ok(type->form.reader.kind == DW_Form_Ref1); - T_Ok(type->form.reader.ref == 0x30); + TestCheck(type->kind == DW_AttribKind_Type); + TestCheck(type->form.reader.kind == DW_Form_Ref1); + TestCheck(type->form.reader.ref == 0x30); DW_WriterAttrib *data_loc = type->next; - T_Ok(data_loc->kind == DW_AttribKind_DataMemberLocation); - T_Ok(data_loc->form.reader.kind == DW_Form_Data1); - T_Ok(data_loc->form.reader.data.size == 1); - T_Ok(*(U8 *)data_loc->form.reader.data.str == 0); + TestCheck(data_loc->kind == DW_AttribKind_DataMemberLocation); + TestCheck(data_loc->form.reader.kind == DW_Form_Data1); + TestCheck(data_loc->form.reader.data.size == 1); + TestCheck(*(U8 *)data_loc->form.reader.data.str == 0); } DW_WriterTag *m1_tag = m0_tag->next; { - T_Ok(m1_tag->kind == DW_TagKind_Member); - T_Ok(m1_tag->parent == simple_struct_tag); - T_Ok(m1_tag->attrib_count == 4); - T_Ok(m1_tag->info_off == 0x51); - T_Ok(m1_tag->abbrev_id == 6); + TestCheck(m1_tag->kind == DW_TagKind_Member); + TestCheck(m1_tag->parent == simple_struct_tag); + TestCheck(m1_tag->attrib_count == 4); + TestCheck(m1_tag->info_off == 0x51); + TestCheck(m1_tag->abbrev_id == 6); DW_WriterAttrib *name = m1_tag->first_attrib; - T_Ok(name->kind == DW_AttribKind_Name); - T_Ok(name->form.reader.kind == DW_Form_String); - T_Ok(str8_match(name->form.reader.string, str8_lit("m1"), 0)); + TestCheck(name->kind == DW_AttribKind_Name); + TestCheck(name->form.reader.kind == DW_Form_String); + TestCheck(str8_match(name->form.reader.string, str8_lit("m1"), 0)); DW_WriterAttrib *type = name->next; - T_Ok(type->kind == DW_AttribKind_Type); - T_Ok(type->form.reader.kind == DW_Form_Ref1); - T_Ok(type->form.reader.ref == 0x30); + TestCheck(type->kind == DW_AttribKind_Type); + TestCheck(type->form.reader.kind == DW_Form_Ref1); + TestCheck(type->form.reader.ref == 0x30); DW_WriterAttrib *data_loc = type->next; - T_Ok(data_loc->kind == DW_AttribKind_DataMemberLocation); - T_Ok(data_loc->form.reader.kind == DW_Form_Data1); - T_Ok(data_loc->form.reader.data.size == 1); - T_Ok(*(U8 *)data_loc->form.reader.data.str == 10); + TestCheck(data_loc->kind == DW_AttribKind_DataMemberLocation); + TestCheck(data_loc->form.reader.kind == DW_Form_Data1); + TestCheck(data_loc->form.reader.data.size == 1); + TestCheck(*(U8 *)data_loc->form.reader.data.str == 10); DW_WriterAttrib *byte_size = data_loc->next; - T_Ok(byte_size->kind == DW_AttribKind_ByteSize); - T_Ok(byte_size->form.reader.kind == DW_Form_ImplicitConst); - T_Ok(byte_size->form.reader.implicit_const == 123); + TestCheck(byte_size->kind == DW_AttribKind_ByteSize); + TestCheck(byte_size->form.reader.kind == DW_Form_ImplicitConst); + TestCheck(byte_size->form.reader.implicit_const == 123); } } DW_WriterTag *main_tag = simple_struct_tag->next; - T_Ok(main_tag->next == 0); - T_Ok(main_tag->kind == DW_TagKind_SubProgram); - T_Ok(main_tag->parent == main_tag->parent); - T_Ok(main_tag->abbrev_id == 7); + TestCheck(main_tag->next == 0); + TestCheck(main_tag->kind == DW_TagKind_SubProgram); + TestCheck(main_tag->parent == main_tag->parent); + TestCheck(main_tag->abbrev_id == 7); { DW_WriterAttrib *external = main_tag->first_attrib; - T_Ok(external->kind == DW_AttribKind_External); - T_Ok(external->form.reader.kind == DW_Form_Flag); - T_Ok(external->form.reader.flag); + TestCheck(external->kind == DW_AttribKind_External); + TestCheck(external->form.reader.kind == DW_Form_Flag); + TestCheck(external->form.reader.flag); DW_WriterAttrib *prototyped = external->next; - T_Ok(prototyped->kind == DW_AttribKind_Prototyped); - T_Ok(prototyped->form.reader.kind == DW_Form_Flag); - T_Ok(prototyped->form.reader.flag); + TestCheck(prototyped->kind == DW_AttribKind_Prototyped); + TestCheck(prototyped->form.reader.kind == DW_Form_Flag); + TestCheck(prototyped->form.reader.flag); DW_WriterAttrib *low_pc = prototyped->next; - T_Ok(low_pc->kind == DW_AttribKind_LowPc); - T_Ok(low_pc->form.reader.kind == DW_Form_Addr); - T_Ok(low_pc->form.reader.addr.size == sizeof(U64)); - T_Ok(*(U64 *)low_pc->form.reader.addr.str == 0x140001000); + TestCheck(low_pc->kind == DW_AttribKind_LowPc); + TestCheck(low_pc->form.reader.kind == DW_Form_Addr); + TestCheck(low_pc->form.reader.addr.size == sizeof(U64)); + TestCheck(*(U64 *)low_pc->form.reader.addr.str == 0x140001000); DW_WriterAttrib *high_pc = low_pc->next; - T_Ok(high_pc->kind == DW_AttribKind_HighPc); - T_Ok(high_pc->form.reader.kind == DW_Form_Addr); - T_Ok(high_pc->form.reader.addr.size == sizeof(U64)); - T_Ok(*(U64 *)high_pc->form.reader.addr.str == 0x140001004); + TestCheck(high_pc->kind == DW_AttribKind_HighPc); + TestCheck(high_pc->form.reader.kind == DW_Form_Addr); + TestCheck(high_pc->form.reader.addr.size == sizeof(U64)); + TestCheck(*(U64 *)high_pc->form.reader.addr.str == 0x140001004); DW_WriterAttrib *name = high_pc->next; - T_Ok(name->kind == DW_AttribKind_Name); - T_Ok(name->form.reader.kind == DW_Form_String); - T_Ok(str8_match(name->form.reader.string, str8_lit("main"), 0)); + TestCheck(name->kind == DW_AttribKind_Name); + TestCheck(name->form.reader.kind == DW_Form_String); + TestCheck(str8_match(name->form.reader.string, str8_lit("main"), 0)); DW_WriterAttrib *type = name->next; - T_Ok(type->kind == DW_AttribKind_Type); - T_Ok(type->form.reader.kind == DW_Form_Ref1); - T_Ok(type->form.reader.ref == simple_struct_tag->info_off); + TestCheck(type->kind == DW_AttribKind_Type); + TestCheck(type->form.reader.kind == DW_Form_Ref1); + TestCheck(type->form.reader.ref == simple_struct_tag->info_off); DW_WriterAttrib *frame_base = type->next; - T_Ok(frame_base->kind == DW_AttribKind_FrameBase); - T_Ok(frame_base->form.reader.kind == DW_Form_ExprLoc); + TestCheck(frame_base->kind == DW_AttribKind_FrameBase); + TestCheck(frame_base->form.reader.kind == DW_Form_ExprLoc); DW_Expr frame_base_expr = dw_expr_from_data(arena, writer->format, writer->address_size, frame_base->form.reader.exprloc); - T_Ok(frame_base_expr.count == 1); - T_Ok(frame_base_expr.first->opcode == DW_ExprOp_Reg7); - T_Ok(frame_base_expr.first->operands == 0); - T_Ok(frame_base_expr.first->size == 1); - T_Ok(frame_base_expr.first->next == 0); - T_Ok(frame_base_expr.first->prev == 0); + TestCheck(frame_base_expr.count == 1); + TestCheck(frame_base_expr.first->opcode == DW_ExprOp_Reg7); + TestCheck(frame_base_expr.first->operands == 0); + TestCheck(frame_base_expr.first->size == 1); + TestCheck(frame_base_expr.first->next == 0); + TestCheck(frame_base_expr.first->prev == 0); } dw_writer_end(&writer); } -TEST(value_in_register) +Test(value_in_register) { // setup register context X64_RegBlock regs = {0}; @@ -877,12 +877,12 @@ TEST(value_in_register) DW_ExprEvalResult expr_eval = dw_eval_expr(arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, ®s, 0, 0, &expr_value); // validate eval result - T_Ok(expr_eval == DW_ExprEvalResult_Ok); - T_Ok(expr_value.type == DW_ExprValueType_U64); - T_Ok(expr_value.u64 == value); + TestCheck(expr_eval == DW_ExprEvalResult_Ok); + TestCheck(expr_value.type == DW_ExprValueType_U64); + TestCheck(expr_value.u64 == value); } -TEST(value_in_x_register) +Test(value_in_x_register) { // setup register context X64_RegBlock regs = {0}; @@ -902,12 +902,12 @@ TEST(value_in_x_register) DW_ExprEvalResult expr_eval = dw_eval_expr(arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, ®s, 0, 0, &expr_value); // validate eval result - T_Ok(expr_eval == DW_ExprEvalResult_Ok); - T_Ok(expr_value.type == DW_ExprValueType_U64); - T_Ok(expr_value.u64 == value); + TestCheck(expr_eval == DW_ExprEvalResult_Ok); + TestCheck(expr_value.type == DW_ExprValueType_U64); + TestCheck(expr_value.u64 == value); } -TEST(address_of_value) +Test(address_of_value) { // compile a simple program which reads address U64 addr = 0xdeadbeef; @@ -920,12 +920,12 @@ TEST(address_of_value) DW_ExprEvalResult expr_eval = dw_eval_expr(arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, 0, 0, 0, &expr_value); // validate eval result - T_Ok(expr_eval == DW_ExprEvalResult_Ok); - T_Ok(expr_value.type == DW_ExprValueType_Addr); - T_Ok(expr_value.addr == addr); + TestCheck(expr_eval == DW_ExprEvalResult_Ok); + TestCheck(expr_value.type == DW_ExprValueType_Addr); + TestCheck(expr_value.addr == addr); } -TEST(register_relative_variable) +Test(register_relative_variable) { ARCH_Info *arch_info = arch_info_from_arch(Arch_x64); @@ -945,12 +945,12 @@ TEST(register_relative_variable) DW_ExprEvalResult expr_eval = dw_eval_expr(arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, ®s, 0, 0, &expr_value); // validate eval result - T_Ok(expr_eval == DW_ExprEvalResult_Ok); - T_Ok(expr_value.type == DW_ExprValueType_Addr); - T_Ok(expr_value.addr == (1 + 44)); + TestCheck(expr_eval == DW_ExprEvalResult_Ok); + TestCheck(expr_value.type == DW_ExprValueType_Addr); + TestCheck(expr_value.addr == (1 + 44)); } -TEST(frame_relative_variable) +Test(frame_relative_variable) { DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(FBReg), DW_ExprEnc_SLEB128(-50) }; String8 expr_data = dw_encode_expr(arena, Arch_x64, DW_Format_64Bit, expr_encs, ArrayCount(expr_encs)); @@ -960,9 +960,9 @@ TEST(frame_relative_variable) DW_ExprValue expr_value; DW_ExprEvalResult expr_eval = dw_eval_expr(arena, Arch_x64, DW_Format_64Bit, frame_base, 0, 0, max_U64, expr, 0, 0, 0, &expr_value); - T_Ok(expr_eval == DW_ExprEvalResult_Ok); - T_Ok(expr_value.type == DW_ExprValueType_Addr); - T_Ok(expr_value.addr == frame_base -50); + TestCheck(expr_eval == DW_ExprEvalResult_Ok); + TestCheck(expr_value.type == DW_ExprValueType_Addr); + TestCheck(expr_value.addr == frame_base -50); } internal @@ -972,7 +972,7 @@ MACHINE_OP_MEM_READ(t_machine_op_mem_read) return MachineOpResult_Ok; } -TEST(call_by_reference) +Test(call_by_reference) { U8 *memory = push_array(arena, U8, 128); U64 value = 0xc0ffee; @@ -992,12 +992,12 @@ TEST(call_by_reference) DW_ExprValue expr_value = { 0 }; DW_ExprEvalResult expr_eval = dw_eval_expr(arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, ®s, t_machine_op_mem_read, 0, &expr_value); - T_Ok(expr_value.type == DW_ExprValueType_Generic); - T_Ok(expr_value.generic.size == sizeof(U64)); - T_Ok(*(U64 *)expr_value.generic.str == value); + TestCheck(expr_value.type == DW_ExprValueType_Generic); + TestCheck(expr_value.generic.size == sizeof(U64)); + TestCheck(*(U64 *)expr_value.generic.str == value); } -TEST(plus_uconst) +Test(plus_uconst) { U64 struct_addr = 0x123; DW_ExprEnc expr_encs[] = { DW_ExprEnc_Op(Addr), DW_ExprEnc_Addr(struct_addr), DW_ExprEnc_Op(PlusUConst), DW_ExprEnc_ULEB128(4) }; @@ -1007,12 +1007,12 @@ TEST(plus_uconst) DW_ExprValue expr_value; DW_ExprEvalResult expr_eval = dw_eval_expr(arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, 0, t_machine_op_mem_read, 0, &expr_value); - T_Ok(expr_value.type == DW_ExprValueType_Addr); - T_Ok(expr_value.addr == 0x123 + 4); + TestCheck(expr_value.type == DW_ExprValueType_Addr); + TestCheck(expr_value.addr == 0x123 + 4); } #if 0 -TEST(reg_split_spill) +Test(reg_split_spill) { // setup register context X64_RegBlock regs = {0}; @@ -1037,4 +1037,3 @@ TEST(reg_split_spill) DW_ExprEvalResult expr_eval = dw_eval_expr(arena, Arch_x64, DW_Format_64Bit, 0, 0, 0, max_U64, expr, regs_read_dwarf_x64, ®s, 0, 0, &expr_value); } #endif - diff --git a/src/torture/torture_radlink.c b/src/linker/tests/linker_tests.c similarity index 99% rename from src/torture/torture_radlink.c rename to src/linker/tests/linker_tests.c index f7ec015f..0b0e5e0d 100644 --- a/src/torture/torture_radlink.c +++ b/src/linker/tests/linker_tests.c @@ -644,14 +644,14 @@ TEST(merge) t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.test entry.obj test.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_CircularMerge); } // circular merge with extra link t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.data /merge:.data=.test entry.obj test.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_CircularMerge); } @@ -677,14 +677,14 @@ TEST(merge) // illegal merge with .reloc t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.reloc entry.obj test.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge); } // illegal merge with .rsrc t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /merge:.test=.rsrc entry.obj test.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge); } @@ -1784,7 +1784,7 @@ TEST(abs_vs_common) if (g_last_exit_code == 0) { // TODO: validate that linker issues multiply defined symbol error t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe common.obj abs.obj entry.obj"); - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); } else { T_Ok(g_last_exit_code != 0); @@ -2092,7 +2092,7 @@ TEST(undef_reloc_section) T_Ok(t_write_file(str8_lit("sec_defn.obj"), sec_defn_obj)); t_invoke_linkerf("/subsystem:console /entry:my_entry /out:a.exe main.obj sec_defn.obj"); - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_SectRefsDiscardedMemory); } else { T_Ok(g_last_exit_code != 0); @@ -2420,7 +2420,7 @@ TEST(base_relocs) // it is illegal to merge .reloc with other sections t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.reloc=.rdata main.obj func.obj"); - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge); } else { T_Ok(g_last_exit_code != 0); @@ -2428,7 +2428,7 @@ TEST(base_relocs) // the other way around is illegal too t_invoke_linkerf("/subsystem:console /entry:my_entry /dynamicbase /largeaddressaware:no /out:a.exe /merge:.rdata=.reloc main.obj func.obj"); - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_IllegalSectionMerge); } else { T_Ok(g_last_exit_code != 0); @@ -2610,7 +2610,7 @@ TEST(import_export) T_Ok(g_last_exit_code == 0); // validate export table in export.dll - if (t_id_linker() == T_Linker_RAD) { + if (t_id_linker() == Linker_radlink) { // validate export table in export.dll { String8 dll = t_read_file(arena, str8_lit("export.dll")); @@ -2905,7 +2905,7 @@ TEST(comdat_no_duplicates) t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe a.obj b.obj entry.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); } + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); } t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj entry.obj"); T_Ok(g_last_exit_code == 0); @@ -3001,7 +3001,7 @@ TEST(comdat_same_size) t_invoke_linkerf("/subsystem:console /entry:entry /out:b.exe a.obj b.obj c.obj entry.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); } + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_MultiplyDefinedSymbol); } } TEST(comdat_exact_match) @@ -3313,7 +3313,7 @@ TEST(comdat_associative_loop) t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe loop.obj entry.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_AssociativeLoop); } + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_AssociativeLoop); } } TEST(comdat_associative_non_comdat) @@ -3430,7 +3430,7 @@ TEST(comdat_associative_out_of_bounds) t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe entry.obj bad.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_IllData); } + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_IllData); } } TEST(comdat_with_offset) @@ -3790,7 +3790,7 @@ TEST(include) // test unresolved include t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /include:ewq entry.obj"); T_Ok(g_last_exit_code != 0); - if (t_id_linker() == T_Linker_RAD) { T_Ok(g_last_exit_code == LNK_Error_UnresolvedSymbol); } + if (t_id_linker() == Linker_radlink) { T_Ok(g_last_exit_code == LNK_Error_UnresolvedSymbol); } } TEST(communal_var_vs_regular) @@ -4811,7 +4811,7 @@ TEST(fail_if_mismatch) T_Ok(t_write_file(str8_lit("a2.obj"), a2)); t_invoke_linkerf("entry.obj a1.obj a2.obj /entry:entry /subsystem:console /out:a2.exe"); - if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch); + if (t_id_linker() == Linker_radlink) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch); else T_Ok(g_last_exit_code != 0); // ------------------------------------------------------------ @@ -4829,14 +4829,14 @@ TEST(fail_if_mismatch) T_Ok(t_write_file(str8_lit("conf_dirs.obj"), conf_dirs)); t_invoke_linkerf("entry.obj conf_dirs.obj /entry:entry /subsystem:console /out:conf_dirs.exe"); - if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch); + if (t_id_linker() == Linker_radlink) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch); else T_Ok(g_last_exit_code != 0); // ------------------------------------------------------------ // passing switch on command line t_invoke_linkerf("entry.obj a1.obj /FAILIFMISMATCH:a=2 /out:cmddir.exe"); - if (t_id_linker() == T_Linker_RAD) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch); + if (t_id_linker() == Linker_radlink) T_Ok(g_last_exit_code == LNK_Error_FailIfMismatch); else T_Ok(g_last_exit_code != 0); } diff --git a/src/linux/base/linux_base.c b/src/linux/base/linux_base.c index ce11d0eb..0dc09903 100644 --- a/src/linux/base/linux_base.c +++ b/src/linux/base/linux_base.c @@ -127,6 +127,17 @@ lnx_thread_entry_point(void *ptr) return 0; } +//////////////////////////////// +//~ rjf: @per_os_impl Debugger Attachment Checking + +internal B32 +debugger_is_attached(void) +{ + B32 result = 0; + // TODO(rjf) + return result; +} + //////////////////////////////// //~ rjf: @per_os_impl Platform Time Functions @@ -1292,13 +1303,13 @@ internal B32 process_join(Process process, U64 endt_us, U64 *exit_code_out) { B32 result = 0; - + pid_t pid = (pid_t)process.u64[0]; for(;;) { int status = 0; pid_t wait_result = LNX_RETRY_ON_EINTR(waitpid(pid, &status, (endt_us == max_U64) ? 0 : WNOHANG)); - + if((wait_result == pid) && (WIFEXITED(status) || WIFSIGNALED(status))) { result = 1; @@ -1309,13 +1320,13 @@ process_join(Process process, U64 endt_us, U64 *exit_code_out) } break; } - + if(wait_result == -1) { break; } if(endt_us == 0) { break; } - + U64 now_us = now_time_us(); if(now_us >= endt_us) { break; } - + U64 left_us = endt_us - now_us; U64 sleep_us = Min(left_us, Thousand(1)); usleep((useconds_t)sleep_us); diff --git a/src/torture/torture_md.c b/src/mdesk/tests/mdesk_tests.c similarity index 96% rename from src/torture/torture_md.c rename to src/mdesk/tests/mdesk_tests.c index 5eabb69f..8c3b6a13 100644 --- a/src/torture/torture_md.c +++ b/src/mdesk/tests/mdesk_tests.c @@ -1,6 +1,7 @@ -#define T_Group "MD" +// Copyright (c) Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) -SKIP(md_tokenizer) +SkippedTest(md_tokenizer) { MD_TokenizeResult result; @@ -126,5 +127,3 @@ SKIP(md_tokenizer) T_Ok(result.tokens.v[1].flags & MD_TokenFlag_Identifier); } } - -#undef T_Group diff --git a/src/torture/dbg_tests/basic_stepping_test.c b/src/mule/old_test_debuggees/basic_stepping_test.c similarity index 100% rename from src/torture/dbg_tests/basic_stepping_test.c rename to src/mule/old_test_debuggees/basic_stepping_test.c diff --git a/src/torture/dbg_tests/gnu_sysv_hash.c b/src/mule/old_test_debuggees/gnu_sysv_hash.c similarity index 100% rename from src/torture/dbg_tests/gnu_sysv_hash.c rename to src/mule/old_test_debuggees/gnu_sysv_hash.c diff --git a/src/torture/dbg_tests/mule.cpp b/src/mule/old_test_debuggees/mule.cpp similarity index 100% rename from src/torture/dbg_tests/mule.cpp rename to src/mule/old_test_debuggees/mule.cpp diff --git a/src/torture/dbg_tests/step_into_for_goto.c b/src/mule/old_test_debuggees/step_into_for_goto.c similarity index 100% rename from src/torture/dbg_tests/step_into_for_goto.c rename to src/mule/old_test_debuggees/step_into_for_goto.c diff --git a/src/torture/dbg_tests/step_into_lazy_module_func.c b/src/mule/old_test_debuggees/step_into_lazy_module_func.c similarity index 100% rename from src/torture/dbg_tests/step_into_lazy_module_func.c rename to src/mule/old_test_debuggees/step_into_lazy_module_func.c diff --git a/src/torture/dbg_tests/step_into_static_module_func.c b/src/mule/old_test_debuggees/step_into_static_module_func.c similarity index 95% rename from src/torture/dbg_tests/step_into_static_module_func.c rename to src/mule/old_test_debuggees/step_into_static_module_func.c index 0c3f4937..f261122a 100644 --- a/src/torture/dbg_tests/step_into_static_module_func.c +++ b/src/mule/old_test_debuggees/step_into_static_module_func.c @@ -1,37 +1,37 @@ -/// test: { -/// windows: { -/// compile: "/Od /Z7 /c main.c module.c" -/// link: "/fixed /debug:full /dll module.obj" -/// link: "/fixed /debug:full module.lib main.obj /out:main.exe /incremental:no" -/// launch: "main.exe" -/// } -/// linux: { -/// compile: "-O0 -g module.c -fPIC -shared -o libmodule.so" -/// compile: "-O0 -g main.c -L. -lmodule -o main -Wl,-rpath,%CWD%,-z,now -fno-plt" -/// launch: "./main" -/// } -/// } -/// - -/// file: "module.c" - -#if _WIN32 -__declspec(dllexport) -#endif -int foo(int a) -{ /// 3: at - return a + 1; -} - -/// file: "main.c" - -#if _WIN32 -__declspec(dllimport) -#endif -int foo(); - -int main() -{ /// 1: { step_into, at, step_over } - foo(123); /// 2: { at, step_into } -} - +/// test: { +/// windows: { +/// compile: "/Od /Z7 /c main.c module.c" +/// link: "/fixed /debug:full /dll module.obj" +/// link: "/fixed /debug:full module.lib main.obj /out:main.exe /incremental:no" +/// launch: "main.exe" +/// } +/// linux: { +/// compile: "-O0 -g module.c -fPIC -shared -o libmodule.so" +/// compile: "-O0 -g main.c -L. -lmodule -o main -Wl,-rpath,%CWD%,-z,now -fno-plt" +/// launch: "./main" +/// } +/// } +/// + +/// file: "module.c" + +#if _WIN32 +__declspec(dllexport) +#endif +int foo(int a) +{ /// 3: at + return a + 1; +} + +/// file: "main.c" + +#if _WIN32 +__declspec(dllimport) +#endif +int foo(); + +int main() +{ /// 1: { step_into, at, step_over } + foo(123); /// 2: { at, step_into } +} + diff --git a/src/torture/torture_dbg.c b/src/raddbg/tests/raddbg_tests.c similarity index 83% rename from src/torture/torture_dbg.c rename to src/raddbg/tests/raddbg_tests.c index 9c23b219..4dcca5f1 100644 --- a/src/torture/torture_dbg.c +++ b/src/raddbg/tests/raddbg_tests.c @@ -1,6 +1,207 @@ // Copyright (c) Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +//////////////////////////////// +// IPC Controller + +typedef struct +{ + B32 running; + U64 run_gen; + U64 ip; + Arch arch; + U64 vaddr_min; + U64 vaddr_max; + U64 ip_vaddr; + U64 sp_base; + U64 tls_root; + U64 tls_index; + U64 tls_offset; + U64 timestamp; + U64 exception_code; + U64 bp_flags; + String8 string; + OperatingSystem target_os; + U64 tls_model; + String8 stop_cause; +} T_DbgState; + +typedef struct +{ + String8 file_path; + TxtPt pt; + Rng1U64 voff_range; +} T_DbgLine; + +typedef struct +{ + U64 count; + T_DbgLine *v; +} T_DbgLineArray; + +typedef struct +{ + String8 expr; + String8 value; + String8 type; + String8 error; +} T_Eval; + +//////////////////////////////// +// Dbg Script + +#define T_DbgScriptCmdKind_XList \ +X(Breakpoint, "bp") \ +X(ClearBreakpoints, "bp_clear") \ +X(Halt, "halt") \ +X(Run, "run") \ +X(RunToLine, "run_to_line") \ +X(StepOver, "step_over") \ +X(StepInto, "step_into") \ +X(StepOut, "step_out") \ +X(StepOverInst, "step_over_inst") \ +X(StepIntoInst, "step_over_inst") \ +X(StepOverLine, "step_over_line") \ +X(StepIntoLine, "step_into_line") \ +X(KillAll, "kll_all") \ +X(At, "at") \ +X(Eval, "eval") + +typedef enum +{ + T_DbgScriptCmdKind_Null, +#define X(n,...) T_DbgScriptCmdKind_##n, + T_DbgScriptCmdKind_XList +#undef X + T_DbgScriptCmdKind_Count +} T_DbgScriptCmdKind; + +#define T_DbgScriptDirectiveKind_XList \ +X(Compile, "compile") \ +X(Link, "link") \ +X(Launch, "launch") \ +X(Skip, "skip") + +typedef enum +{ + T_DbgScriptDirectiveKind_Null, +#define X(q,w) T_DbgScriptDirectiveKind_##q, + T_DbgScriptDirectiveKind_XList +#undef X + T_DbgScriptDirectiveKind_Count, +} T_DbgScriptDirectiveKind; + +typedef struct T_DbgScriptFile +{ + struct T_DbgScriptFile *next; + String8 path; + String8 source; + TxtPt pt; +} T_DbgScriptFile; + +typedef struct +{ + U64 count; + T_DbgScriptFile *first; + T_DbgScriptFile *last; +} T_DbgScriptFileList; + +typedef struct T_DbgScriptCmd +{ + struct T_DbgScriptCmd *next; + T_DbgScriptCmdKind kind; + TxtPt pt; + S64 at_delta; +} T_DbgScriptCmd; + +typedef struct T_DbgScriptProgram +{ + TxtPt pt; + U64 order; + OperatingSystem os; + T_DbgScriptFile *file; + U64 count; + T_DbgScriptCmd *first; + T_DbgScriptCmd *last; + struct T_DbgScriptProgram *next; +} T_DbgScriptProgram; + +typedef struct +{ + U64 count; + T_DbgScriptProgram *first; + T_DbgScriptProgram *last; +} T_DbgScriptProgramList; + +typedef struct T_DbgScriptDirective +{ + struct T_DbgScriptDirective *next; + T_DbgScriptDirectiveKind kind; + TxtPt pt; + String8 args; + union { + struct { + Compiler cc; + } compile; + }; +} T_DbgScriptDirective; + +typedef struct +{ + U64 count; + T_DbgScriptDirective *first; + T_DbgScriptDirective *last; +} T_DbgScriptDirectiveList; + +typedef struct +{ + String8 file_path; + T_DbgScriptDirectiveList directives[OperatingSystem_COUNT][T_DbgScriptDirectiveKind_Count]; + T_DbgScriptFileList files; + U64 program_count; + T_DbgScriptProgram **programs; + B32 skip; +} T_DbgScript; + + +//////////////////////////////// +// IPC Controller + +// error helper +internal void t_errorf_md(String8 file_name, String8 source, MD_Node *n, char *fmt, ...); + +// reply parse helpers +internal B32 t_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out); +internal B32 t_ipc_parse_u32(MD_Node *node, String8 child_name, U32 *out); +internal B32 t_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out); +internal B32 t_ipc_parse_b32(MD_Node *node, String8 child_name, B32 *out); +#define t_ipc_parse_int(n, c, ptr) t_ipc_parse_int_(n, c, sizeof(*ptr), ptr) + +// debugger commands +internal B32 t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, MD_ParseResult *reply_out); +internal B32 t_dbg_send_cmdf(U64 timeout_us, Arena *reply_arena, MD_ParseResult *reply_out, char *fmt, ...); +internal T_DbgState * t_dbg_state(Arena *arena, U64 timeout_us); +internal B32 t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_us); +internal String8 t_dbg_value_from_expr(Arena *arena, String8 expr); +internal String8 t_dbg_value_from_exprf(Arena *arena, char *fmt, ...); +internal B32 t_dbg_send_cmd_and_wait_stop(String8 cmd, U64 timeout_us); +internal B32 t_dbg_ping(U64 timeout_us); +internal B32 t_dbg_bp_add_line(String8 file, U64 line); +internal B32 t_dbg_bp_add_func(String8 func_name); +internal B32 t_dbg_bp_add_addr(U64 addr); +internal B32 t_dbg_launch(String8 cmdline, U64 timeout_us); +internal B32 t_dbg_eval(Arena *arena, String8 expr, T_Eval *eval_out); + +//////////////////////////////// +// Dbg Script + +internal String8 t_string_from_dbg_script_cmd_kind(T_DbgScriptCmdKind v); +internal T_DbgScriptCmdKind t_dbg_script_cmd_kind_from_string(String8 cmd); +internal B32 t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript *script_out); +internal B32 t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us); +internal void t_dbg_register_script_tests(Arena *arena, String8 folder_path); + + #define T_Dbg_DefaultTimeout TIMEOUT_SEC(5) extern B32 g_stop_on_first_fail_or_crash; @@ -119,7 +320,7 @@ t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, MD_ParseResult * // parse reply Arena *a = reply_arena ? reply_arena : scratch.arena; String8 reply_text = str8_copy(a, g_output); - + t_infof("IPC-Reply: \"%S\"\n", reply_text); MD_ParseResult reply = md_parse_from_text(a, str8_lit("ipc_reply"), reply_text); @@ -153,13 +354,13 @@ internal T_DbgState * t_dbg_state(Arena *arena, U64 timeout_us) { Temp scratch = scratch_begin(&arena, 1); - + T_DbgState *result = 0; // send status request MD_ParseResult reply = {0}; if ( ! t_dbg_send_cmd(str8_lit("state"), timeout_us, arena, &reply)) { goto exit; } - + // parse reply MD_Node *state_md = md_child_from_string(reply.root, str8_lit("state"), 0); MD_Node *stop_event_md = md_child_from_string(state_md, str8_lit("stop_event"), 0); @@ -186,14 +387,14 @@ t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_u // send line map request MD_ParseResult reply = {0}; if(!t_dbg_send_cmdf(timeout_us, arena, &reply, "line_from_vaddr 0x%llx", vaddr)) { goto exit; } - + // parse reply MD_Node *lines_md = md_child_from_string(reply.root, str8_lit("lines"), 0); - + typedef struct Node { struct Node *next; T_DbgLine v; } Node; Node *first_line = 0, *last_line = 0; U64 line_count = 0; - + for MD_EachNode(n, lines_md->first) { T_DbgLine line = {0}; if ( ! t_ipc_parse_string(n, str8_lit("file_path"), &line.file_path)) { t_infof("INFO: 'lines' is missing 'file_path'\n"); goto exit; } @@ -201,13 +402,13 @@ t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_u if ( ! t_ipc_parse_int (n, str8_lit("column_num"), &line.pt.column)) { t_infof("INFO: 'lines' is missing 'column_num'\n"); goto exit; } if ( ! t_ipc_parse_int (n, str8_lit("voff_range_min"), &line.voff_range.min)) { t_infof("INFO: 'lines' is missing 'voff_range_min'\n"); goto exit; } if ( ! t_ipc_parse_int (n, str8_lit("voff_range_max"), &line.voff_range.max)) { t_infof("INFO: 'lines' is missing 'voff_range_max'\n"); goto exit; } - + Node *n = push_array(scratch.arena, Node, 1); n->v = line; SLLQueuePush(first_line, last_line, n); line_count += 1; } - + if (lines_out && line_count > 0) { lines_out->count = 0; lines_out->v = push_array(arena, T_DbgLine, line_count); @@ -479,9 +680,9 @@ internal B32 t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript *script_out) { Temp scratch = scratch_begin(&arena, 1); - + B32 is_ok = 0; - + T_DbgScript script = { .file_path = push_str8_copy(arena, file_path) }; // parse out mdesk out of the comments @@ -489,41 +690,41 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript MD_TokenChunkList annot_chunks = {0}; for EachIndex(token_idx, source_tokens.tokens.count) { MD_Token source_token = source_tokens.tokens.v[token_idx]; - + // skip non-comment tokens if (~source_token.flags & MD_TokenFlag_Comment) { continue; } - + // read token string String8 source_token_string = str8_substr(source, source_token.range); - + if (str8_match_wildcard(source_token_string, str8_lit("*///*"), 0)) { - + // drop comment prefix String8 raw_annots = str8_skip(str8_skip_chop_whitespace(source_token_string), 3); - + // parse annotations MD_TokenizeResult annot_parse = md_tokenize_from_text(scratch.arena, raw_annots); - + for EachIndex(i, annot_parse.tokens.count) { MD_Token annot_token = annot_parse.tokens.v[i]; - + // adjust token range so they point back into the source file annot_token.range = shift_1u64(annot_token.range, (U64)(raw_annots.str - source.str)); - + // append annotation token md_token_chunk_list_push(scratch.arena, &annot_chunks, 4096, annot_token); } - + // append new line token MD_Token newline_token = md_token_make(r1u64(source_token.range.max, source_token.range.max), MD_TokenFlag_Newline); md_token_chunk_list_push(scratch.arena, &annot_chunks, 4096, newline_token); } } - + // script annotations -> mdesk tree MD_TokenArray annot_tokens = md_token_array_from_chunk_list(scratch.arena, &annot_chunks); MD_ParseResult script_parse = md_parse_from_text_tokens(scratch.arena, file_path, source, annot_tokens); - + // was parse ok? -> error if (script_parse.msgs.worst_message_kind >= MD_MsgKind_Error) { t_errorf("ERROR: cannot tokenize mdesk file: \"%S\"\n", file_path); @@ -531,11 +732,11 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript String8 msg_kind_string = {0}; switch(msg->kind) { - default:{}break; - case MD_MsgKind_Note: {msg_kind_string = str8_lit("note");}break; - case MD_MsgKind_Warning: {msg_kind_string = str8_lit("warning");}break; - case MD_MsgKind_Error: {msg_kind_string = str8_lit("error");}break; - case MD_MsgKind_FatalError: {msg_kind_string = str8_lit("fatal error");}break; + default:{}break; + case MD_MsgKind_Note: {msg_kind_string = str8_lit("note");}break; + case MD_MsgKind_Warning: {msg_kind_string = str8_lit("warning");}break; + case MD_MsgKind_Error: {msg_kind_string = str8_lit("error");}break; + case MD_MsgKind_FatalError: {msg_kind_string = str8_lit("fatal error");}break; } TxtPt pt = mg_txt_pt_from_string_off(source, msg->node->src_offset); String8 loc = push_str8f(scratch.arena, "%S:%I64d:%I64d", file_path, pt.line, pt.column); @@ -543,38 +744,38 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript } goto exit; } - + // @test: { // first child node of the root must be a test header MD_Node *test = script_parse.root->first; - + // is node test? -> error if ( ! str8_matchi(test->string, str8_lit("test"))) { t_errorf("ERROR: %S: missing test header\n", file_path); goto exit; } - + for MD_EachNode(n, test->first) { OperatingSystem os = operating_system_from_string(n->string); - + // is OS string correct? -> error if (os == OperatingSystem_Null && n->string.size > 0) { t_errorf_md(file_path, source, n, "test is defined for unknown os: \"%S\"\n", n->string); goto exit; } - + for MD_EachNode(field, n->first) { // define table for mapping string to a directive kind struct { T_DbgScriptDirectiveKind kind; String8 string; } dir_string_map[] = { - #define X(q,w) { T_DbgScriptDirectiveKind_##q, str8_lit(w) }, +#define X(q,w) { T_DbgScriptDirectiveKind_##q, str8_lit(w) }, T_DbgScriptDirectiveKind_XList - #undef X +#undef X }; - + // string -> directive T_DbgScriptDirectiveKind kind = T_DbgScriptDirectiveKind_Null; for EachElement(i, dir_string_map) { @@ -583,18 +784,18 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript break; } } - + // was directive found? -> error if (kind == T_DbgScriptDirectiveKind_Null) { t_errorf_md(file_path, source, n, "unknown field in test header \"%S\"\n", field->string); goto exit; } - + // alloc directive node T_DbgScriptDirective *dir = push_array(arena, T_DbgScriptDirective, 1); dir->kind = kind; dir->pt = mg_txt_pt_from_string_off(source, field->src_offset); - + // TODO: collapse if (field->flags & MD_NodeFlag_HasBraceLeft) { // walk each sub-field of the MD node, and find nodes for the directive @@ -607,9 +808,9 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript t_errorf_md(file_path, source, sub_field, "value of CC must be a string literal e.g. CC: \"clang\"\n"); goto exit; } - - if (str8_matchi(cc->string, str8_lit("clang"))) { dir->compile.cc = T_Compiler_Clang; } - else if (str8_matchi(cc->string, str8_lit("cl"))) { dir->compile.cc = T_Compiler_Cl; } + + if (str8_matchi(cc->string, str8_lit("clang"))) { dir->compile.cc = Compiler_clang; } + else if (str8_matchi(cc->string, str8_lit("cl"))) { dir->compile.cc = Compiler_msvc; } else { t_errorf_md(file_path, source, cc, "unknown compiler name: \"%S\"\n", sub_field->string); goto exit; @@ -639,23 +840,23 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript t_errorf_md(file_path, source, field, "missing value on field %S\n", field->string); goto exit; } - + // more than one node? -> error if ( ! md_node_is_nil(field->first->next)) { t_errorf_md(file_path, source, field, "field %S accepts only one value\n", field->string); goto exit; } - + // node is not a string literal? -> error if (~field->first->flags & MD_NodeFlag_StringLiteral) { t_errorf_md(file_path, source, field, "field %S accepts only strings\n", field->string); goto exit; } - + // copy string from MD node dir->args = str8_copy(arena, field->first->string); } - + // append new directive T_DbgScriptDirectiveList *list = &script.directives[os][kind]; SLLQueuePush(list->first, list->last, dir); @@ -670,18 +871,18 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript MD_NodePtrList files = {0}; for MD_EachNode(n, script_parse.root->first->next) { if (str8_matchi(n->string, str8_lit("file"))) { - + // is file value a string? -> error if ( ! md_node_is_nil(n->first->next) || ! (n->first->flags & MD_NodeFlag_StringLiteral)) { t_errorf_md(file_path, source, n, "value of the 'file' must be a string, (e.g. file: \"main.c\")\n"); goto exit; } - + // append file node md_node_ptr_list_push(scratch.arena, &files, n); } } - + // no nodes? -> treat whole file as a script if (files.count == 0) { MD_Node *whole_file = push_array(scratch.arena, MD_Node, 1); @@ -691,14 +892,14 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript whole_file->first->string = str8_skip_last_slash(file_path); md_node_ptr_list_push(scratch.arena, &files, whole_file); } - + HashMap files_hm = {0}; for EachNode(n_ptr, MD_NodePtrNode, files.first) { MD_Node *n = n_ptr->v; - + // get file name String8 file_name = n->first->string; - + // was file seen? -> error MD_Node *is_declared = hash_map_search_string_raw(&files_hm, file_name); if (is_declared) { @@ -707,19 +908,19 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript goto exit; } hash_map_push_string_raw(scratch.arena, &files_hm, file_name, n); - + // file ends at EOF or before the next file directive U64 src_opl = source.size; if (n_ptr->next) { src_opl = n_ptr->next->v->src_offset; } - + // sub-string the source file String8 sub_source = str8_substr(source, r1u64(n->src_offset, src_opl)); U64 file_min = str8_find_needle(sub_source, 0, str8_lit("\n"), 0) + 1; U64 file_max = str8_find_needle_reverse(sub_source, 0, str8_lit("\n"), 0); sub_source = str8_substr(sub_source, r1u64(file_min, file_max)); - + // append new file node T_DbgScriptFile *file = push_array(arena, T_DbgScriptFile, 1); file->path = t_make_file_path(arena, file_name); @@ -737,9 +938,9 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript // find order number nodes U64 order = 0; if ( ! try_u64_from_str8_c_rules(n->string, &order)) { - continue; + continue; } - + // correlate MD node to the script file T_DbgScriptFile *file = 0; for (file = script.files.first; file != 0; file = file->next) { @@ -747,7 +948,7 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript break; } } - + // found file? -> error if (file == 0) { t_errorf_md(file_path, source, n, "failed to correlate MD node to the script source file\n"); @@ -760,7 +961,7 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript t_errorf_md(file_path, source, n, "duplicate order number %llu found, previous defined at %llu\n", order, p->pt.line); goto exit; } - + // alloc & fill out program p = push_array(arena, T_DbgScriptProgram, 1); p->pt = mg_txt_pt_from_string_off(source, n->src_offset); @@ -779,9 +980,9 @@ t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript // parse cmd args MD_Node *cmd_arg = cmd_n->first; - + if (md_node_is_nil(cmd_arg)) { continue; } - + if (cmd->kind == T_DbgScriptCmdKind_At) { if ( ! try_s64_from_str8_c_rules(cmd_arg->string, &cmd->at_delta)) { t_errorf_md(file_path, source, cmd_arg, "failed to parse \"%S\"", cmd_arg->string); @@ -868,7 +1069,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us) S64 at_line_s64 = (S64)(program->pt.line - program->file->pt.line) + cmd->at_delta; U64 at_line_u64 = at_line_s64 >= 0 ? (U64)at_line_s64 : 0; AssertAlways(at_line_u64 > 0); - + if (lines.count == 0) { t_errorf("ERROR: %S:%llu:%llu: no source location maps for vaddr: 0x%llx\n", script->file_path, cmd->pt.line, cmd->pt.column, ip); goto exit; @@ -877,7 +1078,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us) // match expected vs current debugger locations for EachIndex(i, lines.count) { B32 mismatch = lines.v[i].pt.line != at_line_u64 || - !str8_match(lines.v[i].file_path, program->file->path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive); + !str8_match(lines.v[i].file_path, program->file->path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive); if (mismatch) { t_errorf("ERROR: %S:%llu: location check did not pass:\n", script->file_path, cmd->pt.line); t_errorf(" Expected: %S:%llu\n", program->file->path, at_line_u64); @@ -914,7 +1115,7 @@ T_RunSig(dbg_script_runner) // source -> script T_DbgScript script = {0}; if ( ! t_dbg_parse_script(arena, user_data, source, &script)) { - result_out->status = T_RunStatus_Fail; + result_out->status = TestStatus_Fail; goto exit; } @@ -925,7 +1126,7 @@ T_RunSig(dbg_script_runner) T_Ok(0); } } - + // compiler vars HashTable *script_vars = hash_table_init(arena, 1000); hash_table_push_path_string(arena, script_vars, str8_lit("FILE"), user_data); @@ -934,37 +1135,37 @@ T_RunSig(dbg_script_runner) // run compilers for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Compile].first) { - T_Compiler compiler = directive->compile.cc; - + Compiler compiler = directive->compile.cc; + // pick default compiler if none selected - if (directive->compile.cc == T_Compiler_Null) { + if (directive->compile.cc == Compiler_Null) { switch (OperatingSystem_CURRENT) { - case OperatingSystem_Windows: { compiler = T_Compiler_Cl; } break; - case OperatingSystem_Linux: { compiler = T_Compiler_Clang; } break; + case OperatingSystem_Windows: { compiler = Compiler_msvc; } break; + case OperatingSystem_Linux: { compiler = Compiler_clang; } break; } } // get compiler path String8 compiler_path = {0}; switch (compiler) { - case T_Compiler_Null: break; - case T_Compiler_Cl: compiler_path = t_cl_path(); break; - case T_Compiler_Clang: compiler_path = t_clang_path(); break; - case T_Compiler_Gcc: compiler_path = t_gcc_path(); break; + case Compiler_Null: break; + case Compiler_msvc: compiler_path = t_cl_path(); break; + case Compiler_clang: compiler_path = t_clang_path(); break; + case Compiler_gcc: compiler_path = t_gcc_path(); break; } // invoke compiler with arguments from directive String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args); - - if (compiler == T_Compiler_Cl) { expanded_args = str8f(arena, "/nologo %S", expanded_args); } - + + if (compiler == Compiler_msvc) { expanded_args = str8f(arena, "/nologo %S", expanded_args); } + if (t_invoke(compiler_path, expanded_args, max_U64) == 0) { t_errorf("ERROR: failed to launch compiler: \"%S %S\"\n", compiler_path, expanded_args); T_Ok(0); } - - if (compiler == T_Compiler_Cl) { g_output = str8_skip(g_output, str8_chop_line(&g_output).size); } // file name print - + + if (compiler == Compiler_msvc) { g_output = str8_skip(g_output, str8_chop_line(&g_output).size); } // file name print + if (g_last_exit_code) { t_errorf("ERROR: %S:%llu: %S\n", script.file_path, (unsigned long long)directive->pt.line, g_errors); if (g_stop_on_first_fail_or_crash) { @@ -986,19 +1187,13 @@ T_RunSig(dbg_script_runner) T_Ok(0); } } - - // if test does not have any directive -> skip - U64 total_directive_count = 0; - for EachIndex(i, ArrayCount(script.directives[OperatingSystem_CURRENT])) { - total_directive_count += script.directives[OperatingSystem_CURRENT][i].count; - } - + // is skip flag set? -> exit - if (g_build_only || total_directive_count == 0 || script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Skip].count) { - result_out->status = T_RunStatus_Skip; + if (g_build_only || script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Skip].count) { + result_out->status = TestStatus_Skip; goto exit; } - + // launch targets for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Launch].first) { String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args); diff --git a/src/torture/torture_d2r.c b/src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c similarity index 100% rename from src/torture/torture_d2r.c rename to src/rdi_from_dwarf/tests/rdi_from_dwarf_tests.c diff --git a/src/torture/torture_p2r.c b/src/rdi_from_pdb/tests/rdi_from_pdb_tests.c similarity index 100% rename from src/torture/torture_p2r.c rename to src/rdi_from_pdb/tests/rdi_from_pdb_tests.c diff --git a/src/torture/torture.c b/src/torture/torture.c index f7162932..cf32d384 100644 --- a/src/torture/torture.c +++ b/src/torture/torture.c @@ -72,31 +72,6 @@ t_test_group_from_name(Arena *arena, String8 pattern) //////////////////////////////// -internal char * -t_string_from_result(T_RunStatus v) -{ - switch (v) { - case T_RunStatus_Fail: return "FAIL"; - case T_RunStatus_Crash: return "CRASH"; - case T_RunStatus_Pass: return "PASS"; - case T_RunStatus_Skip: return "SKIP"; - default: break; - } - return 0; -} - -internal char * -t_color_from_result(T_RunStatus v) -{ - switch (v) { -#define X(n,c,...) case T_RunStatus_##n: return c; - T_Run_XList -#undef X - default: break; - } - return "null"; -} - internal void t_break_if_debugger_present(void) { @@ -108,14 +83,14 @@ t_break_if_debugger_present(void) #endif } -internal T_Linker +internal Linker t_id_linker(void) { String8 name = str8_chop_last_dot(str8_skip_last_slash(g_linker_path)); - if (str8_match(name, str8_lit("radlink"), StringMatchFlag_CaseInsensitive)) { return T_Linker_RAD; } - if (str8_match(name, str8_lit("link"), StringMatchFlag_CaseInsensitive)) { return T_Linker_MSVC; } - if (str8_match(name, str8_lit("lld-link"), StringMatchFlag_CaseInsensitive)) { return T_Linker_LLVM; } - return T_Linker_Null; + if (str8_match(name, str8_lit("radlink"), StringMatchFlag_CaseInsensitive)) { return Linker_radlink; } + if (str8_match(name, str8_lit("link"), StringMatchFlag_CaseInsensitive)) { return Linker_msvc; } + if (str8_match(name, str8_lit("lld-link"), StringMatchFlag_CaseInsensitive)) { return Linker_lld; } + return Linker_Null; } internal B32 @@ -212,19 +187,19 @@ t_run_caller(void *raw_ctx) Temp scratch = scratch_begin(0,0); g_is_first_print = 1; - + T_RunCtx *ctx = raw_ctx; - ctx->result.status = T_RunStatus_Pass; - + ctx->result.status = TestStatus_Pass; + String8List test_out = {0}; - + if (ctx->test->skip) { - ctx->result.status = T_RunStatus_Skip; + ctx->result.status = TestStatus_Skip; } else { ctx->test->r(scratch.arena, ctx->user_data, &ctx->result, &test_out); } - - if (ctx->result.status == T_RunStatus_Fail || ctx->result.status == T_RunStatus_Crash) { + + if (ctx->result.status == TestStatus_Fail || ctx->result.status == TestStatus_Crash) { for EachNode(n, String8Node, test_out.first) { t_errorf("%S", n->string); } @@ -235,27 +210,27 @@ t_run_caller(void *raw_ctx) t_errorf("%S\n", g_output); } } - + scratch_end(scratch); } -internal T_RunResult +internal TestResult t_run(T_Test *test, String8 user_data) { - T_RunCtx ctx = { .test = test, .user_data = user_data, .result.status = T_RunStatus_Fail }; + T_RunCtx ctx = { .test = test, .user_data = user_data, .result.status = TestStatus_Fail }; t_run_caller(&ctx); - - if (ctx.result.status == T_RunStatus_Fail || ctx.result.status == T_RunStatus_Crash) { + + if (ctx.result.status == TestStatus_Fail || ctx.result.status == TestStatus_Crash) { if (g_output.size > 0 || g_errors.size > 0) { t_errorf("Last captured output:\n"); if (g_output.size) { t_errorf("%S\n", g_output); } if (g_errors.size) { t_errorf("%S\n", g_errors); } } } - + fflush(stdout); fflush(stderr); - + return ctx.result; } @@ -336,7 +311,7 @@ t_cl_version(void) if ( ! version.size) { Temp scratch = scratch_begin(0, 0); - + t_invoke_cl(""); AssertAlways(g_last_exit_code == 0); @@ -344,10 +319,10 @@ t_cl_version(void) U64 version_lo = str8_find_needle(g_output, 0, needle, 0); version_lo += needle.size + 1; AssertAlways(version_lo < g_output.size); - + U64 version_hi = str8_find_needle(g_output, version_lo, str8_lit(" "), 0); AssertAlways(version_hi < g_output.size); - + version = str8_substr(g_output, r1u64(version_lo, version_hi)); AssertAlways(version.size > 0); @@ -355,7 +330,7 @@ t_cl_version(void) ArenaParams params = { .reserve_size = sizeof(buffer), .commit_size = sizeof(buffer), .optional_backing_buffer = buffer }; Arena *arena = arena_alloc_(¶ms); version = str8_copy(arena, version); - + MemoryZeroStruct(&g_output); scratch_end(scratch); } @@ -413,7 +388,7 @@ t_cwd_path(void) if (path[0] == 0) { Temp scratch = scratch_begin(0, 0); String8 cwd = get_current_path(scratch.arena); - + // TODO: linux and windows return two different things, we need to settle what to do here // should get_current_path return directory paths with slash or no slash if ((str8_match_wildcard(cwd, str8_lit("*\\"), 0) && str8_match_wildcard(cwd, str8_lit("*/"), 0))) { @@ -422,7 +397,7 @@ t_cwd_path(void) } else { cwd = str8_chop_last_slash(cwd); } - + MemoryCopyStr8(path, cwd); path[cwd.size] = 0; scratch_end(scratch); @@ -448,19 +423,19 @@ internal B32 t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) { Temp scratch = scratch_begin(&g_output_arena,1); - + B32 is_ok = 0; - + // clean up global state arena_clear(g_output_arena); MemoryZeroStruct(&g_output); g_last_exit_code = max_U64; - + String8List stdout_parts = {0}; String8List stderr_parts = {0}; U64 stdout_idx = 0; U64 stderr_idx = 1; - + #if OS_WINDOWS typedef enum { Win32CaptureState_Null, @@ -478,7 +453,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) String8List *parts; Win32CaptureState state; } Win32Capture; - + Win32Capture captures_win32[2] = {0}; for EachElement(i, captures_win32) { // create read pipe @@ -488,23 +463,23 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) SECURITY_ATTRIBUTES read_at = { .nLength = sizeof(read_at), .bInheritHandle = 0 }; captures_win32[i].read_pipe_handle = CreateNamedPipeW(pipe_name16.str, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, MB(1), MB(1), 0, &read_at); AssertAlways(captures_win32[i].read_pipe_handle != INVALID_HANDLE_VALUE); - + // create overlapped write file SECURITY_ATTRIBUTES write_at = { .nLength = sizeof(write_at), .bInheritHandle = 1 }; captures_win32[i].write_pipe_handle = CreateFileW(pipe_name16.str, GENERIC_WRITE, 0, &write_at, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); AssertAlways(captures_win32[i].write_pipe_handle != INVALID_HANDLE_VALUE); - + // create event for overlapped captures_win32[i].event = CreateEventW(0, 1, 0, 0); AssertAlways(captures_win32[i].event != NULL); - + // alloc capture buffer captures_win32[i].buffer_size = MB(1); captures_win32[i].buffer = push_array(scratch.arena, U8, captures_win32[i].buffer_size); } captures_win32[stdout_idx].parts = &stdout_parts; captures_win32[stderr_idx].parts = &stderr_parts; - + File read_capture_handles [ArrayCount(captures_win32)] = {0}; File write_capture_handles[ArrayCount(captures_win32)] = {0}; for EachElement(i, captures_win32) { read_capture_handles[i] = (File){ (U64)captures_win32[i].read_pipe_handle }; } @@ -516,7 +491,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) B32 is_live; struct pollfd *poll_fd; } LinuxCapture; - + LinuxCapture captures_linux[2] = {0}; for EachElement(i, captures_linux) { if (pipe2(captures_linux[i].fds, 0) != 0) { @@ -525,10 +500,10 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) } captures_linux[i].is_live = 1; } - + captures_linux[0].parts = &stdout_parts; captures_linux[1].parts = &stderr_parts; - + File read_capture_handles[2] = {0}, write_capture_handles[2] = {0}; read_capture_handles[0].u64[0] = captures_linux[0].fds[0]; read_capture_handles[1].u64[0] = captures_linux[1].fds[0]; @@ -546,13 +521,13 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) .cmd_line = lnk_arg_list_parse_windows_rules(scratch.arena, cmdline), }; str8_list_push_front(scratch.arena, &launch_opts.cmd_line, exe_path); - + String8 full_cmd_line = str8_list_join(scratch.arena, &launch_opts.cmd_line, &(StringJoin){ .sep = str8_lit(" ") }); - + // invoke exe Process process_handle = process_launch(&launch_opts); if (process_match(process_handle, process_zero())) { goto exit; } - + // capture process output #if OS_WINDOWS { @@ -561,24 +536,24 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) CloseHandle(captures_win32[i].write_pipe_handle); MemoryZeroStruct(&write_capture_handles[i]); } - + B32 is_process_live = 1; for (U64 endt_us = ENDT_US(timeout_us);;) { HANDLE wait_handles[ArrayCount(captures_win32) + 1] = {0}; U64 wait_handle_count = 0; - + // queue process if (is_process_live) { wait_handles[wait_handle_count++] = (HANDLE)process_handle.u64[0]; } - + for EachElement(capture_idx, captures_win32) { while (captures_win32[capture_idx].state == Win32CaptureState_Null) { // init overlapped so when child writes to capture buffer this event is signaled AssertAlways(ResetEvent(captures_win32[capture_idx].event)); MemoryZeroStruct(&captures_win32[capture_idx].overlapped); captures_win32[capture_idx].overlapped.hEvent = captures_win32[capture_idx].event; - + // begin overlapped read DWORD read_size = 0; if (ReadFile(captures_win32[capture_idx].read_pipe_handle, captures_win32[capture_idx].buffer, captures_win32[capture_idx].buffer_size, &read_size, &captures_win32[capture_idx].overlapped)) { @@ -600,7 +575,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) break; } } - + if (captures_win32[capture_idx].state == Win32CaptureState_Pending) { // event now should signal whenever pipe has data to read captures_win32[capture_idx].wait_idx = wait_handle_count++; @@ -609,23 +584,23 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) captures_win32[capture_idx].wait_idx = max_U64; } } - + // exit if there are no handles if (wait_handle_count == 0) { break; } - + // compute wait time DWORD wait_ms = INFINITE; if (timeout_us != max_U64) { U64 now_us = now_time_us(); wait_ms = now_us < endt_us ? ClampTop((endt_us - now_us + 999) / 1000, max_U32-1) : 0; } - + // wait on process and read pipes DWORD wait_result = WaitForMultipleObjects(wait_handle_count, wait_handles, 0, wait_ms); - + if (wait_result >= WAIT_OBJECT_0 && wait_result < WAIT_OBJECT_0 + wait_handle_count) { DWORD wait_idx = wait_result - WAIT_OBJECT_0; - + if (is_process_live && wait_idx == 0) { DWORD exit_code = 0; if(GetExitCodeProcess((HANDLE)process_handle.u64[0], &exit_code)) { @@ -641,7 +616,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) } } AssertAlways(pipe_idx != max_U64); - + DWORD read_size; if (GetOverlappedResult(captures_win32[pipe_idx].read_pipe_handle, &captures_win32[pipe_idx].overlapped, &read_size, 0)) { if (read_size > 0) { @@ -649,7 +624,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) String8 string = str8(captures_win32[pipe_idx].buffer, read_size); String8 string_copy = str8_copy(scratch.arena, string); str8_list_push(scratch.arena, captures_win32[pipe_idx].parts, string_copy); - + // queue next overlapped read captures_win32[pipe_idx].state = Win32CaptureState_Null; } else { @@ -664,7 +639,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) break; } } - + // (timeout) kill process if alive so we can safeley cancel async IO if (is_process_live) { if (TerminateProcess((HANDLE)process_handle.u64[0], 999)) { @@ -672,20 +647,20 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) Assert(0 && "process is taking too long to exit"); } } else { Assert(0 && "failed to kill process"); } - + DWORD exit_code = 0; if (GetExitCodeProcess((HANDLE)process_handle.u64[0], &exit_code)) { g_last_exit_code = exit_code; } else { Assert(0 && "failed to get process exit code"); } } - + // (timeout) cancel pending async IO for EachElement(i, captures_win32) { if (captures_win32[i].state == Win32CaptureState_Pending) { BOOL cancel_ok = CancelIoEx(captures_win32[i].read_pipe_handle, &captures_win32[i].overlapped); DWORD cancel_error = cancel_ok ? ERROR_SUCCESS : GetLastError(); AssertAlways(cancel_ok || cancel_error == ERROR_NOT_FOUND); - + DWORD read_size = 0; if (GetOverlappedResult(captures_win32[i].read_pipe_handle, &captures_win32[i].overlapped, &read_size, 1)) { if (read_size > 0) { @@ -700,7 +675,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) captures_win32[i].state = Win32CaptureState_EOF; } } - + // close windows specific handles CloseHandle((HANDLE)process_handle.u64[0]); for EachElement(i, captures_win32) { @@ -713,14 +688,14 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) close((int)write_capture_handles[i].u64[0]); MemoryZeroStruct(&write_capture_handles[i]); } - + pid_t pid = (pid_t)process_handle.u64[0]; int pidfd = syscall(SYS_pidfd_open, pid, 0); if (pidfd < 0) { fprintf(stderr, "ERROR: failed to translate pid(%d) to pidfd\n", pid); goto exit; } - + B32 is_process_live = 1; U64 endt_us = ENDT_US(timeout_us); U64 read_buffer_default_size = MB(1); @@ -729,12 +704,12 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) for (;;) { struct pollfd fds[3] = {0}; int nfds = 0; - + // append process if (is_process_live) { fds[nfds++] = (struct pollfd){ .fd = pidfd, .events = POLLIN | POLLHUP }; } - + // append pipes for EachElement(i, captures_linux) { if (captures_linux[i].is_live) { @@ -742,17 +717,17 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) fds[nfds++] = (struct pollfd){ .fd = captures_linux[i].fds[0], .events = POLLIN | POLLHUP }; } } - + // exit if there are no more handles to poll if (nfds == 0) { break; } - + // compute wait time int wait_ms = -1; if (timeout_us != max_U64) { U64 now_us = now_time_us(); wait_ms = now_us < endt_us ? ClampTop((endt_us - now_us + 999) / 1000, max_U32-1) : 0; } - + // wait for kernel to signal any of the wait handles int poll_result = poll(fds, nfds, wait_ms); if (poll_result < 0) { @@ -773,12 +748,12 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) } else { fprintf(stderr, "ERROR: failed to reap process %d\n", pid); } - + // signal on process fd means exit is_process_live = 0; } } - + for EachElement(i, captures_linux) { if (captures_linux[i].is_live) { if (captures_linux[i].poll_fd->revents & POLLIN) { @@ -787,7 +762,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) read_buffer_size = read_buffer_default_size; read_buffer = push_array(scratch.arena, U8, read_buffer_default_size); } - + ssize_t read_size = LNX_RETRY_ON_EINTR(read(captures_linux[i].poll_fd->fd, read_buffer, read_buffer_size)); if (read_size > 0) { str8_list_push(scratch.arena, captures_linux[i].parts, str8(read_buffer, read_size)); @@ -807,40 +782,40 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us) } } } - + if (captures_linux[i].poll_fd->revents & POLLHUP) { captures_linux[i].is_live = 0; } } } } - + // close process handle if (close(pidfd) < 0) { fprintf(stderr, "ERROR: failed to close process handle %d\n", pidfd); } #endif - + t_infof("Invoke: {\n"); t_infof(" CMDL: %S\n", full_cmd_line); t_infof(" WDIR: %S\n", g_wdir); t_infof(" Exit: %u\n", g_last_exit_code); t_infof("}\n"); - + // update output global g_output = str8_list_join(g_output_arena, &stdout_parts, 0); g_errors = str8_list_join(g_output_arena, &stderr_parts, 0); - + // write to the output file if (g_redirect_stdout) { write_data_to_file_path(g_stdout_file_name, g_output); } - + is_ok = 1; // process was launched (does not mean exited successfully) exit:; for EachElement(i, read_capture_handles) { file_close(read_capture_handles[i]); } for EachElement(i, write_capture_handles) { file_close(write_capture_handles[i]); } - + scratch_end(scratch); return is_ok; } @@ -1104,6 +1079,8 @@ t_help(void) fprintf(stderr, " torture +* Force-run all tests\n"); } +internal void t_dbg_register_script_tests(Arena *arena, String8 folder_path); + internal void t_entry_point(CmdLine *cmdline) { @@ -1127,25 +1104,25 @@ t_entry_point(CmdLine *cmdline) // { B32 print_help = cmd_line_has_flag(cmdline, str8_lit("help")) || - cmd_line_has_flag(cmdline, str8_lit("h")); + cmd_line_has_flag(cmdline, str8_lit("h")); if (print_help) { t_help(); goto exit; } } - + // Gather tests { // register debugger tests String8 test_folder_path = str8f(scratch.arena, "%S/torture/dbg_tests", t_src_path()); t_dbg_register_script_tests(scratch.arena, test_folder_path); - + // sort tests g_torture_tests = push_array(scratch.arena, T_Test *, g_torture_test_count); for EachIndex(i, g_torture_test_count) { g_torture_tests[i] = &g_torture_tests_[i]; } radsort(g_torture_tests, g_torture_test_count, t_test_ptr_is_before); } - + // // Handle -list // @@ -1167,7 +1144,7 @@ t_entry_point(CmdLine *cmdline) g_clang_path = cmd_line_string(cmdline, str8_lit("clang")); g_gcc_path = cmd_line_string(cmdline, str8_lit("gcc")); g_linker_path = cmd_line_string(cmdline, str8_lit("linker")); - + // // Handle -test_data // @@ -1185,63 +1162,63 @@ t_entry_point(CmdLine *cmdline) U64List targets = {0}; { String8List inputs = {0}; - + CmdLineOpt *target_opt = 0; if (target_opt == 0) { target_opt = cmd_line_opt_from_string(cmdline, str8_lit("target")); } if (target_opt == 0) { target_opt = cmd_line_opt_from_string(cmdline, str8_lit("t")); } - + // handle explicit target switch if (target_opt) { str8_list_concat_in_place(&inputs, &target_opt->value_strings); } - + // accept inputs from the command line as target tests to run str8_list_concat_in_place(&inputs, &cmdline->inputs); - + // no inputs -> print help and exit if (inputs.node_count == 0) { t_help(); goto exit; } - + HashMap hm = {0}; for EachNode(input_n, String8Node, inputs.first) { String8 t = input_n->string; - + // parse mode typedef enum { Mode_Default, Mode_Skip, Mode_Force, } Mode; Mode mode = Mode_Default; if (str8_match_wildcard(t, str8_lit("+*"), 0)) { mode = Mode_Force; t = str8_skip(t, 1); } else if (str8_match_wildcard(t, str8_lit("!*"), 0)) { mode = Mode_Skip; t = str8_skip(t, 1); } - + if (str8_find_needle(t, 0, str8_lit("/"), 0) >= t.size) { t = str8f(scratch.arena, "*/%S", t); } - + U64 match_count = 0; - + for EachIndex(test_idx, g_torture_test_count) { // match test names String8 test_name = t_test_name_from_idx(scratch.arena, test_idx); - + if (str8_match_wildcard(test_name, t, StringMatchFlag_CaseInsensitive)) { // set skip flag switch (mode) { - case Mode_Default: break; - case Mode_Skip: g_torture_tests[test_idx]->skip = 1; break; - case Mode_Force: g_torture_tests[test_idx]->skip = 0; break; + case Mode_Default: break; + case Mode_Skip: g_torture_tests[test_idx]->skip = 1; break; + case Mode_Force: g_torture_tests[test_idx]->skip = 0; break; } - + // append test when not in skipping mode if ( ! hash_map_search_string_u64(&hm, test_name)) { hash_map_push_string_u64(scratch.arena, &hm, test_name, 1); u64_list_push(scratch.arena, &targets, test_idx); } - + match_count += 1; } } - + if (match_count == 0) { fprintf(stderr, "WARNING: no matches found for input: %.*s\n", str8_varg(input_n->string)); } @@ -1253,13 +1230,13 @@ t_entry_point(CmdLine *cmdline) g_stop_on_first_fail_or_crash = !cmd_line_has_flag(cmdline, str8_lit("keep_going")); g_build_only = cmd_line_has_flag(cmdline, str8_lit("build_only")); g_output_arena = arena_alloc(); - + // default options when running under debugger #if OS_WINDOWS if (!cmd_line_has_flag(cmdline, str8_lit("print_stdout")) && IsDebuggerPresent()) { g_redirect_stdout = 0; } - + // automatically close child processes on exit { HANDLE job_handle = CreateJobObjectA(0, 0); @@ -1310,19 +1287,19 @@ t_entry_point(CmdLine *cmdline) max_group_size = Max(max_group_size, t_group_from_test_idx(test_idx).size); } - U64 run_counters[T_RunStatus_Count] = {0}; + U64 run_counters[TestStatus_COUNT] = {0}; U64 max_digit_count = count_digits_u64(target_indices.count, 10); U64 total_time_start = now_time_us(); - + typedef struct { U64 target_idx, d; } Slowest; Slowest slowest[5] = {0}; for EachElement(i, slowest) { slowest[i].target_idx = max_U64; } - + U64List skipped_tests = {0}; - + for EachIndex(i, target_indices.count) { if (i == 0) { PrintHeader("Tests"); } - + U64 target_idx = target_indices.v[i]; T_Test *test = g_torture_tests[target_idx]; @@ -1335,11 +1312,11 @@ t_entry_point(CmdLine *cmdline) fprintf(stdout, "%.*s %.*s/ %s", str8_varg(t_group_from_test(test)), (int)(max_group_size - t_group_from_test(test).size), spaces, test->label); fprintf(stdout, " %.*s ", (int)dots_count, dots); fflush(stdout); - + // setup output directory g_wdir = push_str8f(scratch.arena, "%S/%s", g_out, test->label); g_wdir = full_path_from_path(scratch.arena, g_wdir); - + // delete files from last run in the work directory if (folder_path_exists(g_wdir)) { t_delete_dir(g_wdir); @@ -1353,21 +1330,32 @@ t_entry_point(CmdLine *cmdline) // run test U64 run_start_time = now_time_us(); - T_RunResult result = t_run(test, test->user_data); + TestResult result = t_run(test, test->user_data); U64 run_end_time = now_time_us(); - + // update run_counters[result.status] += 1; - - // print run status - fprintf(stdout, "%s%s" T_RESET, t_color_from_result(result.status), t_string_from_result(result.status)); - if (result.status == T_RunStatus_Pass) { + // rjf: map status -> color / string + char *status_name_cstr = 0; + char *color_cstr = 0; + switch(result.status) + { + default: + case TestStatus_Pass: {status_name_cstr = "PASS"; color_cstr = T_GREEN;}break; + case TestStatus_Fail: {status_name_cstr = "FAIL"; color_cstr = T_RED;}break; + case TestStatus_Crash:{status_name_cstr = "CRASH"; color_cstr = T_RED;}break; + } + + // print run status + fprintf(stdout, "%s%s" T_RESET, color_cstr, status_name_cstr); + + if (result.status == TestStatus_Pass) { U64 d = run_end_time - run_start_time; DateTime t = date_time_from_micro_seconds(d); String8 s = string_from_elapsed_time(scratch.arena, t); fprintf(stdout, " %.*s", str8_varg(s)); - + fflush(stdout); U64 insert_idx = max_U64; @@ -1387,15 +1375,15 @@ t_entry_point(CmdLine *cmdline) } fprintf(stdout, "\n"); - if (result.status == T_RunStatus_Fail) { + if (result.status == TestStatus_Fail) { fprintf(stdout, " ERROR: %s:%d: condition: \"%s\"\n", result.fail_file, result.fail_line, result.fail_cond); } - if (result.status == T_RunStatus_Fail || result.status == T_RunStatus_Crash) { + if (result.status == TestStatus_Fail || result.status == TestStatus_Crash) { if (g_stop_on_first_fail_or_crash) { goto exit; } } - - if (result.status == T_RunStatus_Skip) { + + if (result.status == TestStatus_Skip) { u64_list_push(scratch.arena, &skipped_tests, target_idx); } } @@ -1404,22 +1392,22 @@ t_entry_point(CmdLine *cmdline) if (target_indices.count > 0 && sum_array_u64(ArrayCount(run_counters), run_counters) > 0) { U64 total_time_dt = total_time_end - total_time_start; String8 total_time_str = string_from_elapsed_time(scratch.arena, date_time_from_micro_seconds(total_time_dt)); - + fprintf(stderr, "\n"); PrintHeader("Summary"); - fprintf(stderr, " Passed %llu\n", (unsigned long long)run_counters[T_RunStatus_Pass]); - fprintf(stderr, " Failed %llu\n", (unsigned long long)run_counters[T_RunStatus_Fail]); - fprintf(stderr, " Crashed %llu\n", (unsigned long long)run_counters[T_RunStatus_Crash]); - fprintf(stderr, " Skipped %llu\n", (unsigned long long)run_counters[T_RunStatus_Skip]); + fprintf(stderr, " Passed %llu\n", (unsigned long long)run_counters[TestStatus_Pass]); + fprintf(stderr, " Failed %llu\n", (unsigned long long)run_counters[TestStatus_Fail]); + fprintf(stderr, " Crashed %llu\n", (unsigned long long)run_counters[TestStatus_Crash]); + fprintf(stderr, " Skipped %llu\n", (unsigned long long)run_counters[TestStatus_Skip]); fprintf(stderr, " Time %.*s\n", str8_varg(total_time_str)); - + U64 slow_count = 0; for EachElement(i, slowest) { Slowest s = slowest[i]; if (s.target_idx >= g_torture_test_count) { break; } slow_count += 1; } - + if (slow_count > 3) { U64 label_max = 0; U64 group_max = 0; @@ -1428,7 +1416,7 @@ t_entry_point(CmdLine *cmdline) label_max = Max(strlen(g_torture_tests[s.target_idx]->label), label_max); group_max = Max(t_group_from_test_idx(s.target_idx).size, group_max); } - + fprintf(stderr, " \nSlow Tests\n"); for EachElement(i, slowest) { Slowest s = slowest[i]; @@ -1444,10 +1432,10 @@ t_entry_point(CmdLine *cmdline) } } - exit_code = run_counters[T_RunStatus_Fail] + run_counters[T_RunStatus_Crash]; + exit_code = run_counters[TestStatus_Fail] + run_counters[TestStatus_Crash]; exit:; } - + scratch_end(scratch); exit(exit_code); } diff --git a/src/torture/torture.h b/src/torture/torture.h index fff10f90..a2ca2d69 100644 --- a/src/torture/torture.h +++ b/src/torture/torture.h @@ -11,31 +11,8 @@ #define T_YELLOW "\x1b[33m" #define T_BLUE "\x1b[34m" -//#define X(n, c) -#define T_Run_XList \ - X(Fail, T_RED) \ - X(Crash, T_RED) \ - X(Pass, T_GREEN) \ - X(Skip, T_RESET) - -typedef enum -{ -#define X(n,...) T_RunStatus_##n, -T_Run_XList -#undef X - T_RunStatus_Count -} T_RunStatus; - -typedef struct -{ - T_RunStatus status; - char *fail_file; - int fail_line; - char *fail_cond; -} T_RunResult; - -#define T_RunSig(name) void t_##name(Arena *arena, String8 user_data, T_RunResult *result_out, String8List *test_out) -typedef void (*T_Run)(Arena *arena, String8 user_data, T_RunResult *result_out, String8List *test_out); +#define T_RunSig(name) void t_##name(Arena *arena, String8 user_data, TestResult *result_out, String8List *test_out) +typedef void (*T_Run)(Arena *arena, String8 user_data, TestResult *result_out, String8List *test_out); typedef struct { @@ -50,26 +27,10 @@ typedef struct typedef struct { T_Test *test; - String8 user_data; - T_RunResult result; + String8 user_data; + TestResult result; } T_RunCtx; -typedef enum -{ - T_Compiler_Null, - T_Compiler_Cl, - T_Compiler_Clang, - T_Compiler_Gcc, -} T_Compiler; - -typedef enum -{ - T_Linker_Null, - T_Linker_RAD, - T_Linker_MSVC, - T_Linker_LLVM -} T_Linker; - extern U64 g_torture_test_count; extern T_Test **g_torture_tests; extern T_Test g_torture_tests_[0xffffff]; @@ -77,34 +38,30 @@ extern T_Test g_torture_tests_[0xffffff]; internal void t_break_if_debugger_present(void); #define T_AddTest(name, f, l, skip, ...) \ - T_RunSig(name); \ - __VA_ARGS__ void t_add_test_##name(void) \ - { \ - g_torture_tests_[g_torture_test_count++] = (T_Test){ f, Stringify(name), l, &t_##name, skip }; \ - } +T_RunSig(name); \ +__VA_ARGS__ void t_add_test_##name(void) \ +{ \ +g_torture_tests_[g_torture_test_count++] = (T_Test){ f, Stringify(name), l, &t_##name, skip }; \ +} #if COMPILER_MSVC # pragma section(".CRT$XCU", read) # define TEST_(name, skip) \ - T_AddTest(name, __FILE__, __LINE__, skip) \ - __declspec(allocate(".CRT$XCU")) void(*r_##name)(void) = t_add_test_##name; \ - __pragma(comment(linker, "/include:" Stringify(r_##name))) +T_AddTest(name, __FILE__, __LINE__, skip) \ +__declspec(allocate(".CRT$XCU")) void(*r_##name)(void) = t_add_test_##name; \ +__pragma(comment(linker, "/include:" Stringify(r_##name))) #else # define TEST_(name, skip) T_AddTest(name, __FILE__, __LINE__, skip, __attribute__((constructor))) #endif #define TEST(name) \ - TEST_(name, 0) \ - T_RunSig(name) +TEST_(name, 0) \ +T_RunSig(name) #define SKIP(name) \ - TEST_(name, 1) \ - T_RunSig(name) +TEST_(name, 1) \ +T_RunSig(name) -#define T_Ok(c) do { if (!(c)) { \ - *result_out = (T_RunResult){ .fail_file = __FILE__, .fail_line = __LINE__, .fail_cond = Stringify(c) }; \ - t_break_if_debugger_present(); \ - return; \ -} } while(0) +#define T_Ok(c) TestCheck(c) #define T_MatchLinef(out, ...) T_Ok(t_match_linef(out, __VA_ARGS__)) #define t_outf(...) str8_list_pushf(arena, test_out, ## __VA_ARGS__) @@ -129,9 +86,9 @@ internal B32 t_delete_file(String8 name); internal String8 t_make_file_path(Arena *arena, String8 name); // test runner -internal void t_run_caller(void *raw_ctx); -internal void t_run_fail_handler(void *raw_ctx); -internal T_RunResult t_run(T_Test *test, String8 user_data); +internal void t_run_caller(void *raw_ctx); +internal void t_run_fail_handler(void *raw_ctx); +internal TestResult t_run(T_Test *test, String8 user_data); // tools internal String8 t_radbin_path(void); diff --git a/src/torture/torture_dbg.h b/src/torture/torture_dbg.h deleted file mode 100644 index c1aac25a..00000000 --- a/src/torture/torture_dbg.h +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -//////////////////////////////// -// IPC Controller - -typedef struct -{ - B32 running; - U64 run_gen; - U64 ip; - Arch arch; - U64 vaddr_min; - U64 vaddr_max; - U64 ip_vaddr; - U64 sp_base; - U64 tls_root; - U64 tls_index; - U64 tls_offset; - U64 timestamp; - U64 exception_code; - U64 bp_flags; - String8 string; - OperatingSystem target_os; - U64 tls_model; - String8 stop_cause; -} T_DbgState; - -typedef struct -{ - String8 file_path; - TxtPt pt; - Rng1U64 voff_range; -} T_DbgLine; - -typedef struct -{ - U64 count; - T_DbgLine *v; -} T_DbgLineArray; - -typedef struct -{ - String8 expr; - String8 value; - String8 type; - String8 error; -} T_Eval; - -//////////////////////////////// -// Dbg Script - -#define T_DbgScriptCmdKind_XList \ - X(Breakpoint, "bp") \ - X(ClearBreakpoints, "bp_clear") \ - X(Halt, "halt") \ - X(Run, "run") \ - X(RunToLine, "run_to_line") \ - X(StepOver, "step_over") \ - X(StepInto, "step_into") \ - X(StepOut, "step_out") \ - X(StepOverInst, "step_over_inst") \ - X(StepIntoInst, "step_over_inst") \ - X(StepOverLine, "step_over_line") \ - X(StepIntoLine, "step_into_line") \ - X(KillAll, "kll_all") \ - X(At, "at") \ - X(Eval, "eval") - -typedef enum -{ - T_DbgScriptCmdKind_Null, -#define X(n,...) T_DbgScriptCmdKind_##n, - T_DbgScriptCmdKind_XList -#undef X - T_DbgScriptCmdKind_Count -} T_DbgScriptCmdKind; - -#define T_DbgScriptDirectiveKind_XList \ - X(Compile, "compile") \ - X(Link, "link") \ - X(Launch, "launch") \ - X(Skip, "skip") - -typedef enum -{ - T_DbgScriptDirectiveKind_Null, -#define X(q,w) T_DbgScriptDirectiveKind_##q, - T_DbgScriptDirectiveKind_XList -#undef X - T_DbgScriptDirectiveKind_Count, -} T_DbgScriptDirectiveKind; - -typedef struct T_DbgScriptFile -{ - struct T_DbgScriptFile *next; - String8 path; - String8 source; - TxtPt pt; -} T_DbgScriptFile; - -typedef struct -{ - U64 count; - T_DbgScriptFile *first; - T_DbgScriptFile *last; -} T_DbgScriptFileList; - -typedef struct T_DbgScriptCmd -{ - struct T_DbgScriptCmd *next; - T_DbgScriptCmdKind kind; - TxtPt pt; - S64 at_delta; -} T_DbgScriptCmd; - -typedef struct T_DbgScriptProgram -{ - TxtPt pt; - U64 order; - OperatingSystem os; - T_DbgScriptFile *file; - U64 count; - T_DbgScriptCmd *first; - T_DbgScriptCmd *last; - struct T_DbgScriptProgram *next; -} T_DbgScriptProgram; - -typedef struct -{ - U64 count; - T_DbgScriptProgram *first; - T_DbgScriptProgram *last; -} T_DbgScriptProgramList; - -typedef struct T_DbgScriptDirective -{ - struct T_DbgScriptDirective *next; - T_DbgScriptDirectiveKind kind; - TxtPt pt; - String8 args; - union { - struct { - T_Compiler cc; - } compile; - }; -} T_DbgScriptDirective; - -typedef struct -{ - U64 count; - T_DbgScriptDirective *first; - T_DbgScriptDirective *last; -} T_DbgScriptDirectiveList; - -typedef struct -{ - String8 file_path; - T_DbgScriptDirectiveList directives[OperatingSystem_COUNT][T_DbgScriptDirectiveKind_Count]; - T_DbgScriptFileList files; - U64 program_count; - T_DbgScriptProgram **programs; - B32 skip; -} T_DbgScript; - - -//////////////////////////////// -// IPC Controller - -// error helper -internal void t_errorf_md(String8 file_name, String8 source, MD_Node *n, char *fmt, ...); - -// reply parse helpers -internal B32 t_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out); -internal B32 t_ipc_parse_u32(MD_Node *node, String8 child_name, U32 *out); -internal B32 t_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out); -internal B32 t_ipc_parse_b32(MD_Node *node, String8 child_name, B32 *out); -#define t_ipc_parse_int(n, c, ptr) t_ipc_parse_int_(n, c, sizeof(*ptr), ptr) - -// debugger commands -internal B32 t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, MD_ParseResult *reply_out); -internal B32 t_dbg_send_cmdf(U64 timeout_us, Arena *reply_arena, MD_ParseResult *reply_out, char *fmt, ...); -internal T_DbgState * t_dbg_state(Arena *arena, U64 timeout_us); -internal B32 t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgLineArray *lines_out, U64 timeout_us); -internal String8 t_dbg_value_from_expr(Arena *arena, String8 expr); -internal String8 t_dbg_value_from_exprf(Arena *arena, char *fmt, ...); -internal B32 t_dbg_send_cmd_and_wait_stop(String8 cmd, U64 timeout_us); -internal B32 t_dbg_ping(U64 timeout_us); -internal B32 t_dbg_bp_add_line(String8 file, U64 line); -internal B32 t_dbg_bp_add_func(String8 func_name); -internal B32 t_dbg_bp_add_addr(U64 addr); -internal B32 t_dbg_launch(String8 cmdline, U64 timeout_us); -internal B32 t_dbg_eval(Arena *arena, String8 expr, T_Eval *eval_out); - -//////////////////////////////// -// Dbg Script - -internal String8 t_string_from_dbg_script_cmd_kind(T_DbgScriptCmdKind v); -internal T_DbgScriptCmdKind t_dbg_script_cmd_kind_from_string(String8 cmd); -internal B32 t_dbg_parse_script(Arena *arena, String8 file_path, String8 source, T_DbgScript *script_out); -internal B32 t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us); -internal void t_dbg_register_script_tests(Arena *arena, String8 folder_path); - diff --git a/src/torture/torture_main.c b/src/torture/torture_main.c index 6745399c..b240b3ae 100644 --- a/src/torture/torture_main.c +++ b/src/torture/torture_main.c @@ -6,6 +6,7 @@ #define BUILD_TITLE "TORTURE" +#define BUILD_TESTS 1 #define BUILD_CONSOLE_INTERFACE 1 #define OS_FEATURE_GRAPHICAL 1 #define DMN_INIT_MANUAL 1 @@ -94,8 +95,6 @@ #include "linker/lnk_log.h" #include "linker/lnk_debug_helper.h" #include "torture.h" -#include "torture_radlink.h" -#include "torture_dbg.h" #include "base/base_inc.c" #include "x64/x64.c" @@ -167,15 +166,15 @@ #include "linker/pdb_ext/pdb_helpers.c" #include "linker/pdb_ext/pdb_builder.c" #include "linker/lnk_debug_helper.c" - #include "torture.c" -#include "torture_base.c" -#include "torture_md.c" -#include "torture_radlink.c" -#include "torture_dwarf.c" -#include "torture_d2r.c" -#include "torture_p2r.c" -#include "torture_dbg.c" + +#include "base/tests/base_tests.c" +#include "mdesk/tests/mdesk_tests.c" +#include "linker/tests/linker_tests.c" +#include "dwarf/tests/dwarf_tests.c" +#include "rdi_from_dwarf/tests/rdi_from_dwarf_tests.c" +#include "rdi_from_pdb/tests/rdi_from_pdb_tests.c" +#include "raddbg/tests/raddbg_tests.c" internal B32 frame(void) { return 0; } diff --git a/src/torture/torture_radlink.h b/src/torture/torture_radlink.h deleted file mode 100644 index 17ce34d9..00000000 --- a/src/torture/torture_radlink.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#pragma once - -//////////////////////////////// - -typedef enum -{ - T_MsvcLinkExitCode_UnresolvedExternals = 1120, - T_MsvcLinkExitCode_CorruptOrInvalidSymbolTable = 1235, - T_MsvcLinkExitCode_SectionsFoundWithDifferentAttributes = 4078, -} T_MsvcLinkExitCode; - -//////////////////////////////// - -internal T_Linker t_id_linker(void); - -internal COFF_ObjSection * t_push_text_section(COFF_ObjWriter *obj_writer, String8 data); -internal COFF_ObjSection * t_push_data_section(COFF_ObjWriter *obj_writer, String8 data); -internal COFF_ObjSection * t_push_rdata_section(COFF_ObjWriter *obj_writer, String8 data); - -internal String8 t_make_entry_obj(Arena *arena); -internal String8 t_make_sec_defn_obj(Arena *arena, String8 payload); -internal String8 t_make_obj_with_directive(Arena *arena, String8 directive); - -internal B32 t_write_entry_obj(void); - diff --git a/src/win32/base/win32_base.c b/src/win32/base/win32_base.c index 4e6add41..a7bf6c0f 100644 --- a/src/win32/base/win32_base.c +++ b/src/win32/base/win32_base.c @@ -156,6 +156,16 @@ w32_thread_entry_point(void *ptr) return 0; } +//////////////////////////////// +//~ rjf: @per_os_impl Debugger Attachment Checking + +internal B32 +debugger_is_attached(void) +{ + B32 result = IsDebuggerPresent(); + return result; +} + //////////////////////////////// //~ rjf: @per_os_impl Platform Time Functions