print out last captured output when a test fails or crashes

This commit is contained in:
Nikita Smith
2026-06-02 12:09:48 -07:00
committed by Ryan Fleury
parent a645622b10
commit 887ee21d94
7 changed files with 245 additions and 214 deletions
+100 -62
View File
@@ -2,21 +2,23 @@
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
// command line // command line
global String8 g_stdout_file_name = str8_lit_comp("torture.out"); global String8 g_stdout_file_name = str8_lit_comp("torture.out");
global String8 g_wdir; global String8 g_wdir;
global String8 g_out = str8_lit_comp("torture"); global String8 g_out = str8_lit_comp("torture");
global B32 g_verbose; global B32 g_verbose;
global B32 g_redirect_stdout = 1; global B32 g_redirect_stdout = 1;
global B32 g_stop_on_first_fail_or_crash = 1; global B32 g_stop_on_first_fail_or_crash = 1;
global String8 g_linker; global String8 g_linker;
global String8 g_test_data; global String8 g_test_data;
// tests // tests
U64 g_torture_test_count; U64 g_torture_test_count;
T_Test g_torture_tests[0xffffff]; T_Test g_torture_tests[0xffffff];
// invoke // invoke
global int g_last_exit_code; global int g_last_exit_code;
global Arena *g_output_arena;
global String8 g_output;
internal String8 internal String8
t_test_name_from_idx(Arena *arena, U64 test_idx) t_test_name_from_idx(Arena *arena, U64 test_idx)
@@ -180,6 +182,11 @@ t_run(T_Run run, String8 user_data)
} else { } else {
t_run_caller(&ctx); t_run_caller(&ctx);
} }
if (ctx.result.status == T_RunStatus_Fail || ctx.result.status == T_RunStatus_Crash) {
fprintf(stderr, "Last captured output:\n%.*s\n", str8_varg(g_output));
}
return ctx.result; return ctx.result;
} }
@@ -229,19 +236,18 @@ t_cl_version(void)
if ( ! version.size) { if ( ! version.size) {
Temp scratch = scratch_begin(0, 0); Temp scratch = scratch_begin(0, 0);
String8 output = {0}; t_invoke_cl("");
t_invoke_(t_cl_path(), str8_zero(), max_U64, scratch.arena, &output);
AssertAlways(g_last_exit_code == 0); AssertAlways(g_last_exit_code == 0);
String8 needle = str8_lit("Version"); String8 needle = str8_lit("Version");
U64 version_lo = str8_find_needle(output, 0, needle, 0); U64 version_lo = str8_find_needle(g_output, 0, needle, 0);
version_lo += needle.size + 1; version_lo += needle.size + 1;
AssertAlways(version_lo < output.size); AssertAlways(version_lo < g_output.size);
U64 version_hi = str8_find_needle(output, version_lo, str8_lit(" "), 0); U64 version_hi = str8_find_needle(g_output, version_lo, str8_lit(" "), 0);
AssertAlways(version_hi < output.size); AssertAlways(version_hi < g_output.size);
version = str8_substr(output, r1u64(version_lo, version_hi)); version = str8_substr(g_output, r1u64(version_lo, version_hi));
AssertAlways(version.size > 0); AssertAlways(version.size > 0);
local_persist U8 buffer[4096]; local_persist U8 buffer[4096];
@@ -249,6 +255,7 @@ t_cl_version(void)
Arena *arena = arena_alloc_(&params); Arena *arena = arena_alloc_(&params);
version = str8_copy(arena, version); version = str8_copy(arena, version);
MemoryZeroStruct(&g_output);
scratch_end(scratch); scratch_end(scratch);
} }
@@ -322,27 +329,27 @@ t_src_path(void)
} }
internal B32 internal B32
t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout, Arena *output_arena, String8 *output_out) t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout)
{ {
Temp scratch = scratch_begin(&output_arena,1); Temp scratch = scratch_begin(&g_output_arena,1);
B32 is_ok = 0; B32 is_ok = 0;
B32 capture_output = output_out || g_redirect_stdout; // clean up global state
arena_pop_to(g_output_arena, 0);
MemoryZeroStruct(&g_output);
g_last_exit_code = -1; g_last_exit_code = -1;
#if OS_WINDOWS
File read_pipe_handle = {0}, write_pipe_handle = {0}; File read_pipe_handle = {0}, write_pipe_handle = {0};
if (capture_output) { HANDLE read_pipe, write_pipe;
HANDLE read_pipe, write_pipe; SECURITY_ATTRIBUTES at = { .nLength = sizeof(at), .bInheritHandle = 1 };
SECURITY_ATTRIBUTES at = { .nLength = sizeof(at), .bInheritHandle = 1 }; if (!CreatePipe(&read_pipe, &write_pipe, &at, 0)) { AssertAlways(0 && "failed to create a pipe"); }
if (!CreatePipe(&read_pipe, &write_pipe, &at, 0)) { read_pipe_handle = (File){ .u64[0] = (U64)read_pipe };
AssertAlways(0 && "failed to create a pipe"); write_pipe_handle = (File){ .u64[0] = (U64)write_pipe };
} #else
read_pipe_handle = (File){ .u64[0] = (U64)read_pipe }; # error NotImplemented
write_pipe_handle = (File){ .u64[0] = (U64)write_pipe }; #endif
}
// Build Launch Options // Build Launch Options
ProcessLaunchParams launch_opts = { ProcessLaunchParams launch_opts = {
.path = g_wdir, .path = g_wdir,
@@ -362,54 +369,84 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout, Ar
// invoke exe // invoke exe
Process process_handle = process_launch(&launch_opts); Process process_handle = process_launch(&launch_opts);
if (process_match(process_handle, process_zero())) { goto exit; }
// close handle so last to ReadFile does not block // close handle so last to ReadFile does not block
file_close(write_pipe_handle); file_close(write_pipe_handle);
if ( ! process_match(process_handle, process_zero())) { // capture process output
if (capture_output) { String8List output = {0};
// capture process output for (;;) {
String8List output = {0}; String8 string = file_read_cstring(scratch.arena, read_pipe_handle, 0);
for (;;) { if (string.size == 0) { break; }
String8 string = file_read_cstring(scratch.arena, read_pipe_handle, 0); str8_list_push(scratch.arena, &output, string);
if (string.size == 0) { break; } }
str8_list_push(scratch.arena, &output, string); file_close(read_pipe_handle);
}
file_close(read_pipe_handle);
if (output_out) { // update output global
*output_out = str8_list_join(output_arena, &output, 0); g_output = str8_list_join(g_output_arena, &output, 0);
}
// write to the output file // write to the output file
if (g_redirect_stdout) { if (g_redirect_stdout) {
write_data_list_to_file_path(g_stdout_file_name, output); write_data_list_to_file_path(g_stdout_file_name, output);
}
}
U64 exit_code_u64 = 0;
if (process_join(process_handle, timeout, &exit_code_u64)) {
g_last_exit_code = (int)exit_code_u64;
is_ok = 1;
} else {
process_kill(process_handle);
}
} }
U64 exit_code_u64 = 0;
if (process_join(process_handle, timeout, &exit_code_u64)) {
g_last_exit_code = (int)exit_code_u64;
is_ok = 1;
} else {
process_kill(process_handle);
}
exit:;
scratch_end(scratch); scratch_end(scratch);
return is_ok; return is_ok;
} }
internal B32 internal B32
t_invoke_(String8 exe_path, String8 cmdline, U64 timeout, Arena *output_arena, String8 *output_out) t_invoke(String8 exe_path, String8 cmdline, U64 timeout)
{ {
return t_invoke_env(exe_path, cmdline, (String8List){0}, timeout, output_arena, output_out); return t_invoke_env(exe_path, cmdline, (String8List){0}, timeout);
} }
internal B32 internal B32
t_invoke(String8 exe_path, String8 cmdline, U64 timeout) t_invoke_cl(char *fmt, ...)
{ {
return t_invoke_(exe_path, cmdline, timeout, 0, 0); Temp scratch = scratch_begin(0,0);
va_list args;
va_start(args, fmt);
String8 cmdl = push_str8fv(scratch.arena, fmt, args);
va_end(args);
B32 is_ok = t_invoke(t_cl_path(), cmdl, max_U64);
scratch_end(scratch);
return is_ok;
}
internal B32
t_invoke_linkerf(char *fmt, ...)
{
Temp scratch = scratch_begin(0,0);
va_list args;
va_start(args, fmt);
String8 cmdl = push_str8fv(scratch.arena, fmt, args);
va_end(args);
B32 is_ok = t_invoke(t_radlink_path(), cmdl, max_U64);
scratch_end(scratch);
return is_ok;
}
internal B32
t_invoke_radbin(char *fmt, ...)
{
Temp scratch = scratch_begin(0,0);
va_list args;
va_start(args, fmt);
String8 cmdl = push_str8fv(scratch.arena, fmt, args);
va_end(args);
B32 is_ok = t_invoke(t_radbin_path(), cmdl, max_U64);
scratch_end(scratch);
return is_ok;
} }
internal void internal void
@@ -422,10 +459,10 @@ t_kill_all(String8 pattern)
while (dmn_process_iter_next(scratch.arena, &it, &info)) { while (dmn_process_iter_next(scratch.arena, &it, &info)) {
if (str8_match_wildcard(info.name, pattern, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive)) { if (str8_match_wildcard(info.name, pattern, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive)) {
#if OS_WINDOWS #if OS_WINDOWS
if (!t_invoke_(str8_lit("taskkill"), str8f(scratch.arena, "/PID %u /F", info.pid), max_U64, 0, 0)) { fprintf(stderr, "ERROR: failed to invoke taskkill\n"); } if (!t_invoke(str8_lit("taskkill"), str8f(scratch.arena, "/PID %u /F", info.pid), max_U64)) { fprintf(stderr, "ERROR: failed to invoke taskkill\n"); }
#elif OS_LINUX #elif OS_LINUX
NotImplemented; // TODO: test NotImplemented; // TODO: test
if (!t_invoke_(str8_lit("kill"), str8f(scratch.arena, " -9 %u", info.pid), max_U64, 0, 0)) { fprintf(stderr, "ERROR: failed to invoke kill\n"); } if (!t_invoke(str8_lit("kill"), str8f(scratch.arena, " -9 %u", info.pid), max_U64)) { fprintf(stderr, "ERROR: failed to invoke kill\n"); }
#else #else
# error NotImplemented # error NotImplemented
#endif #endif
@@ -703,6 +740,7 @@ t_entry_point(CmdLine *cmdline)
g_verbose = cmd_line_has_flag(cmdline, str8_lit("verbose")); g_verbose = cmd_line_has_flag(cmdline, str8_lit("verbose"));
g_redirect_stdout = !cmd_line_has_flag(cmdline, str8_lit("print_stdout")); g_redirect_stdout = !cmd_line_has_flag(cmdline, str8_lit("print_stdout"));
g_stop_on_first_fail_or_crash = !cmd_line_has_flag(cmdline, str8_lit("keep_going")); g_stop_on_first_fail_or_crash = !cmd_line_has_flag(cmdline, str8_lit("keep_going"));
g_output_arena = arena_alloc();
// default options when running under debugger // default options when running under debugger
#if OS_WINDOWS #if OS_WINDOWS
+8 -2
View File
@@ -90,7 +90,13 @@ internal String8 t_radlink_path(void);
internal String8 t_cwd_path(void); internal String8 t_cwd_path(void);
internal String8 t_src_path(void); internal String8 t_src_path(void);
internal B32 t_invoke_(String8 exe_path, String8 cmdline, U64 timeout, Arena *output_arena, String8 *output_out);
internal B32 t_invoke(String8 exe, String8 cmdline, U64 timeout); internal B32 t_invoke(String8 exe, String8 cmdline, U64 timeout);
#define t_invoke_cl(f, ...) t_invoke_(t_cl_path(), str8f(scratch.arena, f, ## __VA_ARGS__), max_U64, 0, 0) internal B32 t_invoke_cl(char *fmt, ...);
internal B32 t_invoke_linkerf(char *fmt, ...);
internal B32 t_invoke_radbin(char *fmt, ...);
internal void t_kill_all(String8 pattern); internal void t_kill_all(String8 pattern);
#define t_invoke_linker_timeout(c, t) T_Ok(t_invoke(g_linker, c, t))
#define t_invoke_linker_timeoutf(t, f, ...) t_invoke_linker_timeout(push_str8f(arena, f, ##__VA_ARGS__), t)
#define t_invoke_linker(c) t_invoke_linker_timeout(c, max_U64)
+3 -5
View File
@@ -32,7 +32,7 @@ d2r_rdi_from_dwarf_writer(Arena *arena, DW_Writer *writer)
B32 was_pdb_deleted = delete_file_at_path(t_make_file_path(scratch.arena, str8_lit("a.pdb"))); B32 was_pdb_deleted = delete_file_at_path(t_make_file_path(scratch.arena, str8_lit("a.pdb")));
Assert(was_pdb_deleted); Assert(was_pdb_deleted);
t_invoke_(t_radbin_path(), str8_lit("-rdi a.exe -out:a.rdi"), max_U64, 0, 0); t_invoke(t_radbin_path(), str8_lit("-rdi a.exe -out:a.rdi"), max_U64);
Assert(g_last_exit_code == 0); Assert(g_last_exit_code == 0);
String8 raw_rdi = t_read_file(arena, str8_lit("a.rdi")); String8 raw_rdi = t_read_file(arena, str8_lit("a.rdi"));
@@ -275,11 +275,9 @@ TEST(d2r_line_table)
for EachElement(i, test_table) { for EachElement(i, test_table) {
for EachIndex(k, test_table[i].line_size) { for EachIndex(k, test_table[i].line_size) {
String8 cmd_line = str8f(arena, "-voff2line -voff:0x%llx a.rdi", test_table[i].voff + k); t_invoke_radbin("-voff2line -voff:0x%llx a.rdi", test_table[i].voff + k);
String8 output = {0};
t_invoke_(t_radbin_path(), cmd_line, max_U64, arena, &output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
T_MatchLinef(&output, "%S:%llu", test_table[i].file->path, test_table[i].ln); T_MatchLinef(&g_output, "%S:%llu", test_table[i].file->path, test_table[i].ln);
} }
} }
+15 -10
View File
@@ -33,10 +33,8 @@ t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, RD_IpcReply *rep
#endif #endif
// send command // send command
String8 cmdline = str8f(scratch.arena, "--ipc --pid:%u %S", dbg_pid, cmd); String8 cmdline = str8f(scratch.arena, "--ipc --pid:%u %S", dbg_pid, cmd);
Arena *output_arena = reply_arena ? reply_arena : scratch.arena; if (t_invoke(t_raddbg_path(), cmdline, timeout_us) == 0) { goto exit; }
String8 output = {0};
if (t_invoke_(t_raddbg_path(), cmdline, timeout_us, output_arena, &output) == 0) { goto exit; }
B32 has_reply = 1; B32 has_reply = 1;
@@ -55,7 +53,9 @@ t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, RD_IpcReply *rep
if (has_reply) { if (has_reply) {
// parse reply // parse reply
RD_IpcReply reply = rd_ipc_mdesk_reply_from_string(output_arena, output); Arena *a = reply_arena ? reply_arena : scratch.arena;
String8 reply_text = str8_copy(a, g_output);
RD_IpcReply reply = rd_ipc_mdesk_reply_from_string(a, reply_text);
if (rd_ipc_reply_is_ok(&reply) == 0) { goto exit; } if (rd_ipc_reply_is_ok(&reply) == 0) { goto exit; }
if (md_node_is_nil(reply.msg)) { goto exit; } if (md_node_is_nil(reply.msg)) { goto exit; }
if (reply_arena && reply_out) { *reply_out = reply; } if (reply_arena && reply_out) { *reply_out = reply; }
@@ -111,7 +111,7 @@ t_dbg_stop_event(Arena *arena, T_DbgStopEvent *out, U64 timeout_us)
// send status request // send status request
RD_IpcReply reply = {0}; RD_IpcReply reply = {0};
if ( ! t_dbg_send_cmd(str8_lit("stop_event"), timeout_us, scratch.arena, &reply)) { goto exit; } if ( ! t_dbg_send_cmd(str8_lit("stop_event"), timeout_us, arena ? arena : scratch.arena, &reply)) { goto exit; }
// parse reply // parse reply
if ( ! rd_ipc_parse_b32 (reply.msg, str8_lit("ok"), &is_ok)) { AssertAlways(0); goto exit; } if ( ! rd_ipc_parse_b32 (reply.msg, str8_lit("ok"), &is_ok)) { AssertAlways(0); goto exit; }
@@ -131,7 +131,7 @@ t_dbg_stop_event(Arena *arena, T_DbgStopEvent *out, U64 timeout_us)
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("tls_model"), &v.tls_model)) { AssertAlways(0); goto exit; } if ( ! rd_ipc_parse_int (reply.msg, str8_lit("tls_model"), &v.tls_model)) { AssertAlways(0); goto exit; }
if ( ! rd_ipc_parse_string (reply.msg, str8_lit("stop_cause"), &v.stop_cause)) { AssertAlways(0); goto exit; } if ( ! rd_ipc_parse_string (reply.msg, str8_lit("stop_cause"), &v.stop_cause)) { AssertAlways(0); goto exit; }
if (out != 0) { *out = v; } if (arena && out != 0) { *out = v; }
exit:; exit:;
return is_ok; return is_ok;
@@ -223,7 +223,7 @@ t_dbg_send_cmd_and_wait_stop(String8 cmd, U64 timeout_us)
} while (t > 0); } while (t > 0);
//--- Status --------------------- //--- Status ---------------------
if (is_stopped) { if (0 && is_stopped) {
T_DbgStatus status = {0}; T_DbgStatus status = {0};
AssertAlways(t_dbg_status(&status, 0)); AssertAlways(t_dbg_status(&status, 0));
@@ -271,8 +271,9 @@ t_dbg_launch(String8 cmdline, U64 timeout_us)
Temp scratch = scratch_begin(0, 0); Temp scratch = scratch_begin(0, 0);
B32 dbg_ready = 0; B32 dbg_ready = 0;
String8 user_path = t_make_file_path(scratch.arena, str8_lit("test.raddbg_user")); String8 user_path = t_make_file_path(scratch.arena, str8_lit("test.raddbg_user"));
cmdline = str8f(scratch.arena, "--user:\"%S\" %S", user_path, cmdline); String8 project_path = t_make_file_path(scratch.arena, str8_lit("test.raddbg_project"));
cmdline = str8f(scratch.arena, "--user:\"%S\" --project:\"%S\" %S", user_path, project_path, cmdline);
// launch debugger // launch debugger
OS_ProcessLaunchParams launch_opts = { OS_ProcessLaunchParams launch_opts = {
@@ -585,6 +586,10 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
case T_DbgScriptCmdKind_ClearBreakpoints: t_dbg_send_cmdf(0,0,0, "clear_breakpoints"); break; case T_DbgScriptCmdKind_ClearBreakpoints: t_dbg_send_cmdf(0,0,0, "clear_breakpoints"); break;
case T_DbgScriptCmdKind_Run: t_dbg_send_cmd(str8_lit("run"), timeout_us, 0, 0); break; case T_DbgScriptCmdKind_Run: t_dbg_send_cmd(str8_lit("run"), timeout_us, 0, 0); break;
case T_DbgScriptCmdKind_At: { case T_DbgScriptCmdKind_At: {
T_DbgStatus status = {0};
AssertAlways(t_dbg_status(&status, 0));
AssertAlways(status.running == 0);
// map IP -> source location // map IP -> source location
U64 ip = u64_from_str8(t_dbg_value_from_exprf(scratch.arena, "reg:rip"), 10); U64 ip = u64_from_str8(t_dbg_value_from_exprf(scratch.arena, "reg:rip"), 10);
T_DbgSourceLocation loc = {0}; T_DbgSourceLocation loc = {0};
+32 -43
View File
@@ -5011,14 +5011,15 @@ TEST(debug_p_sig_mismatch)
T_Ok(t_write_file(str8_lit("b.obj"), b_obj)); T_Ok(t_write_file(str8_lit("b.obj"), b_obj));
T_Ok(t_write_entry_obj()); T_Ok(t_write_entry_obj());
String8 output = {0}; t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:full a.obj b.obj entry.obj");
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full a.obj b.obj entry.obj"), max_U64, arena, &output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
B32 found_error = 0; B32 found_error = 0;
String8 a_obj_path = t_make_file_path(arena, str8_lit("a.obj")); String8 a_obj_path = t_make_file_path(arena, str8_lit("a.obj"));
String8 b_obj_path = t_make_file_path(arena, str8_lit("b.obj")); String8 b_obj_path = t_make_file_path(arena, str8_lit("b.obj"));
String8 expected_line = str8f(arena, "Error(%03d): %S: PCH signature mismatch, expected 0x%x got 0x%x; PCH obj %S", LNK_Error_PrecompSigMismatch, b_obj_path, b_sig, a_sig, a_obj_path); String8 expected_line = str8f(arena, "Error(%03d): %S: PCH signature mismatch, expected 0x%x got 0x%x; PCH obj %S", LNK_Error_PrecompSigMismatch, b_obj_path, b_sig, a_sig, a_obj_path);
String8 output = g_output;
while (output.size) { while (output.size) {
String8 line = t_chop_line(&output); String8 line = t_chop_line(&output);
found_error = str8_match(line, expected_line, StringMatchFlag_CaseInsensitive); found_error = str8_match(line, expected_line, StringMatchFlag_CaseInsensitive);
@@ -5124,10 +5125,10 @@ TEST(debug_p_and_debug_t_in_obj)
} }
})); }));
T_Ok(t_write_entry_obj()); T_Ok(t_write_entry_obj());
String8 output = {0}; t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:full pch.obj entry.obj");
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full pch.obj entry.obj"), max_U64, arena, &output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
String8 output = g_output;
B32 found_warning = 0; B32 found_warning = 0;
String8 pch_obj_path = t_make_file_path(arena, str8_lit("pch.obj")); String8 pch_obj_path = t_make_file_path(arena, str8_lit("pch.obj"));
String8 expected_line = str8f(arena, "Warning(%03d): %S: multiple sections with debug types detected, obj must have either .debug$T or .debug$P; discarding both sections", LNK_Warning_MultipleDebugTAndDebugP, pch_obj_path); String8 expected_line = str8f(arena, "Warning(%03d): %S: multiple sections with debug types detected, obj must have either .debug$T or .debug$P; discarding both sections", LNK_Warning_MultipleDebugTAndDebugP, pch_obj_path);
@@ -5531,12 +5532,11 @@ TEST(cyclic_type)
T_Ok(t_write_entry_obj()); T_Ok(t_write_entry_obj());
T_Ok(t_write_file(str8_lit("cycle.obj"), raw_coff)); T_Ok(t_write_file(str8_lit("cycle.obj"), raw_coff));
String8 cmd_line = str8f(arena, "/subsystem:console /entry:entry /out:a.exe /debug:full /rad_ignore:-%u cycle.obj entry.obj", LNK_Error_InvalidTypeIndex); t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:full /rad_ignore:-%u cycle.obj entry.obj", LNK_Error_InvalidTypeIndex);
String8 output = {0};
t_invoke_(t_radlink_path(), cmd_line, max_U64, arena, &output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
B32 is_cycle_detected = 0; String8 output = g_output;
B32 is_cycle_detected = 0;
while (output.size && !is_cycle_detected) { while (output.size && !is_cycle_detected) {
is_cycle_detected = t_match_linef(&output, is_cycle_detected = t_match_linef(&output,
"Error(043): %S: LF_POINTER(type_index: 0x1000) forward refs member type index 0x1001 (leaf struct offset: 0x0)", "Error(043): %S: LF_POINTER(type_index: 0x1000) forward refs member type index 0x1001 (leaf struct offset: 0x0)",
@@ -6006,14 +6006,13 @@ TEST(ghash_check_corrupt)
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj)); T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
T_Ok(t_write_entry_obj()); T_Ok(t_write_entry_obj());
String8 output = {0}; t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj");
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj"), max_U64, arena, &output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
B32 is_warning_found = 0; B32 is_warning_found = 0;
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj")); String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
String8 expected_line = str8f(arena, "Warning(%03u): %S: .debug$H section is too small to contain the header", LNK_Warning_GHash, debug_obj_path, LLVM_GHash_Magic); String8 expected_line = str8f(arena, "Warning(%03u): %S: .debug$H section is too small to contain the header", LNK_Warning_GHash, debug_obj_path, LLVM_GHash_Magic);
for (String8 i = output; i.size > 0 && !is_warning_found; ) { for (String8 i = g_output; i.size > 0 && !is_warning_found; ) {
String8 line = t_chop_line(&i); String8 line = t_chop_line(&i);
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive); is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
} }
@@ -6056,14 +6055,13 @@ TEST(ghash_check_magic)
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj)); T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
T_Ok(t_write_entry_obj()); T_Ok(t_write_entry_obj());
String8 output = {0}; t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj");
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj"), max_U64, arena, &output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
B32 is_warning_found = 0; B32 is_warning_found = 0;
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj")); String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
String8 expected_line = str8f(arena, "Warning(%03u): %S: .debug$H contains invalid magic: got 0x7b, expected 0x%x", LNK_Warning_GHash, debug_obj_path, LLVM_GHash_Magic); String8 expected_line = str8f(arena, "Warning(%03u): %S: .debug$H contains invalid magic: got 0x7b, expected 0x%x", LNK_Warning_GHash, debug_obj_path, LLVM_GHash_Magic);
for (String8 i = output; i.size > 0 && !is_warning_found; ) { for (String8 i = g_output; i.size > 0 && !is_warning_found; ) {
String8 line = t_chop_line(&i); String8 line = t_chop_line(&i);
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive); is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
} }
@@ -6106,14 +6104,13 @@ TEST(ghash_check_version)
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj)); T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
T_Ok(t_write_entry_obj()); T_Ok(t_write_entry_obj());
String8 output = {0}; t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj");
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj"), max_U64, arena, &output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
B32 is_warning_found = 0; B32 is_warning_found = 0;
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj")); String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H version: got %u, expected %u", LNK_Warning_GHash, debug_obj_path, 0xbeef, LLVM_GHash_CurrentVersion); String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H version: got %u, expected %u", LNK_Warning_GHash, debug_obj_path, 0xbeef, LLVM_GHash_CurrentVersion);
for (String8 i = output; i.size > 0 && !is_warning_found; ) { for (String8 i = g_output; i.size > 0 && !is_warning_found; ) {
String8 line = t_chop_line(&i); String8 line = t_chop_line(&i);
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive); is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
} }
@@ -6148,14 +6145,13 @@ TEST(ghash_check_hash_alg)
T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj)); T_Ok(t_write_file(str8_lit("debug.obj"), debug_obj));
T_Ok(t_write_entry_obj()); T_Ok(t_write_entry_obj());
String8 output = {0}; t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj");
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj"), max_U64, arena, &output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
B32 is_warning_found = 0; B32 is_warning_found = 0;
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj")); String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H hash algorithm: got SHA1_8, expected BALK3", LNK_Warning_GHash, debug_obj_path); String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H hash algorithm: got SHA1_8, expected BALK3", LNK_Warning_GHash, debug_obj_path);
for (String8 i = output; i.size > 0 && !is_warning_found; ) { for (String8 i = g_output; i.size > 0 && !is_warning_found; ) {
String8 line = t_chop_line(&i); String8 line = t_chop_line(&i);
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive); is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
} }
@@ -6191,13 +6187,13 @@ TEST(ghash_match_debug_t)
T_Ok(t_write_entry_obj()); T_Ok(t_write_entry_obj());
String8 output = {0}; String8 output = {0};
t_invoke_(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj"), max_U64, arena, &output); t_invoke_linkerf("/subsystem:console /entry:entry /out:a.exe /debug:ghash entry.obj debug.obj");
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
B32 is_warning_found = 0; B32 is_warning_found = 0;
String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj")); String8 debug_obj_path = t_make_file_path(arena, str8_lit("debug.obj"));
String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H hash count and type count: got 3 hashes for 2 types", LNK_Warning_GHash, debug_obj_path); String8 expected_line = str8f(arena, "Warning(%03u): %S: mismatched .debug$H hash count and type count: got 3 hashes for 2 types", LNK_Warning_GHash, debug_obj_path);
for (String8 i = output; i.size > 0 && !is_warning_found; ) { for (String8 i = g_output; i.size > 0 && !is_warning_found; ) {
String8 line = t_chop_line(&i); String8 line = t_chop_line(&i);
is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive); is_warning_found = str8_match(expected_line, line, StringMatchFlag_CaseInsensitive);
} }
@@ -6426,18 +6422,17 @@ t_radlink_validate_asan_out(String8 obj_name)
Temp scratch = scratch_begin(0,0); Temp scratch = scratch_begin(0,0);
B32 is_ok = 0; B32 is_ok = 0;
t_invoke_(t_radlink_path(), str8f(scratch.arena, "%S /debug:full", obj_name), max_U64, 0, 0); t_invoke_linkerf("%S /debug:full", obj_name);
if (g_last_exit_code != 0) { goto exit; } if (g_last_exit_code != 0) { goto exit; }
String8 exe_path = t_make_file_path(scratch.arena, str8f(scratch.arena, "%S.exe", str8_chop_last_dot(obj_name))); String8 exe_path = t_make_file_path(scratch.arena, str8f(scratch.arena, "%S.exe", str8_chop_last_dot(obj_name)));
String8 out = {0};
char *old_path_cstr = getenv("PATH"); char *old_path_cstr = getenv("PATH");
String8List env = {0}; String8List env = {0};
str8_list_pushf(scratch.arena, &env, "PATH=%S;%S", str8_chop_last_slash(t_cl_path()), str8_cstring(old_path_cstr)); str8_list_pushf(scratch.arena, &env, "PATH=%S;%S", str8_chop_last_slash(t_cl_path()), str8_cstring(old_path_cstr));
t_invoke_env(exe_path, str8_zero(), env, max_U64, scratch.arena, &out); t_invoke_env(exe_path, str8_zero(), env, max_U64);
String8 s = out; String8 s = g_output;
String8 header = t_chop_line(&s); String8 header = t_chop_line(&s);
if ( ! str8_match(header, str8_lit("================================================================="), 0)) { if ( ! str8_match(header, str8_lit("================================================================="), 0)) {
@@ -6469,8 +6464,7 @@ TEST(infer_asan)
// /MD // /MD
{ {
T_Ok(t_write_file(str8_lit("main.c"), str8_cstring(program))); T_Ok(t_write_file(str8_lit("main.c"), str8_cstring(program)));
String8 cl_output = {0}; t_invoke_cl("/MD /fsanitize=address /Z7 /c /Fo:main_md.obj main.c");
t_invoke_(t_cl_path(), str8_lit("/MD /fsanitize=address /Z7 /c /Fo:main_md.obj main.c"), max_U64, arena, &cl_output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
T_Ok(t_radlink_validate_asan_out(str8_lit("main_md.obj"))); T_Ok(t_radlink_validate_asan_out(str8_lit("main_md.obj")));
} }
@@ -6478,8 +6472,7 @@ TEST(infer_asan)
// /MDd // /MDd
{ {
T_Ok(t_write_file(str8_lit("main.c"), str8_cstring(program))); T_Ok(t_write_file(str8_lit("main.c"), str8_cstring(program)));
String8 cl_output = {0}; t_invoke_cl("/MDd /fsanitize=address /Z7 /c /Fo:main_mdd.obj main.c");
t_invoke_(t_cl_path(), str8_lit("/MDd /fsanitize=address /Z7 /c /Fo:main_mdd.obj main.c"), max_U64, arena, &cl_output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
T_Ok(t_radlink_validate_asan_out(str8_lit("main_mdd.obj"))); T_Ok(t_radlink_validate_asan_out(str8_lit("main_mdd.obj")));
} }
@@ -6487,8 +6480,7 @@ TEST(infer_asan)
// /MT // /MT
{ {
T_Ok(t_write_file(str8_lit("main.c"), str8_cstring(program))); T_Ok(t_write_file(str8_lit("main.c"), str8_cstring(program)));
String8 cl_output = {0}; t_invoke_cl("/MT /fsanitize=address /Z7 /c /Fo:main_mt.obj main.c");
t_invoke_(t_cl_path(), str8_lit("/MT /fsanitize=address /Z7 /c /Fo:main_mt.obj main.c"), max_U64, arena, &cl_output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
T_Ok(t_radlink_validate_asan_out(str8_lit("main_mt.obj"))); T_Ok(t_radlink_validate_asan_out(str8_lit("main_mt.obj")));
} }
@@ -6496,8 +6488,7 @@ TEST(infer_asan)
// /MTd // /MTd
{ {
T_Ok(t_write_file(str8_lit("main.c"), str8_cstring(program))); T_Ok(t_write_file(str8_lit("main.c"), str8_cstring(program)));
String8 cl_output = {0}; t_invoke_cl("/MT /fsanitize=address /Z7 /c /Fo:main_mtd.obj main.c");
t_invoke_(t_cl_path(), str8_lit("/MT /fsanitize=address /Z7 /c /Fo:main_mtd.obj main.c"), max_U64, arena, &cl_output);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
T_Ok(t_radlink_validate_asan_out(str8_lit("main_mtd.obj"))); T_Ok(t_radlink_validate_asan_out(str8_lit("main_mtd.obj")));
} }
@@ -6509,9 +6500,7 @@ TEST(infer_asan)
TEST(determ_test) TEST(determ_test)
{ {
// compile the test target (torture) // compile the test target (torture)
String8 cl_line = str8f(arena, "/fsanitize=address /c /Z7 /Fo:test.obj -I%S /Zc:preprocessor %S/torture/torture_main.c", t_src_path(), t_src_path()); t_invoke_cl("/fsanitize=address /c /Z7 /Fo:test.obj -I%S /Zc:preprocessor %S/torture/torture_main.c", t_src_path(), t_src_path());
String8 cl_out = {0};
t_invoke_(t_cl_path(), cl_line, max_U64, arena, &cl_out);
T_Ok(g_last_exit_code == 0); T_Ok(g_last_exit_code == 0);
U64 run_count = 25; U64 run_count = 25;
-5
View File
@@ -22,11 +22,6 @@ typedef enum
//////////////////////////////// ////////////////////////////////
#define t_invoke_linker_timeout(c, t) T_Ok(t_invoke(g_linker, c, t))
#define t_invoke_linker_timeoutf(t, f, ...) t_invoke_linker_timeout(push_str8f(arena, f, ##__VA_ARGS__), t)
#define t_invoke_linker(c) t_invoke_linker_timeout(c, max_U64)
#define t_invoke_linkerf(f, ...) t_invoke_linker(push_str8f(arena, f, ##__VA_ARGS__))
internal T_Linker t_id_linker(void); 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_text_section(COFF_ObjWriter *obj_writer, String8 data);