diff --git a/src/linker/lnk_debug_info.c b/src/linker/lnk_debug_info.c index 4d4c77e0..7176e96e 100644 --- a/src/linker/lnk_debug_info.c +++ b/src/linker/lnk_debug_info.c @@ -1692,7 +1692,7 @@ THREAD_POOL_TASK_FUNC(lnk_replace_type_names_with_hashes_lenient_task) B32 is_lambda = colon_pos != 0; if (is_lambda) { - U64 size = raddbg_snprintf(temp, sizeof(temp), "%llx", name_hash); + U64 size = raddbg_snprintf(temp, sizeof(temp), "%llx", (long long)name_hash); Assert(size < udt_info.name.size); Assert(size < udt_info.unique_name.size); MemoryCopy(udt_info.name.str, temp, size+1); @@ -1712,7 +1712,7 @@ THREAD_POOL_TASK_FUNC(lnk_replace_type_names_with_hashes_lenient_task) } else { // replace uniuqe type name with hash udt_info.unique_name.str = udt_info.name.str + udt_info.name.size + 1; - udt_info.unique_name.size = raddbg_snprintf((char *)udt_info.unique_name.str, udt_info.unique_name.size, "%llx", name_hash); + udt_info.unique_name.size = raddbg_snprintf((char *)udt_info.unique_name.str, udt_info.unique_name.size, "%llx", (long long)name_hash); // update leaf header U64 new_size = sizeof(CV_LeafKind) + @@ -1778,7 +1778,7 @@ THREAD_POOL_TASK_FUNC(lnk_replace_type_names_with_hashes_full_task) } // replace name with hash - udt_info.name.size = raddbg_snprintf((char *)udt_info.name.str, udt_info.name.size, "%llx", name_hash); + udt_info.name.size = raddbg_snprintf((char *)udt_info.name.str, udt_info.name.size, "%llx", (long long)name_hash); // parse struct size CV_NumericParsed dummy; diff --git a/src/torture/torture.h b/src/torture/torture.h index ca49aa43..ecc383a8 100644 --- a/src/torture/torture.h +++ b/src/torture/torture.h @@ -39,6 +39,14 @@ typedef struct String8 user_data; } T_Test; +typedef enum +{ + T_Compiler_Null, + T_Compiler_Cl, + T_Compiler_Clang, + T_Compiler_Gcc, +} T_Compiler; + typedef enum { T_Linker_Null, diff --git a/src/torture/torture_d2r.c b/src/torture/torture_d2r.c index 98b23d9f..af7e5a74 100644 --- a/src/torture/torture_d2r.c +++ b/src/torture/torture_d2r.c @@ -151,19 +151,21 @@ dw_writer_tag_end(writer); \ 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); \ -T_Ok(str8_match(str8_from_rdi_string_idx(rdi, type->built_in.name_string_idx), str8_lit(Stringify(r)), 0)); \ +#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); diff --git a/src/torture/torture_dbg.c b/src/torture/torture_dbg.c index 46a15c7a..d6f6aaa4 100644 --- a/src/torture/torture_dbg.c +++ b/src/torture/torture_dbg.c @@ -5,6 +5,8 @@ #define T_Dbg_DefaultTimeout TIMEOUT_SEC(5) +extern B32 g_stop_on_first_fail_or_crash; + //////////////////////////////// // IPC Controller @@ -40,10 +42,15 @@ t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, RD_IpcReply *rep // parse reply Arena *a = reply_arena ? reply_arena : scratch.arena; String8 reply_text = str8_copy(a, g_output); - //fprintf(stderr, "Reply: %.*s\n", str8_varg(reply_text)); RD_IpcReply reply = rd_ipc_mdesk_reply_from_string(a, reply_text); - if (rd_ipc_reply_is_ok(&reply) == 0) { goto exit; } - if (md_node_is_nil(reply.msg)) { goto exit; } + + //fprintf(stderr, "Reply: %.*s\n", str8_varg(reply_text)); + + // validate reply + if (reply.parse.msgs.worst_message_kind >= MD_MsgKind_Error) { goto exit; } + if (md_node_is_nil(reply.msg)) { goto exit; } + //if ( ! str8_matchi(reply.parse.root->first->string, cmd)) { Assert(0); goto exit; } + if (reply_arena && reply_out) { *reply_out = reply; } } @@ -70,21 +77,26 @@ internal B32 t_dbg_status(T_DbgStatus *status_out, U64 timeout_us) { Temp scratch = scratch_begin(0, 0); + + B32 is_ok = 0; T_DbgStatus status = {0}; - B32 is_ok = 0; // send status request RD_IpcReply reply = {0}; if ( ! t_dbg_send_cmd(str8_lit("status"), timeout_us, scratch.arena, &reply)) { goto exit; } // 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("running"), &status.running)) { AssertAlways(0); goto exit; } - if ( ! rd_ipc_parse_int(reply.msg, str8_lit("run_gen"), &status.run_gen)) { AssertAlways(0); goto exit; } - if ( ! rd_ipc_parse_int(reply.msg, str8_lit("ip"), &status.ip)) { AssertAlways(0); goto exit; } + if ( ! rd_ipc_parse_b32(reply.msg, str8_lit("ok"), &is_ok)) { fprintf(stderr, "ERROR: failed to parse reply member: ok\n"); Assert(0); goto exit; } + if ( ! rd_ipc_parse_b32(reply.msg, str8_lit("running"), &status.running)) { fprintf(stderr, "ERROR: failed to parse reply member: running\n"); Assert(0); goto exit; } + if ( ! rd_ipc_parse_int(reply.msg, str8_lit("run_gen"), &status.run_gen)) { fprintf(stderr, "ERROR: failed to parse reply member: run_gen\n"); Assert(0); goto exit; } + if ( ! rd_ipc_parse_int(reply.msg, str8_lit("ip"), &status.ip)) { fprintf(stderr, "ERROR: failed to parse reply member: ip\n"); Assert(0); goto exit; } if (status_out != 0) { *status_out = status; } exit:; + if ( ! is_ok && reply.parse.root) { + fprintf(stderr, "\tReply: %.*s\n", str8_varg(reply.parse.root->raw_string)); + } + scratch_end(scratch); return is_ok; } @@ -230,9 +242,9 @@ t_dbg_send_cmd_and_wait_stop(String8 cmd, U64 timeout_us) printf(" IP: %.*s\n", str8_varg(ip)); printf(" SP: %.*s\n", str8_varg(sp)); printf(" File Path: %.*s\n", str8_varg(loc.file_path)); - printf(" Line: %lld\n", loc.pt.line); - printf(" Column: %lld\n", loc.pt.column); - printf(" Run Gen: %llu\n", status.run_gen); + printf(" Line: %lld\n", (long long)loc.pt.line); + printf(" Column: %lld\n", (long long)loc.pt.column); + printf(" Run Gen: %llu\n", (unsigned long long)status.run_gen); printf(" Stop Cause: \"%.*s\"\n", str8_varg(last_stop.stop_cause)); fflush(stdout); } @@ -256,15 +268,15 @@ t_dbg_launch(String8 cmdline, U64 timeout_us) String8 user_path = t_make_file_path(scratch.arena, str8_lit("test.raddbg_user")); String8 project_path = t_make_file_path(scratch.arena, str8_lit("test.raddbg_project")); - cmdline = str8f(scratch.arena, "--gen_crash_dump --user:\"%S\" --project:\"%S\" %S", user_path, project_path, cmdline); + cmdline = str8f(scratch.arena, "%S --gen_crash_dump --user:\"%S\" --project:\"%S\" --logs:\"%S\" %S", t_raddbg_path(), user_path, project_path, g_wdir, cmdline); // launch debugger OS_ProcessLaunchParams launch_opts = { .path = g_wdir, .inherit_env = 1, + .consoleless = 1, .cmd_line = lnk_arg_list_parse_windows_rules(scratch.arena, cmdline), }; - str8_list_push_front(scratch.arena, &launch_opts.cmd_line, t_raddbg_path()); OS_Handle dbg_handle = os_process_launch(&launch_opts); if (os_handle_match(dbg_handle, os_handle_zero())) { AssertAlways(0 && "failed to launch debugger"); goto exit; } @@ -272,7 +284,7 @@ t_dbg_launch(String8 cmdline, U64 timeout_us) // cache debugger PID g_dbg_pid = GetProcessId((HANDLE)dbg_handle.u64[0]); #elif OS_LINUX - g_dbg_pid = safe_cast_u32(handle.u64[0]); + g_dbg_pid = (int)dbg_handle.u64[0]; #else # error NotImplemented #endif @@ -307,10 +319,10 @@ t_dbg_eval(Arena *arena, String8 expr, T_Eval *eval_out) B32 is_ok = t_dbg_send_cmd(cmd, T_Dbg_DefaultTimeout, arena, &reply); T_Eval e = {0}; - if ( ! rd_ipc_parse_string(reply.msg, str8_lit("expr"), &e.expr)) { AssertAlways(0); goto exit; } - if ( ! rd_ipc_parse_string(reply.msg, str8_lit("value"), &e.value)) { AssertAlways(0); goto exit; } - if ( ! rd_ipc_parse_string(reply.msg, str8_lit("type"), &e.type)) { AssertAlways(0); goto exit; } - if ( ! rd_ipc_parse_string(reply.msg, str8_lit("error"), &e.error)) { AssertAlways(0); goto exit; } + if ( ! rd_ipc_parse_string(reply.msg, str8_lit("expr"), &e.expr)) { fprintf(stderr, "ERROR: failed to parse reply member: expr\n"); Assert(0); goto exit; } + if ( ! rd_ipc_parse_string(reply.msg, str8_lit("value"), &e.value)) { fprintf(stderr, "ERROR: failed to parse reply member: value\n"); Assert(0); goto exit; } + if ( ! rd_ipc_parse_string(reply.msg, str8_lit("type"), &e.type)) { fprintf(stderr, "ERROR: failed to parse reply member: type\n"); Assert(0); goto exit; } + if ( ! rd_ipc_parse_string(reply.msg, str8_lit("error"), &e.error)) { fprintf(stderr, "ERROR: failed to parse reply member: error\n"); Assert(0); goto exit; } if (eval_out) { *eval_out = e; } exit:; @@ -511,7 +523,7 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source) p->file = file; hash_table_push_u64_raw(scratch.arena, ht, order, p); } else { - fprintf(stderr, "ERROR: duplicate order number %llu found on line %llu\n", order, p->line); + fprintf(stderr, "ERROR: duplicate order number %llu found on line %llu\n", (unsigned long long)order, (unsigned long long)p->line); } MD_Node *cmd_name = n->first; @@ -574,12 +586,24 @@ 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_Run: t_dbg_send_cmd(str8_lit("run"), timeout_us, 0, 0); break; case T_DbgScriptCmdKind_At: { - // map IP -> source location + // TODO: debugger does not populate eval cache with registers before first frame, + // so this racy, for now use lower level option +#if 0 U64 ip = u64_from_str8(t_dbg_value_from_exprf(scratch.arena, "reg:rip"), 10); if (ip == 0) { fprintf(stderr, "ERROR: invalid IP address: 0x%llx\n", (unsigned long long)ip); goto exit; } +#else + T_DbgStatus temp_status = {0}; + if ( ! t_dbg_status(&temp_status, T_Dbg_DefaultTimeout)) { + fprintf(stderr, "ERROR: failed to query IP\n"); + goto exit; + } + U64 ip = temp_status.ip; +#endif + + // map IP -> source location T_DbgSourceLocation loc = {0}; if (t_dbg_src_line(scratch.arena, ip, &loc, T_Dbg_DefaultTimeout) == 0) { fprintf(stderr, "ERROR: failed to map IP (0x%llx) to source location\n", (unsigned long long)ip); @@ -597,8 +621,9 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us) if (mismatch) { fprintf(stderr, "ERROR: location check did not pass:\n"); fprintf(stderr, " Script : %.*s\n", str8_varg(script->file_path)); - fprintf(stderr, " Expected: %.*s:%llu\n", str8_varg(program->file->path), at_line_u64); - fprintf(stderr, " Got : %.*s:%llu\n", str8_varg(loc.file_path), loc.pt.line); + fprintf(stderr, " Expected: %.*s:%llu\n", str8_varg(program->file->path), (unsigned long long)at_line_u64); + fprintf(stderr, " Got : %.*s:%llu\n", str8_varg(loc.file_path), (unsigned long long)loc.pt.line); + fprintf(stderr, " IP : 0x%llx\n", (unsigned long long)ip); goto exit; } } break; @@ -619,6 +644,11 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us) internal T_RunSig(dbg_script_runner) { + if ( ! os_file_path_exists(t_raddbg_path())) { + fprintf(stderr, "ERROR: failed to find debugger \"%.*s\"\n", str8_varg(t_raddbg_path())); + T_Ok(0); + } + // read source file String8 source = os_data_from_file_path(arena, user_data); if (source.size == 0) { @@ -632,18 +662,43 @@ T_RunSig(dbg_script_runner) // write source files to test folder for EachNode(file, T_DbgScriptFile, script.files.first) { if (os_write_data_to_file_path(file->path, file->source) == 0) { - fprintf(stderr, "ERROR: %.*s:%llu: failed to write: \"%.*s\"\n", str8_varg(user_data), file->line, str8_varg(file->path)); + fprintf(stderr, "ERROR: %.*s:%llu: failed to write: \"%.*s\"\n", str8_varg(user_data), (unsigned long long)file->line, str8_varg(file->path)); T_Ok(0); } } // run compilers - String8 compiler_path = t_cl_path(); for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Compile].first) { + T_Compiler compiler = directive->compile.compiler; + + // pick default compiler if none selected + if (compiler == T_Compiler_Null) { + switch (OperatingSystem_CURRENT) { + case OperatingSystem_Windows: { compiler = T_Compiler_Cl; } break; + case OperatingSystem_Linux: { compiler = T_Compiler_Clang; } break; + } + } + + // get compiler path + String8 compiler_path = {0}; + switch (compiler) { + case T_Compiler_Null: break; + case T_Compiler_Cl: compiler_path = t_cl_path(); break; + case T_Compiler_Clang: compiler_path = t_clang_path(); break; + case T_Compiler_Gcc: compiler_path = t_gcc_path(); break; + } + + // invoke compiler with arguments from directive if (t_invoke(compiler_path, directive->args, max_U64) == 0) { fprintf(stderr, "ERROR: failed to launch compiler: \"%.*s %.*s\"\n", str8_varg(compiler_path), str8_varg(directive->args)); T_Ok(0); } + if (g_last_exit_code) { + fprintf(stderr, "ERROR: %.*s\n", str8_varg(g_output)); + if (g_stop_on_first_fail_or_crash) { + T_Ok(0); + } + } } // run linkers @@ -697,12 +752,12 @@ t_dbg_register_script_tests(Arena *arena, String8 folder_path) String8 file_path = n->string; // test files may contain dots for extensions we have to escape them when creating output folder for a test - String8List file_name_parts = str8_split(scratch.arena, str8_skip_last_slash(file_path), ".", 1, 0); + String8List file_name_parts = str8_split_by_string_chars(scratch.arena, str8_skip_last_slash(file_path), str8_lit("."), 0); String8 file_name_escaped = str8_list_join(arena, &file_name_parts, &(StringJoin){.sep=str8_lit("-"), .post = str8_lit("\0") }); g_torture_tests[g_torture_test_count++] = (T_Test){ .group = T_Group, - .label = file_name_escaped.str, + .label = (char*)file_name_escaped.str, .r = t_dbg_script_runner, .user_data = str8_copy(arena, file_path), }; diff --git a/src/torture/torture_dbg.h b/src/torture/torture_dbg.h index 7267a98d..85366d9b 100644 --- a/src/torture/torture_dbg.h +++ b/src/torture/torture_dbg.h @@ -86,6 +86,11 @@ typedef struct T_DbgScriptDirective T_DbgScriptDirectiveKind kind; U64 line; String8 args; + union { + struct { + T_Compiler compiler; + } compile; + }; } T_DbgScriptDirective; typedef struct diff --git a/src/torture/torture_dwarf.c b/src/torture/torture_dwarf.c index 5d346368..46d2d185 100644 --- a/src/torture/torture_dwarf.c +++ b/src/torture/torture_dwarf.c @@ -120,7 +120,7 @@ dw_input_from_writer(Arena *arena, DW_Writer *writer) String8 raw_coff = coff_from_obj(scratch.arena, obj); t_write_file(str8_lit("dwarf.obj"), raw_coff); - t_invoke(str8_lit("radlink"), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full dwarf.obj"), max_U64); + t_invoke(t_radlink_path(), str8_lit("/subsystem:console /entry:entry /out:a.exe /debug:full dwarf.obj"), max_U64); delete_file_at_path(t_make_file_path(scratch.arena, str8_lit("a.pdb"))); String8 exe = t_read_file(arena, str8_lit("a.exe"));