mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-10-14 04:49:41 -07:00
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:
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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
+20
-63
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user