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

This commit is contained in:
Ryan Fleury committed 2026-06-02 12:11:01 -07:00
1 parent a151df283f
commit 4e72553006
29 files changed
+1090 -1045

No files matched your search

@@ -1,59 +0,0 @@
/// test: {
/// windows: {
/// compile: "/Od /Z7 /c main.c /Fo:main.obj"
/// compile: "/Od /Z7 /c foo.c /Fo:foo.obj"
/// link: "/fixed /debug:full /dll foo.obj /out:foo.dll /implib:foo.lib"
/// link: "/fixed /debug:full main.obj foo.lib /out:main.exe"
/// launch: "main.exe"
/// }
/// linux: {
/// compile: "-O0 -g main.c foo.c -o main"
/// launch: "./main"
/// }
/// }
///
// TODO: linux: -no-pie crashes the tracer
//
// TODO: crashes
// compile: "-O0 -g foo.c -fPIC -shared -o libfoo.so"
// compile: "-O0 -g main.c -L. -lfoo -o main -Wl,-rpath,%CWD%"
// ...
// dw_compute_cfa dwarf_unwind.c:261:5
// d_establish_frame_unwind_context__dwarf dbg_engine_ctrl.c:1946:25
// d_unwind_from_thread dbg_engine_ctrl.c:2913:30
// ...
/// file: "foo.c"
#if defined(_WIN32)
__declspec(dllexport)
#endif
int foo(int a)
{ /// 6: at
/// 7: step_over
return a + 1; /// 8: at
/// 9: step_out
}
/// file: "main.c"
#include <stdio.h>
#if defined(_WIN32)
__declspec(dllimport)
#endif
int foo();
int main() /// 1: step_into
{ /// 2: at
/// 3: step_over
int x = foo(123); /// 4: at
/// 5: step_into
/// 10: at: -2
/// 11: step_over
printf("Hello, world!\n"); /// 12: at
/// 13: step_over
/// 14: run
}
-246
View File
@@ -1,246 +0,0 @@
/// test: {
/// linux: {
/// skip: ""
/// compile: "-O0 -g sysv_module.c -fPIC -shared -Wl,--hash-style=sysv -o libsysv_module.so"
/// compile: "-O0 -g gnu_module.c -fPIC -shared -Wl,--hash-style=gnu -o libgnu_module.so"
/// compile: "-O0 -g main.c -L. -lsysv_module -lgnu_module -o main -Wl,-rpath,%CWD%,-z,now -fno-plt"
/// launch: "./main"
/// }
/// }
/// file: "sysv_module.c"
__attribute__((visibility("default"))) int sysv_default_object = 10;
__attribute__((visibility("protected"))) int sysv_protected_object = 11;
__attribute__((visibility("hidden"))) int sysv_hidden_object = 12;
__attribute__((visibility("internal"))) int sysv_internal_object = 13;
__attribute__((weak, visibility("default"))) int sysv_weak_object = 14;
__thread __attribute__((visibility("default"))) int sysv_tls_object = 15;
__thread __attribute__((visibility("protected"))) int sysv_protected_tls_object = 16;
__thread __attribute__((weak, visibility("default"))) int sysv_weak_tls_object = 17;
static __thread int sysv_static_tls_object = 18;
static int sysv_static_object = 19;
/*
extern long write(int, const void *, unsigned long);
extern char sysv_notype_symbol;
extern char sysv_hidden_notype_symbol;
extern char sysv_internal_notype_symbol;
__asm__(".globl sysv_notype_symbol\n"
"sysv_notype_symbol:\n"
" .byte 0\n"
".globl sysv_hidden_notype_symbol\n"
".hidden sysv_hidden_notype_symbol\n"
"sysv_hidden_notype_symbol:\n"
" .byte 0\n"
".globl sysv_internal_notype_symbol\n"
".internal sysv_internal_notype_symbol\n"
"sysv_internal_notype_symbol:\n"
" .byte 0\n");
*/
__attribute__((visibility("default"))) int
sysv_default_func(int x)
{
return x + 101; /// 1: { run_to_line at }
}
__attribute__((weak, visibility("default"))) int
sysv_weak_func(int x)
{
return x + 102; /// 2: { run_to_line at }
}
__attribute__((visibility("protected"))) int
sysv_protected_func(int x)
{
return x + 103; /// 3: { run_to_line at }
}
__attribute__((visibility("internal"))) int
sysv_internal_func(int x)
{
return x + 104; /// 4: { run_to_line at }
}
static int
sysv_ifunc_impl(int x)
{
return x + 105; /// 5: { run_to_line at }
}
static void *
sysv_ifunc_resolver(void)
{
return sysv_ifunc_impl;
}
__attribute__((ifunc("sysv_ifunc_resolver"), visibility("default"))) int sysv_ifunc_func(int x);
__attribute__((visibility("hidden"))) int
sysv_hidden_func(int x)
{
return x + sysv_hidden_object; /// 6: { run_to_line at }
}
static int
sysv_static_func(int x)
{
return x + sysv_static_object; /// 7: { run_to_line at }
}
__attribute__((visibility("default"))) int
sysv_run_all(int x)
{
int result = 0;
result += sysv_default_func(x);
result += sysv_weak_func(x);
result += sysv_protected_func(x);
result += sysv_internal_func(x);
result += sysv_ifunc_func(x);
result += sysv_hidden_func(x);
result += sysv_static_func(x);
result += sysv_default_object;
result += sysv_weak_object;
result += sysv_protected_object;
result += sysv_hidden_object;
result += sysv_tls_object;
result += sysv_weak_tls_object;
result += sysv_protected_tls_object;
result += sysv_static_tls_object;
result += sysv_static_object;
result += sysv_internal_object;
//result += (int)((unsigned long)&sysv_notype_symbol & 1);
//result += (int)((unsigned long)&sysv_hidden_notype_symbol & 1);
//result += (int)((unsigned long)&sysv_internal_notype_symbol & 1);
//result += (int)write(-1, 0, 0);
return result; /// 8: { run_to_line at }
}
/// file: "gnu_module.c"
__attribute__((visibility("default"))) int gnu_default_object = 20;
__attribute__((weak, visibility("default"))) int gnu_weak_object = 21;
__attribute__((visibility("protected"))) int gnu_protected_object = 22;
__attribute__((visibility("hidden"))) int gnu_hidden_object = 23;
__attribute__((visibility("internal"))) int gnu_internal_object = 24;
__thread __attribute__((visibility("default"))) int gnu_tls_object = 25;
__thread __attribute__((weak, visibility("default"))) int gnu_weak_tls_object = 26;
__thread __attribute__((visibility("protected"))) int gnu_protected_tls_object = 27;
static __thread int gnu_static_tls_object = 28;
static int gnu_static_object = 29;
/*
extern long write(int, const void *, unsigned long);
extern char gnu_notype_symbol;
extern char gnu_hidden_notype_symbol;
extern char gnu_internal_notype_symbol;
__asm__(".globl gnu_notype_symbol\n"
"gnu_notype_symbol:\n"
" .byte 0\n"
".globl gnu_hidden_notype_symbol\n"
".hidden gnu_hidden_notype_symbol\n"
"gnu_hidden_notype_symbol:\n"
" .byte 0\n"
".globl gnu_internal_notype_symbol\n"
".internal gnu_internal_notype_symbol\n"
"gnu_internal_notype_symbol:\n"
" .byte 0\n");
*/
__attribute__((visibility("default"))) int
gnu_default_func(int x)
{
return x * 101; /// 9: { run_to_line at }
}
__attribute__((weak, visibility("default"))) int
gnu_weak_func(int x)
{
return x * 102; /// 10: { run_to_line at }
}
__attribute__((visibility("protected"))) int
gnu_protected_func(int x)
{
return x * 103; /// 11: { run_to_line at }
}
__attribute__((visibility("internal"))) int
gnu_internal_func(int x)
{
return x * 104; /// 12: { run_to_line at }
}
static int
gnu_ifunc_impl(int x)
{
return x * 105; /// 13: { run_to_line at }
}
static void *
gnu_ifunc_resolver(void)
{
return gnu_ifunc_impl;
}
__attribute__((ifunc("gnu_ifunc_resolver"), visibility("default"))) int gnu_ifunc_func(int x);
__attribute__((visibility("hidden"))) int
gnu_hidden_func(int x)
{
return x * gnu_hidden_object; /// 14: { run_to_line at }
}
static int
gnu_static_func(int x)
{
return x * gnu_static_object; /// 15: { run_to_line at }
}
__attribute__((visibility("default"))) int
gnu_run_all(int x)
{
int result = 0;
result += gnu_default_func(x);
result += gnu_weak_func(x);
result += gnu_protected_func(x);
result += gnu_internal_func(x);
result += gnu_ifunc_func(x);
result += gnu_hidden_func(x);
result += gnu_static_func(x);
result += gnu_default_object;
result += gnu_weak_object;
result += gnu_protected_object;
result += gnu_hidden_object;
result += gnu_tls_object;
result += gnu_weak_tls_object;
result += gnu_protected_tls_object;
result += gnu_static_tls_object;
result += gnu_static_object;
result += gnu_internal_object;
/*
result += (int)((unsigned long)&gnu_notype_symbol & 1);
result += (int)((unsigned long)&gnu_hidden_notype_symbol & 1);
result += (int)((unsigned long)&gnu_internal_notype_symbol & 1);
result += (int)write(-1, 0, 0);
*/
return result; /// 16: { run_to_line at }
}
/// file: "main.c"
int sysv_run_all(int x);
int gnu_run_all(int x);
int main()
{
int result = 0;
result += sysv_run_all(123); /// 17: { run_to_line at }
result += gnu_run_all(321); /// 18: { run_to_line at }
return result; /// 19: { run_to_line at run }
}
File diff suppressed because it is too large. Load diff
@@ -1,25 +0,0 @@
/// test: {
/// windows: {
/// skip: ""
/// compile: {
/// cc: "clang"
/// args: "-O0 -g -o main.exe %FILE%"
/// }
/// launch: "main.exe"
/// }
/// linux: {
/// skip: ""
/// compile: "-O0 -g -o main %FILE%"
/// launch: "./main"
/// }
/// }
int main()
{ /// 1: { step_into step_into }
for (int i = 0; i < 1; /// 2: step_into
++i, ({ goto exit; })) { /// 4: step_into
int x = 0; /// 3: step_into
}
exit:;
} /// 5: at
@@ -1,42 +0,0 @@
// TODO: step into alg needs to detect that it is about to step into a thunk and place breakpoint, respective to OS,
// where thunk calls into resolved function
/// test: {
/// windows: {
/// skip: ""
/// compile: "/Od /Z7 /c main.c module.c"
/// link: "/fixed /debug:full /dll module.obj"
/// link: "/fixed /debug:full /entry:main kernel32.lib module.lib delayimp.lib main.obj /out:main.exe /delayload:module.dll /incremental:no"
/// launch: "main.exe"
/// }
/// linux: {
/// skip: ""
/// compile: "-O0 -g module.c -fPIC -shared -o libmodule.so"
/// compile: "-O0 -g main.c -L. -lmodule -o main -Wl,-rpath,%CWD%"
/// launch: "./main"
/// }
/// }
///
/// file: "module.c"
#if _WIN32
__declspec(dllexport)
#endif
int foo(int a)
{ /// 6: { at, run }
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 }
}
@@ -1,37 +0,0 @@
/// 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 }
}
+138 -150
View File
@@ -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_(&params);
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);
}
+20 -63
View File
@@ -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);
-341
View File
@@ -1,341 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
TEST(str8_list_substr)
{
String8List zero_list = {0};
{
String8List list = {0};
str8_list_pushf(arena, &list, "a");
str8_list_pushf(arena, &list, "b");
str8_list_pushf(arena, &list, "c");
String8List sub = str8_list_substr(arena, list, r1u64(0, 3));
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, "b");
str8_list_pushf(arena, &list, "c");
String8List sub = str8_list_substr(arena, list, r1u64(0, max_U64));
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");
String8List sub = str8_list_substr(arena, list, r1u64(2, 3));
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");
String8List sub = str8_list_substr(arena, list, r1u64(1, 2));
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");
str8_list_pushf(arena, &list, "cd");
str8_list_pushf(arena, &list, "ef");
String8List sub = str8_list_substr(arena, list, r1u64(1, 5));
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));
}
}
TEST(bit_array)
{
for (U64 start=0; start<32*3; start++) {
for (U64 end=start; end<32*3; end++) {
U32 v[3] = { 0 };
for (U64 i=start; i<end; i++) {
v[i/32] |= 1 << (i%32);
}
for (U64 lo=0; lo<32*3; lo++) {
for (U64 hi=0; hi<32*3; hi++) {
U64 expected_idx = Min(hi, end) - 1;
B32 expected_r = hi <= start || lo >= end || lo >= hi || start >= end ? 0 : 1;
U64 idx = bit_array_scan_right_to_left32((U32Array){.v=v, .count=ArrayCount(v)}, lo, hi, 1);
B32 r = idx < hi;
T_Ok(r == expected_r);
if (r) {
T_Ok(idx == expected_idx);
}
}
}
}
}
}
TEST(count_digits)
{
T_Ok(count_digits_u64(0, 10) == 1);
T_Ok(count_digits_u64(99, 10) == 2);
T_Ok(count_digits_u64(999, 10) == 3);
T_Ok(count_digits_u64(9999, 10) == 4);
T_Ok(count_digits_u64(99999, 10) == 5);
T_Ok(count_digits_u64(999999, 10) == 6);
}
TEST(match_wildcard)
{
// empty strings
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit(""), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("*"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit(""), str8_lit("**"), 0) == 1);
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(""), 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);
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?"), 0) == 0);
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("a?"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?b"), 0) == 1);
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);
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*c"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("*b*"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("a*c"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("b*"), 0) == 0);
T_Ok(str8_match_wildcard(str8_lit("abc"), 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("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);
T_Ok(str8_match_wildcard(str8_lit("abc"), str8_lit("?*?"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit("ab"), str8_lit("?*?"), 0) == 1);
T_Ok(str8_match_wildcard(str8_lit("a"), str8_lit("?*?"), 0) == 0);
}
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));
}
T_Ok(hm.tombstone_count == 10);
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};
for EachIndex(round, 100) {
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);
}
}
-597
View File
@@ -1,597 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
// TODO:
//
// [ ] d2r_types alias size and byte size on __float80 typedef mismatch
internal RDI_Parsed *
d2r_rdi_from_dwarf_writer(Arena *arena, DW_Writer *writer)
{
Temp scratch = scratch_begin(&arena, 1);
RDI_Parsed *rdi = push_array(arena, RDI_Parsed, 1);
String8 raw_coff;
{
OBJ *obj = obj_alloc(0, Arch_x64);
OBJ_Section *text_section = obj_push_section(obj, str8_lit(".text"), OBJ_SectionFlag_Read|OBJ_SectionFlag_Exec|OBJ_SectionFlag_Load);
str8_serial_push_u8(obj->arena, &text_section->data, 0xc3);
obj_push_symbol(obj, str8_lit("main"), OBJ_SymbolScope_Global, OBJ_RefKind_Section, text_section);
dw_writer_emit_to_obj(writer, obj);
raw_coff = coff_from_obj(scratch.arena, obj);
obj_release(&obj);
}
Assert(t_write_file(str8_lit("a.obj"), raw_coff));
t_invoke(t_radlink_path(), str8_lit("/subsystem:console /out:a.exe /entry:main /DEBUG:FULL /opt:noref /opt:noicf a.obj"), max_U64);
Assert(g_last_exit_code == 0);
String8 exe = t_read_file(scratch.arena, str8_lit("a.exe"));
Assert(exe.size > 0);
B32 was_pdb_deleted = delete_file_at_path(t_make_file_path(scratch.arena, str8_lit("a.pdb")));
Assert(was_pdb_deleted);
t_invoke(t_radbin_path(), str8_lit("-rdi a.exe -out:a.rdi"), max_U64);
if (g_last_exit_code != 0) {
t_errorf("ERROR: failed to invoke radbin: %S\n", g_errors);
goto exit;
}
String8 raw_rdi = t_read_file(arena, str8_lit("a.rdi"));
Assert(raw_rdi.size > 0);
RDI_ParseStatus rdi_parse_status = rdi_parse(raw_rdi.str, raw_rdi.size, rdi);
Assert(rdi_parse_status == RDI_ParseStatus_Good);
exit:;
scratch_end(scratch);
return rdi;
}
internal RDI_TypeNode *
d2rt_type_from_name(RDI_Parsed *rdi, RDI_ParsedNameMap *map, char *name)
{
String8 s = str8_cstring(name);
RDI_NameMapNode *node = rdi_name_map_lookup(rdi, map, s.str, s.size);
U32 id_count = 0;
U32 *ids = rdi_matches_from_map_node(rdi, node, &id_count);
if (id_count == 1) {
return rdi_element_from_name_idx(rdi, TypeNodes, ids[0]);
}
return 0;
}
TEST(d2r_types)
{
DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64);
{
dw_writer_tag_begin(writer, DW_TagKind_CompileUnit);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Producer, "Test");
#define DeclBaseType(tt, n, e, s) \
DW_WriterTag *tt = dw_writer_tag_begin(writer, DW_TagKind_BaseType); \
dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, s); \
dw_writer_push_attrib_enum(writer, DW_AttribKind_Encoding, DW_ATE_##e); \
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, n); \
dw_writer_tag_end(writer);
DeclBaseType(char_type, "char", SignedChar, 1);
DeclBaseType(unsigned_char_type, "unsigned char", UnsignedChar, 1);
DeclBaseType(char8_type, "char8_t", Utf, 1);
DeclBaseType(char16_type, "char16_t", Utf, 2);
DeclBaseType(char32_type, "char32_t", Utf, 4);
DeclBaseType(wchar_type, "wchar_t", Signed, 4);
DeclBaseType(bool_type, "_Bool", Boolean, 1);
DeclBaseType(short_type, "short", Signed, 2);
DeclBaseType(unsigned_short_type, "unsigned short", Unsigned, 2);
DeclBaseType(short_unsigned_int_type, "short unsigned int", Unsigned, 2);
DeclBaseType(short_int_type, "short int", Signed, 2);
DeclBaseType(unsigned_int_type, "unsigned int", Unsigned, 4);
DeclBaseType(int_type, "int", Signed, 4);
DeclBaseType(long_int_type, "long int", Signed, 8);
DeclBaseType(long_unsigned_int_type, "long unsigned int", Unsigned, 8);
DeclBaseType(long_long_int_type, "long long int", Signed, 8);
DeclBaseType(long_long_unsigned_int, "long long unsigned int", Unsigned, 8);
DeclBaseType(float_type, "float", Float, 4);
DeclBaseType(double_type, "double", Float, 8);
DeclBaseType(long_double_type, "long double", Float, 16);
DeclBaseType(int128_type, "__int128", Signed, 16);
DeclBaseType(uint128_type, "__int128 unsigned", Unsigned, 16);
DeclBaseType(float16_type, "_Float16", Float, 2);
DeclBaseType(bfloat16_type, "__bf16", Float, 2);
//DeclBaseType(float80_type, "__float80", Float, 16);
DeclBaseType(float128_type, "_float128", Float, 16);
DeclBaseType(complex_float_type, "complex float", ComplexFloat, 8);
DeclBaseType(complex_doulbe_type, "complex double", ComplexFloat, 16);
DeclBaseType(complex_long_double_type, "complex long double", ComplexFloat, 32);
DeclBaseType(decimal32_type, "_Decimal32", DecimalFloat, 4);
DeclBaseType(decimal64_type, "_Decimal64", DecimalFloat, 8);
DeclBaseType(decimal128_type, "_Decimal128", DecimalFloat, 16);
#undef DeclBaseType
#define DeclStdint(n, a) \
do { \
dw_writer_tag_begin(writer, DW_TagKind_Typedef); \
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, n); \
dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, a); \
dw_writer_tag_end(writer); \
} while (0)
DeclStdint("uint8_t", unsigned_char_type);
DeclStdint("uint16_t", unsigned_short_type);
DeclStdint("uint32_t", unsigned_int_type);
DeclStdint("uint64_t", long_unsigned_int_type);
DeclStdint("int8_t", char_type);
DeclStdint("int16_t", short_type);
DeclStdint("int32_t", int_type);
DeclStdint("int64_t", long_int_type);
#undef DeclStdInt
// TODO: @native_vector_support
//
// typedef int __attribute__((vector_size(32))) int256
// typedef unsigned int __attribute__((vector_size(32))) uint256
// typedef int __attribute__((vector_size(64))) int512
// typedef unsigned int __attribute__((vector_size(64))) uint512
//
#if 0
dw_writer_tag_begin(writer, DW_TagKind_ArrayType);
dw_writer_push_attrib_flag(writer, DW_AttribKind_GNU_Vector, 1);
dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, int_type);
dw_writer_tag_begin(writer, DW_TagKind_SubrangeType);
dw_writer_push_attrib_uint(writer, DW_AttribKind_UpperBound, 15);
dw_writer_tag_end(writer);
dw_writer_tag_end(writer);
#endif
dw_writer_tag_end(writer);
}
RDI_Parsed *rdi = d2r_rdi_from_dwarf_writer(arena, writer);
RDI_NameMap *types_nm = rdi_element_from_name_idx(rdi, NameMaps, RDI_NameMapKind_Types);
T_Ok(types_nm);
RDI_ParsedNameMap types_map = {0};
rdi_parsed_from_name_map(rdi, types_nm, &types_map);
#define TestBuiltinType(n, bs, r) \
do { \
RDI_TypeNode *alias = d2rt_type_from_name(rdi, &types_map, n); \
T_Ok(alias); \
T_Ok(alias->kind == RDI_TypeKind_Alias); \
T_Ok(alias->flags == 0); \
T_Ok(alias->byte_size == bs); \
RDI_TypeNode *type = rdi_element_from_name_idx(rdi, TypeNodes, alias->user_defined.direct_type_idx); \
T_Ok(type); \
T_Ok(type->kind == RDI_TypeKind_##r); \
T_Ok(type->flags == 0); \
T_Ok(type->byte_size == alias->byte_size); \
U64 expected_name_size = 0; \
U8 *expected_name = rdi_string_from_type_kind(RDI_TypeKind_##r, &expected_name_size); \
T_Ok(str8_match(str8_from_rdi_string_idx(rdi, type->built_in.name_string_idx), str8(expected_name, expected_name_size), 0)); \
} while (0)
TestBuiltinType("char", 1, Char8);
TestBuiltinType("char8_t", 1, UChar8);
TestBuiltinType("char16_t", 2, UChar16);
TestBuiltinType("char32_t", 4, UChar32);
TestBuiltinType("unsigned char", 1, UChar8);
TestBuiltinType("wchar_t", 4, Char32);
TestBuiltinType("_Bool", 1, Bool);
TestBuiltinType("short", 2, S16);
TestBuiltinType("unsigned short", 2, U16);
TestBuiltinType("short unsigned int", 2, U16);
TestBuiltinType("short int", 2, S16);
TestBuiltinType("unsigned int", 4, U32);
TestBuiltinType("int", 4, S32);
TestBuiltinType("long int", 8, S64);
TestBuiltinType("long unsigned int", 8, U64);
TestBuiltinType("long long int", 8, S64);
TestBuiltinType("float", 4, F32);
TestBuiltinType("double", 8, F64);
TestBuiltinType("long double", 16, F128);
TestBuiltinType("__int128", 16, S128);
TestBuiltinType("__int128 unsigned", 16, U128);
TestBuiltinType("_Float16", 2, F16);
TestBuiltinType("__bf16", 2, BF16);
//TestBuiltinType("__float80", 16, F80);
TestBuiltinType("_float128", 16, F128);
TestBuiltinType("complex float", 8, ComplexF32);
TestBuiltinType("complex double", 16, ComplexF64);
TestBuiltinType("complex long double", 32, ComplexF128);
TestBuiltinType("_Decimal32", 4, Decimal32);
TestBuiltinType("_Decimal64", 8, Decimal64);
TestBuiltinType("_Decimal128", 16, Decimal128);
#undef TestBuiltinType
#define TestStdint(n, s, t) \
do { \
RDI_TypeNode *td = d2rt_type_from_name(rdi, &types_map, n); \
T_Ok(td); \
T_Ok(td->kind == RDI_TypeKind_Alias); \
T_Ok(td->flags == 0); \
T_Ok(td->byte_size == s); \
RDI_TypeNode *type = rdi_element_from_name_idx(rdi, TypeNodes, td->user_defined.direct_type_idx); \
T_Ok(type); \
T_Ok(type->kind == RDI_TypeKind_Alias); \
T_Ok(type->flags == 0); \
T_Ok(type->byte_size = td->byte_size); \
T_Ok(str8_match(str8_from_rdi_string_idx(rdi, type->built_in.name_string_idx), str8_lit(t), 0)); \
} while (0)
TestStdint("uint8_t", 1, "unsigned char");
TestStdint("uint16_t", 2, "unsigned short");
TestStdint("uint32_t", 4, "unsigned int");
TestStdint("uint64_t", 8, "long unsigned int");
TestStdint("int8_t", 1, "char");
TestStdint("int16_t", 2, "short");
TestStdint("int32_t", 4, "int");
TestStdint("int64_t", 8, "long int");
#undef TestStdint
dw_writer_end(&writer);
}
SKIP(d2r_line_table)
{
DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64);
String8 comp_dir = str8_lit("c:/DEVEL/");
String8 comp_name = str8_lit("test.c");
DW_WriterFile *foo_file = dw_writer_new_file(writer, str8_lit("/mnt/C/Devel/foo.c"));
DW_WriterFile *comp_file = dw_writer_new_file(writer, str8f(arena, "%S%S", comp_dir, comp_name));
struct {
DW_WriterFile *file; U64 ln; U64 line_size; U64 voff;
} test_table[] = {
{ comp_file, 6, 1 },
{ comp_file, 4, 2 },
{ comp_file, 2, 3 },
{ comp_file, 1, 4 },
{ comp_file, 8, 5 },
{ foo_file, 1, 3 },
{ foo_file, 100, 1 },
{ comp_file, max_U32 - 0x100, 10 },
};
U64 exe_base = coff_default_exe_base_from_machine(COFF_MachineType_X64);
U64 voff = 0x1000;
for EachElement(i, test_table) {
test_table[i].voff = voff;
dw_writer_line_emit(writer, test_table[i].file, test_table[i].ln, 0, exe_base + voff);
voff += test_table[i].line_size;
}
// emit one past last line
dw_writer_line_emit(writer,
test_table[ArrayCount(test_table) - 1].file,
test_table[ArrayCount(test_table) - 1].ln,
0,
exe_base + voff);
dw_writer_tag_begin(writer, DW_TagKind_CompileUnit);
dw_writer_push_attrib_string(writer, DW_AttribKind_Producer, str8_lit("RAD DWARF WRITER"));
dw_writer_push_attrib_string(writer, DW_AttribKind_CompDir, comp_dir);
dw_writer_push_attrib_string(writer, DW_AttribKind_Name, comp_name);
dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, exe_base);
dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, exe_base + voff);
dw_writer_push_attrib_line_ptr(writer, DW_AttribKind_StmtList, 0);
dw_writer_tag_end(writer);
d2r_rdi_from_dwarf_writer(arena, writer);
for EachElement(i, test_table) {
for EachIndex(k, test_table[i].line_size) {
String8 cmdl = str8f(arena, "-voff2line -voff:0x%llx %S", test_table[i].voff + k, t_make_file_path(arena, str8_lit("a.rdi")));
t_invoke_radbin((char *)cmdl.str);
if (g_last_exit_code != 0) {
t_errorf("radbin exited with %llu on \"%S\"\n", (unsigned long long)g_last_exit_code, cmdl);
T_Ok(0);
}
if ( ! t_match_linef(&g_output, "%S:%llu", test_table[i].file->path, test_table[i].ln)) {
t_errorf("ERROR: conversion produced unexpected output for \"radbin %S\"\n" \
" Expected: %S:%llu\n" \
" Got: %S\n",
cmdl,
test_table[i].file->path, (unsigned long long)test_table[i].ln,
g_output);
if (g_verbose) {
t_invoke_radbin("-dump -only:line_tables a.rdi");
t_errorf("\n================================================================================\n");
t_errorf("RDI:\n%S\n", g_output);
t_errorf("================================================================================\n");
t_invoke_radbin("-dump -only:debug_line a.exe");
t_errorf("DWARF:\n%S\n", g_output);
g_output = str8_zero();
}
T_Ok(0);
}
}
}
dw_writer_end(&writer);
}
SKIP(d2r_checksums)
{
DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64);
B32 is_ok = 0;
DW_WriterFile *foo_file = dw_writer_new_file(writer, str8_lit("/mnt/c/devel/foo.c"));
DW_WriterFile *comp_file = dw_writer_new_file(writer, str8_lit("/home/main.c"));
foo_file->md5 = *(U128 *)&(U8[16]){ 0x04, 0x70, 0xe9, 0x6b, 0xa3, 0x05, 0x2c, 0xc8, 0x2d, 0x70, 0xc5, 0xe9, 0x80, 0x8e, 0x8a, 0x4e, };
comp_file->md5 = *(U128 *)&(U8[16]){ 0x82, 0x3c, 0xc6, 0x02, 0x82, 0x9e, 0xf6, 0xce, 0x53, 0xff, 0x5a, 0x93, 0xb6, 0x6e, 0x59, 0x38 };
comp_file->time_stamp = 123; // convert must pick MD5 checksum over time stamp
dw_writer_tag_begin(writer, DW_TagKind_CompileUnit);
dw_writer_push_attrib_string(writer, DW_AttribKind_Producer, str8_lit("RAD DWARF WRITER"));
dw_writer_push_attrib_string(writer, DW_AttribKind_CompDir, str8_chop_last_slash(comp_file->path));
dw_writer_push_attrib_string(writer, DW_AttribKind_Name, str8_skip_last_slash(comp_file->path));
dw_writer_push_attrib_line_ptr(writer, DW_AttribKind_StmtList, 0);
dw_writer_tag_end(writer);
RDI_Parsed *rdi = d2r_rdi_from_dwarf_writer(arena, writer);
U64 checksum_count = 0;
RDI_MD5 *checksums = rdi_table_from_name(rdi, MD5Checksums, &checksum_count);
if (checksum_count != writer->line.file_count + 1) { goto exit; }
RDI_SourceFile *foo_src_file = rdi_source_file_from_normal_path_cstr(rdi, (char *)foo_file->path.str);
if (foo_src_file == 0) {
t_errorf("ERROR: RDI is missing file %S\n", foo_file->path);
goto exit;
}
if (foo_src_file->checksum_kind != RDI_ChecksumKind_MD5) {
t_errorf("ERROR: File %S has unexpected checksum value %u, expected %u\n", foo_file->path, foo_src_file->checksum_kind, RDI_ChecksumKind_MD5);
goto exit;
}
if (MemoryMatch(&foo_file->md5, &checksums[foo_src_file->checksum_idx], sizeof(U128)) != 1) {
t_errorf("ERROR mismatched checksum in file %S\n", foo_file->path);
goto exit;
}
RDI_SourceFile *comp_src_file = rdi_source_file_from_normal_path_cstr(rdi, (char *)comp_file->path.str);
if (comp_src_file == 0) {
t_errorf("ERROR: RDI is missing file %S\n", comp_file->path);
goto exit;
}
if (comp_src_file->checksum_kind != RDI_ChecksumKind_MD5) {
t_errorf("ERROR: File %S has unexpected checksum value %u, expected %u\n", comp_file->path, comp_src_file->checksum_kind, RDI_ChecksumKind_MD5);
goto exit;
}
if (MemoryMatch(&comp_file->md5, &checksums[comp_src_file->checksum_idx], sizeof(U128)) != 1) {
t_errorf("ERROR mismatched checksum in file %S\n", comp_file->path);
goto exit;
}
is_ok = 1;
exit:;
if ( ! is_ok) {
t_errorf("Converter repro: \"radbin -rdi %S\"\n", t_make_file_path(arena, str8_lit("a.exe")));
t_invoke(t_radbin_path(), str8_lit("-dump -only:source_files a.rdi"), max_U64);
t_errorf("RDI:\n%S\n", g_output);
t_invoke(t_radbin_path(), str8_lit("-dump -only:debug_line a.exe"), max_U64);
t_errorf("DWARF:\n%S\n", g_output);
}
dw_writer_end(&writer);
}
#if SUBPROGRAM_CONVERSION_TEST
TEST(d2r_subprogram)
{
DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64);
U64 image_lo = coff_default_exe_base_from_machine(COFF_MachineType_X64);
U64 image_hi = image_lo + 0x10000;
U64 subprogram_lo = image_lo + 0x1000;
U64 subprogram_hi = subprogram_lo + 0x100;
U64 subprogram_entry_addr = subprogram_lo + 5;
String8 subprogram_link_name = str8_lit("@FOOBAR!");
String8 subprogram_name = str8_lit("foobar");
dw_writer_tag_begin(writer, DW_TagKind_CompileUnit);
dw_writer_push_attrib_string (writer, DW_AttribKind_Producer, str8_lit("RAD DWARF WRITER"));
dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, image_lo);
dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, image_hi);
DW_WriterTag *char_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType);
dw_writer_push_attrib_sint (writer, DW_AttribKind_ByteSize, 1);
dw_writer_push_attrib_sint (writer, DW_AttribKind_Encoding, DW_ATE_SignedChar);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "char");
dw_writer_tag_end(writer);
DW_WriterTag *char_ptr_type = dw_writer_tag_begin(writer, DW_TagKind_PointerType);
dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, 8);
dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, char_type);
dw_writer_tag_end(writer);
DW_WriterTag *int_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType);
dw_writer_push_attrib_sint (writer, DW_AttribKind_ByteSize, 4);
dw_writer_push_attrib_sint (writer, DW_AttribKind_Encoding, DW_ATE_Signed);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "int");
dw_writer_tag_end(writer);
DW_WriterTag *int_ptr_type = dw_writer_tag_begin(writer, DW_TagKind_PointerType);
dw_writer_push_attrib_uint(writer, DW_AttribKind_ByteSize, 8);
dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, int_type);
dw_writer_tag_end(writer);
dw_writer_tag_begin(writer, DW_TagKind_SubProgram);
dw_writer_push_attrib_enum (writer, DW_AttribKind_Accessibility, DW_AccessKind_Private);
dw_writer_push_attrib_enum (writer, DW_AttribKind_AddressClass, DW_AddrClassKind_None);
dw_writer_push_attrib_uint (writer, DW_AttribKind_Alignment, 32);
dw_writer_push_attrib_flag (writer, DW_AttribKind_Artificial, 1);
dw_writer_push_attrib_enum (writer, DW_AttribKind_CallingConvention, DW_CallingConventionKind_Program);
dw_writer_push_attrib_flag (writer, DW_AttribKind_Deleted, 1);
dw_writer_push_attrib_address(writer, DW_AttribKind_EntryPc, subprogram_entry_addr);
dw_writer_push_attrib_flag (writer, DW_AttribKind_Explicit, 1);
dw_writer_push_attrib_flag (writer, DW_AttribKind_External, 1);
dw_writer_push_attrib_exprv (writer, DW_AttribKind_FrameBase, DW_ExprEnc_Op(Reg7));
dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, subprogram_hi);
dw_writer_push_attrib_enum (writer, DW_AttribKind_Inline, DW_Inl_DeclaredNotInlined);
dw_writer_push_attrib_string (writer, DW_AttribKind_LinkageName, subprogram_link_name);
dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, subprogram_lo);
dw_writer_push_attrib_flag (writer, DW_AttribKind_MainSubProgram, 1);
dw_writer_push_attrib_string (writer, DW_AttribKind_Name, subprogram_name);
dw_writer_push_attrib_flag (writer, DW_AttribKind_NoReturn, 1);
dw_writer_push_attrib_flag (writer, DW_AttribKind_Prototyped, 1);
dw_writer_push_attrib_flag (writer, DW_AttribKind_Pure, 1);
dw_writer_push_attrib_flag (writer, DW_AttribKind_Recursive, 1);
dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, int_type);
dw_writer_push_attrib_enum (writer, DW_AttribKind_Visibility, DW_Vis_Local);
// TODO: DW_AttribKind_ObjectPointer
// TODO: DW_AttribKind_Ranges
// TODO: DW_AttribKind_StartScope
dw_writer_tag_begin(writer, DW_TagKind_FormalParameter);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "a");
dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, int_ptr_type);
dw_writer_push_attrib_exprv (writer, DW_AttribKind_Location, DW_ExprEnc_Op(Reg2)); // rcx
dw_writer_tag_end(writer);
dw_writer_tag_begin(writer, DW_TagKind_FormalParameter);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "b");
dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, char_ptr_type);
dw_writer_push_attrib_exprv (writer, DW_AttribKind_Location, DW_ExprEnc_Op(Reg5)); // rdi
dw_writer_tag_end(writer);
dw_writer_tag_begin(writer, DW_TagKind_UnspecifiedParameters);
dw_writer_tag_end(writer);
dw_writer_tag_end(writer);
// --------------------------------------------------------------------------------
DW_WriterTag *my_struct_type = dw_writer_tag_reserve(writer, DW_TagKind_StructureType);
DW_WriterTag *const_my_struct_type = dw_writer_tag_begin(writer, DW_TagKind_ConstType);
dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, my_struct_type);
dw_writer_tag_end(writer);
DW_WriterTag *my_struct_ptr_type = dw_writer_tag_begin(writer, DW_TagKind_PointerType);
dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, const_my_struct_type);
dw_writer_tag_end(writer);
dw_writer_tag_begin_reserved(writer, my_struct_type);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "MyStructure");
dw_writer_push_attrib_uint (writer, DW_AttribKind_ByteSize, 0x100);
dw_writer_tag_begin(writer, DW_TagKind_SubProgram);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "MyMethod");
dw_writer_push_attrib_ref (writer, DW_AttribKind_Type, int_ptr_type);
dw_writer_tag_begin(writer, DW_TagKind_FormalParameter);
dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, my_struct_ptr_type);
dw_writer_tag_end(writer);
dw_writer_tag_end(writer);
dw_writer_tag_end(writer);
// --------------------------------------------------------------------------------
dw_writer_tag_end(writer);
RDI_Parsed *rdi = d2r_rdi_from_dwarf_writer(arena, writer);
RDI_Procedure *proc = rdi_procedure_from_name_cstr(rdi, (char *)subprogram_name.str);
RDI_TypeNode *proc_type = rdi_element_from_name_idx(rdi, TypeNodes, proc->type_idx);
String8 proc_string = rdi_string_from_type(arena, rdi, proc, proc_type);
dw_writer_end(&writer);
}
#endif
TEST(d2r_general)
{
DW_Writer *writer = dw_writer_begin(DW_Format_32Bit, DW_Version_5, DW_CompUnitKind_Compile, Arch_x64);
{
dw_writer_tag_begin(writer, DW_TagKind_CompileUnit);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Producer, "Test");
// declare char type
DW_WriterTag *char_type = dw_writer_tag_begin(writer, DW_TagKind_BaseType);
dw_writer_push_attrib_sint(writer, DW_AttribKind_ByteSize, 1);
dw_writer_push_attrib_enum(writer, DW_AttribKind_Encoding, DW_ATE_SignedChar);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "char");
dw_writer_tag_end(writer);
// declare function
dw_writer_tag_begin(writer, DW_TagKind_SubProgram);
dw_writer_push_attrib_address(writer, DW_AttribKind_LowPc, 0x140173f9);
dw_writer_push_attrib_address(writer, DW_AttribKind_HighPc, 0x14017474b);
dw_writer_push_attrib_flag(writer, DW_AttribKind_External, 1);
dw_writer_push_attrib_flag(writer, DW_AttribKind_Prototyped, 1);
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "FooBar");
// declare local
dw_writer_tag_begin(writer, DW_TagKind_Variable);
dw_writer_push_attrib_exprv(writer, DW_AttribKind_Location, DW_ExprEnc_Op(Reg7));
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "TestLocal");
dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, char_type);
dw_writer_tag_end(writer);
// declare param
dw_writer_tag_begin(writer, DW_TagKind_FormalParameter);
dw_writer_push_attrib_exprv(writer, DW_AttribKind_Location, DW_ExprEnc_Op(Reg7));
dw_writer_push_attrib_stringf(writer, DW_AttribKind_Name, "TestParam");
dw_writer_push_attrib_ref(writer, DW_AttribKind_Type, char_type);
dw_writer_tag_end(writer);
dw_writer_tag_end(writer);
}
RDI_Parsed *rdi = d2r_rdi_from_dwarf_writer(arena, writer);
RDI_Symbol *proc = rdi_procedure_from_name_cstr(rdi, "FooBar");
T_Ok(proc);
String8 proc_name = str8_from_rdi_string_idx(rdi, proc->name_string_idx);
T_Ok(str8_match(proc_name, str8_lit("FooBar"), 0));
RDI_Scope *root_scope = rdi_root_scope_from_procedure(rdi, proc);
T_Ok(root_scope);
T_Ok(root_scope->local_count == 2);
{
RDI_Symbol *test_local = rdi_element_from_name_idx(rdi, LocalVariables, root_scope->local_first + 0);
T_Ok(test_local);
T_Ok((test_local->symbol_flags & RDI_SymbolFlag_IsParam) == 0);
String8 test_local_name = str8_from_rdi_string_idx(rdi, test_local->name_string_idx);
T_Ok(str8_match(test_local_name, str8_lit("TestLocal"), 0));
RDI_TypeNode *test_local_type = rdi_element_from_name_idx(rdi, TypeNodes, test_local->type_idx);
T_Ok(test_local_type);
T_Ok(test_local_type->kind == RDI_TypeKind_Alias);
T_Ok(test_local_type->flags == 0);
String8 alias_name = str8_from_rdi_string_idx(rdi, test_local_type->user_defined.name_string_idx);
T_Ok(str8_match(alias_name, str8_lit("char"), 0));
RDI_TypeNode *char_type = rdi_element_from_name_idx(rdi, TypeNodes, test_local_type->user_defined.direct_type_idx);
T_Ok(char_type);
T_Ok(char_type->kind == RDI_TypeKind_Char8);
T_Ok(char_type->flags == 0);
T_Ok(char_type->byte_size == 1);
String8 char_type_name = str8_from_rdi_string_idx(rdi, char_type->built_in.name_string_idx);
T_Ok(str8_match(char_type_name, str8_lit("Char8"), 0));
}
{
RDI_Symbol *test_local = rdi_element_from_name_idx(rdi, LocalVariables, root_scope->local_first + 1);
T_Ok(test_local);
T_Ok((test_local->symbol_flags & RDI_SymbolFlag_IsParam) != 0);
String8 test_local_name = str8_from_rdi_string_idx(rdi, test_local->name_string_idx);
T_Ok(str8_match(test_local_name, str8_lit("TestParam"), 0));
RDI_TypeNode *test_local_type = rdi_element_from_name_idx(rdi, TypeNodes, test_local->type_idx);
T_Ok(test_local_type);
T_Ok(test_local_type->kind == RDI_TypeKind_Alias);
T_Ok(test_local_type->flags == 0);
String8 alias_name = str8_from_rdi_string_idx(rdi, test_local_type->user_defined.name_string_idx);
T_Ok(str8_match(alias_name, str8_lit("char"), 0));
RDI_TypeNode *char_type = rdi_element_from_name_idx(rdi, TypeNodes, test_local_type->user_defined.direct_type_idx);
T_Ok(char_type);
T_Ok(char_type->kind == RDI_TypeKind_Char8);
T_Ok(char_type->flags == 0);
T_Ok(char_type->byte_size == 1);
String8 char_type_name = str8_from_rdi_string_idx(rdi, char_type->built_in.name_string_idx);
T_Ok(str8_match(char_type_name, str8_lit("Char8"), 0));
}
dw_writer_end(&writer);
}
File diff suppressed because it is too large. Load diff
-205
View File
@@ -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);
File diff suppressed because it is too large. Load diff
+9 -10
View File
@@ -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; }
-130
View File
@@ -1,130 +0,0 @@
#define T_Group "MD"
SKIP(md_tokenizer)
{
MD_TokenizeResult result;
// empty
result = md_tokenize_from_text(arena, str8_lit(""));
T_Ok(result.tokens.count == 0);
T_Ok(result.msgs.count == 0);
// identifiers
char *ids[] = {
"a",
"abc",
"_",
"_abc",
"abc123",
"abc_def123",
};
for EachElement(i, ids) {
String8 in = str8_cstring(ids[i]);
result = md_tokenize_from_text(arena, in);
T_Ok(result.tokens.count == 1);
T_Ok(result.tokens.v[0].range.min == 0);
T_Ok(result.tokens.v[0].range.max == in.size);
T_Ok(result.tokens.v[0].flags & MD_TokenFlag_Identifier);
T_Ok((result.tokens.v[0].flags & ~MD_TokenFlag_Identifier) == 0);
T_Ok(result.msgs.count == 0);
}
// numerics
char *numerics[] = {
"0",
"123",
"123.456",
".5",
"-1",
"-123.45"
"123_abc",
"1.2.3",
};
for EachElement(i, numerics) {
String8 in = str8_cstring(numerics[i]);
result = md_tokenize_from_text(arena, in);
T_Ok(result.tokens.count == 1);
T_Ok(result.tokens.v[0].range.min == 0);
T_Ok(result.tokens.v[0].range.max == in.size);
T_Ok(result.tokens.v[0].flags & MD_TokenFlag_Numeric);
T_Ok((result.tokens.v[0].flags & ~MD_TokenFlag_Numeric) == 0);
T_Ok(result.msgs.count == 0);
}
// comments
char *good_comments[] = {
"/**/",
"/* */",
"/* abc */",
"/* ** */",
"/**/",
"/**************/",
"/*abc*/",
"//foo",
"// bar",
"//"
};
B32 good_comments_passed = 0;
for EachElement(i, good_comments) {
String8 in = str8_cstring(good_comments[i]);
result = md_tokenize_from_text(arena, in);
T_Ok(result.tokens.count == 1);
T_Ok(dim_1u64(result.tokens.v[0].range) == in.size);
T_Ok(result.tokens.v[0].flags & MD_TokenFlag_Comment);
T_Ok((result.tokens.v[0].flags & ~MD_TokenFlag_Comment) == 0);
T_Ok(result.msgs.count == 0);
}
char *broken_comments[] = {
"/*",
"/*/",
};
for EachElement(i, broken_comments) {
String8 in = str8_cstring(broken_comments[i]);
result = md_tokenize_from_text(arena, in);
T_Ok(result.msgs.worst_message_kind == MD_MsgKind_Error);
T_Ok(result.msgs.count > 0);
}
// unterminated string
result = md_tokenize_from_text(arena, str8_lit("\"abc\nc"));
T_Ok(result.msgs.worst_message_kind == MD_MsgKind_Error);
T_Ok(result.msgs.count > 0);
// terminated string
//
// TODO: strings with \n in the middle are not correctly tokenized
result = md_tokenize_from_text(arena, str8_lit("\"abc\""));
T_Ok(result.msgs.count == 0);
// line column tracking
{
result = md_tokenize_from_text(arena, str8_lit("a\nb"));
T_Ok(result.tokens.count == 3);
// verify 'a'
T_Ok(result.tokens.v[0].flags & MD_TokenFlag_Identifier);
// verify '\n'
T_Ok(result.tokens.v[1].flags & MD_TokenFlag_Newline);
// verify 'b'
T_Ok(result.tokens.v[2].flags & MD_TokenFlag_Identifier);
}
{
result = md_tokenize_from_text(arena, str8_lit(" abc"));
T_Ok(result.tokens.count == 2);
T_Ok(result.msgs.count == 0);
// verify white space
T_Ok(result.tokens.v[0].flags & MD_TokenFlag_Whitespace);
// verify identifier
T_Ok(result.tokens.v[1].flags & MD_TokenFlag_Identifier);
}
}
#undef T_Group
-127
View File
@@ -1,127 +0,0 @@
#if OS_WINDOWS
// TODO: location is not baked consistently
#if !COMPILER_CLANG || (COMPILER_CLANG && BUILD_DEBUG)
TEST(p2r_determ)
{
U64 num_repeats_per_pdb = 32;
String8 pdb_paths[] =
{
push_str8f(arena, "%S/mule_main.pdb", g_test_data),
push_str8f(arena, "%S/mule_module.pdb", g_test_data),
};
for EachElement(pdb_idx, pdb_paths)
{
// rjf: unpack paths, make output directory
String8 pdb_path = path_normalized_from_string(arena, pdb_paths[pdb_idx]);
// rjf: generate all RDIs
String8List rdi_paths = {0};
String8List dump_paths = {0};
{
ProcessList processes = {0};
for EachIndex(repeat_idx, num_repeats_per_pdb)
{
String8 rdi_name = push_str8f(arena, "repeat_%I64u.rdi", repeat_idx);
String8 rdi_path = t_make_file_path(arena, rdi_name);
str8_list_push(arena, &rdi_paths, rdi_path);
String8 cmdl = str8f(arena, "radbin -rdi -deterministic %S -out:%S", pdb_path, rdi_path);
Process process = launch_cmd_line(cmdl);
T_Ok(!process_match(process_zero(), process));
process_list_push(arena, &processes, process);
}
for EachNode(n, ProcessNode, processes.first)
{
process_join(n->v, max_U64, 0);
}
}
// rjf: generate all dumps
{
ProcessList processes = {0};
for EachNode(n, String8Node, rdi_paths.first)
{
String8 rdi_path = n->string;
String8 dump_path = str8f(arena, "%S.dump", rdi_path);
str8_list_push(arena, &dump_paths, dump_path);
Process process_handle = launch_cmd_linef("radbin -dump -deterministic %S -out:%S", rdi_path, dump_path);
T_Ok(!process_match(process_zero(), process_handle));
process_list_push(arena, &processes, process_handle);
}
for EachNode(n, ProcessNode, processes.first)
{
process_join(n->v, max_U64, 0);
}
}
// rjf: gather all hashes/paths
U64 rdi_hashes_count = rdi_paths.node_count;
U128 *rdi_hashes = push_array(arena, U128, rdi_hashes_count);
String8 *rdi_paths_array = push_array(arena, String8, rdi_hashes_count);
U64 dump_hashes_count = dump_paths.node_count;
U128 *dump_hashes = push_array(arena, U128, dump_hashes_count);
String8 *dump_paths_array = push_array(arena, String8, dump_hashes_count);
{
U64 idx = 0;
for EachNode(n, String8Node, rdi_paths.first)
{
Temp scratch = scratch_begin(0, 0);
String8 rdi_path = n->string;
String8 path = rdi_path;
T_Ok(path.size);
String8 data = data_from_file_path(scratch.arena, path);
T_Ok(data.size);
rdi_hashes[idx] = u128_hash_from_str8(data);
rdi_paths_array[idx] = path;
scratch_end(scratch);
idx += 1;
}
}
{
U64 idx = 0;
for EachNode(n, String8Node, dump_paths.first)
{
Temp scratch = scratch_begin(0, 0);
String8 path = n->string;
T_Ok(path.size);
String8 data = data_from_file_path(scratch.arena, path);
T_Ok(data.size);
dump_hashes[idx] = u128_hash_from_str8(data);
dump_paths_array[idx] = path;
scratch_end(scratch);
idx += 1;
}
}
// rjf: determine if all hashes match
U64 mismatch_idx = max_U64;
for EachIndex(idx, rdi_hashes_count)
{
if(!u128_match(rdi_hashes[idx], rdi_hashes[0]))
{
mismatch_idx = idx;
break;
}
}
for EachIndex(idx, dump_hashes_count)
{
if(!u128_match(dump_hashes[idx], dump_hashes[0]))
{
mismatch_idx = idx;
break;
}
}
// rjf: output bad case info
if (mismatch_idx != max_U64) {
U64 idx = mismatch_idx;
t_outf(" pdb[%I64u] \"%S\"\n", idx, pdb_path);
t_outf(" rdi[%I64u] 0x%I64x:%I64x \"%S\"\n", idx, rdi_hashes[idx].u64[0], rdi_hashes[idx].u64[1], rdi_paths_array[idx]);
t_outf(" dump[%I64u] 0x%I64x:%I64x \"%S\"\n", idx, dump_hashes[idx].u64[0], dump_hashes[idx].u64[1], dump_paths_array[idx]);
T_Ok(0);
}
}
}
#endif
#endif
File diff suppressed because it is too large. Load diff
-28
View File
@@ -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);