mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-08 06:48:41 +00:00
improve error handling in the debugger harness (source file + line
number for error reports and allow scripts without header)
This commit is contained in:
committed by
Ryan Fleury
parent
56ee3bbf7b
commit
09afff7957
+52
-9
@@ -14,6 +14,7 @@ global String8 g_test_data;
|
|||||||
U64 g_torture_test_count;
|
U64 g_torture_test_count;
|
||||||
T_Test **g_torture_tests;
|
T_Test **g_torture_tests;
|
||||||
T_Test g_torture_tests_[0xffffff];
|
T_Test g_torture_tests_[0xffffff];
|
||||||
|
B32 g_is_first_print;
|
||||||
|
|
||||||
// invoke
|
// invoke
|
||||||
global U64 g_last_exit_code;
|
global U64 g_last_exit_code;
|
||||||
@@ -159,21 +160,27 @@ internal void
|
|||||||
t_run_caller(void *raw_ctx)
|
t_run_caller(void *raw_ctx)
|
||||||
{
|
{
|
||||||
Temp scratch = scratch_begin(0,0);
|
Temp scratch = scratch_begin(0,0);
|
||||||
|
|
||||||
|
g_is_first_print = 1;
|
||||||
|
|
||||||
T_RunCtx *ctx = raw_ctx;
|
T_RunCtx *ctx = raw_ctx;
|
||||||
String8List test_out = {0};
|
|
||||||
ctx->result.status = T_RunStatus_Pass;
|
ctx->result.status = T_RunStatus_Pass;
|
||||||
|
|
||||||
|
String8List test_out = {0};
|
||||||
ctx->run(scratch.arena, ctx->user_data, &ctx->result, &test_out);
|
ctx->run(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 == T_RunStatus_Fail || ctx->result.status == T_RunStatus_Crash) {
|
||||||
for EachNode(n, String8Node, test_out.first) {
|
for EachNode(n, String8Node, test_out.first) {
|
||||||
fprintf(stderr, "%.*s", str8_varg(n->string));
|
t_errorf("%S", n->string);
|
||||||
}
|
}
|
||||||
if (g_errors.size) {
|
if (g_errors.size) {
|
||||||
fprintf(stderr, "stderr: \"%.*s\"\n", str8_varg(g_errors));
|
t_errorf("stderr: \"%S\"\n", g_errors);
|
||||||
}
|
}
|
||||||
if (g_output.size) {
|
if (g_output.size) {
|
||||||
fprintf(stderr, "stdout: \"%.*s\"\n", str8_varg(g_output));
|
t_errorf("stdout: \"%S\"\n", g_output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,11 +486,7 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
};
|
};
|
||||||
str8_list_push_front(scratch.arena, &launch_opts.cmd_line, exe_path);
|
str8_list_push_front(scratch.arena, &launch_opts.cmd_line, exe_path);
|
||||||
|
|
||||||
if (g_verbose) {
|
String8 full_cmd_line = str8_list_join(scratch.arena, &launch_opts.cmd_line, &(StringJoin){ .sep = str8_lit(" ") });
|
||||||
String8 full_cmd_line = str8_list_join(scratch.arena, &launch_opts.cmd_line, &(StringJoin){ .sep = str8_lit(" ") });
|
|
||||||
fprintf(stdout, "Command Line: %.*s\n", str8_varg(full_cmd_line));
|
|
||||||
fprintf(stdout, "Working Dir: %.*s\n", str8_varg(g_wdir));
|
|
||||||
}
|
|
||||||
|
|
||||||
// invoke exe
|
// invoke exe
|
||||||
Process process_handle = process_launch(&launch_opts);
|
Process process_handle = process_launch(&launch_opts);
|
||||||
@@ -757,6 +760,12 @@ t_invoke_env(String8 exe_path, String8 cmdline, String8List env, U64 timeout_us)
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
// update output global
|
||||||
g_output = str8_list_join(g_output_arena, &stdout_parts, 0);
|
g_output = str8_list_join(g_output_arena, &stdout_parts, 0);
|
||||||
g_errors = str8_list_join(g_output_arena, &stderr_parts, 0);
|
g_errors = str8_list_join(g_output_arena, &stderr_parts, 0);
|
||||||
@@ -971,6 +980,40 @@ t_match_folders(String8 a, String8 b)
|
|||||||
return is_match;
|
return is_match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
t_infof(char *fmt, ...)
|
||||||
|
{
|
||||||
|
if (g_verbose) {
|
||||||
|
Temp scratch = scratch_begin(0,0);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
String8 result = push_str8fv(scratch.arena, fmt, args);
|
||||||
|
if (g_is_first_print) {
|
||||||
|
g_is_first_print = 0;
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%.*s", str8_varg(result));
|
||||||
|
va_end(args);
|
||||||
|
scratch_end(scratch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
t_errorf(char *fmt, ...)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(0,0);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
String8 result = push_str8fv(scratch.arena, fmt, args);
|
||||||
|
if (g_is_first_print) {
|
||||||
|
g_is_first_print = 0;
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
fprintf(stderr, "%.*s", str8_varg(result));
|
||||||
|
va_end(args);
|
||||||
|
scratch_end(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
t_entry_point(CmdLine *cmdline)
|
t_entry_point(CmdLine *cmdline)
|
||||||
{
|
{
|
||||||
|
|||||||
+16
-2
@@ -101,7 +101,7 @@ internal void t_break_if_debugger_present(void);
|
|||||||
#define ENDT_MS(x) TIMEOUT_US((x)*1000ull)
|
#define ENDT_MS(x) TIMEOUT_US((x)*1000ull)
|
||||||
#define ENDT_SEC(x) TIMEOUT_MS((x)*1000ull)
|
#define ENDT_SEC(x) TIMEOUT_MS((x)*1000ull)
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// output directory
|
// output directory
|
||||||
internal B32 t_write_file_list(String8 name, String8List data);
|
internal B32 t_write_file_list(String8 name, String8List data);
|
||||||
@@ -115,6 +115,7 @@ internal void t_run_caller(void *raw_ctx);
|
|||||||
internal void t_run_fail_handler(void *raw_ctx);
|
internal void t_run_fail_handler(void *raw_ctx);
|
||||||
internal T_RunResult t_run(T_Run run, String8 user_data);
|
internal T_RunResult t_run(T_Run run, String8 user_data);
|
||||||
|
|
||||||
|
// tools
|
||||||
internal String8 t_radbin_path(void);
|
internal String8 t_radbin_path(void);
|
||||||
internal String8 t_cl_path(void);
|
internal String8 t_cl_path(void);
|
||||||
internal String8 t_clang_path(void);
|
internal String8 t_clang_path(void);
|
||||||
@@ -128,7 +129,20 @@ internal B32 t_invoke_cl(char *fmt, ...);
|
|||||||
internal B32 t_invoke_linkerf(char *fmt, ...);
|
internal B32 t_invoke_linkerf(char *fmt, ...);
|
||||||
internal B32 t_invoke_radbin(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(t_radlink_path(), c, t))
|
#define t_invoke_linker_timeout(c, t) T_Ok(t_invoke(t_radlink_path(), c, t))
|
||||||
#define t_invoke_linker_timeoutf(t, f, ...) t_invoke_linker_timeout(push_str8f(arena, f, ##__VA_ARGS__), 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_linker(c) t_invoke_linker_timeout(c, max_U64)
|
||||||
|
|
||||||
|
internal void t_kill_all(String8 pattern);
|
||||||
|
|
||||||
|
internal String8 t_chop_line(String8 *string);
|
||||||
|
internal B32 t_match_line(String8 *output, String8 expected_line);
|
||||||
|
internal B32 t_match_linef(String8 *output, char *fmt, ...);
|
||||||
|
|
||||||
|
// files helper
|
||||||
|
internal String8List t_file_paths_from_dir(Arena *arena, String8 dir);
|
||||||
|
|
||||||
|
// printer
|
||||||
|
internal void t_infof(char *fmt, ...);
|
||||||
|
internal void t_errorf(char *fmt, ...);
|
||||||
|
|
||||||
|
|||||||
+123
-92
@@ -86,15 +86,15 @@ t_dbg_status(T_DbgStatus *status_out, U64 timeout_us)
|
|||||||
if ( ! t_dbg_send_cmd(str8_lit("status"), timeout_us, scratch.arena, &reply)) { goto exit; }
|
if ( ! t_dbg_send_cmd(str8_lit("status"), timeout_us, scratch.arena, &reply)) { goto exit; }
|
||||||
|
|
||||||
// parse reply
|
// parse reply
|
||||||
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("ok"), &is_ok)) { t_errorf("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_b32(reply.msg, str8_lit("running"), &status.running)) { t_errorf("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("run_gen"), &status.run_gen)) { t_errorf("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 ( ! rd_ipc_parse_int(reply.msg, str8_lit("ip"), &status.ip)) { t_errorf("ERROR: failed to parse reply member: ip\n"); Assert(0); goto exit; }
|
||||||
if (status_out != 0) { *status_out = status; }
|
if (status_out != 0) { *status_out = status; }
|
||||||
|
|
||||||
exit:;
|
exit:;
|
||||||
if ( ! is_ok && reply.parse.root) {
|
if ( ! is_ok && reply.parse.root) {
|
||||||
fprintf(stderr, "\tReply: %.*s\n", str8_varg(reply.parse.root->raw_string));
|
t_errorf("\tReply: %S\n", reply.parse.root->raw_string);
|
||||||
}
|
}
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
return is_ok;
|
return is_ok;
|
||||||
@@ -319,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);
|
B32 is_ok = t_dbg_send_cmd(cmd, T_Dbg_DefaultTimeout, arena, &reply);
|
||||||
|
|
||||||
T_Eval e = {0};
|
T_Eval e = {0};
|
||||||
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("expr"), &e.expr)) { t_errorf("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("value"), &e.value)) { t_errorf("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("type"), &e.type)) { t_errorf("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 ( ! rd_ipc_parse_string(reply.msg, str8_lit("error"), &e.error)) { t_errorf("ERROR: failed to parse reply member: error\n"); Assert(0); goto exit; }
|
||||||
if (eval_out) { *eval_out = e; }
|
if (eval_out) { *eval_out = e; }
|
||||||
|
|
||||||
exit:;
|
exit:;
|
||||||
@@ -346,29 +346,44 @@ t_dbg_script_program_is_before(void *raw_a, void *raw_b)
|
|||||||
return t_dbg_script_program_compar(raw_a, raw_b) < 0;
|
return t_dbg_script_program_compar(raw_a, raw_b) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
t_string_from_dbg_script_cmd_kind(T_DbgScriptCmdKind v)
|
||||||
|
{
|
||||||
|
switch (v) {
|
||||||
|
case T_DbgScriptCmdKind_Null: return str8_zero();
|
||||||
|
case T_DbgScriptCmdKind_Breakpoint: return str8_lit("bp");
|
||||||
|
case T_DbgScriptCmdKind_ClearBreakpoints: return str8_lit("bp_clear");
|
||||||
|
case T_DbgScriptCmdKind_Run: return str8_lit("run");
|
||||||
|
case T_DbgScriptCmdKind_Halt: return str8_lit("halt");
|
||||||
|
case T_DbgScriptCmdKind_StepOver: return str8_lit("step_over");
|
||||||
|
case T_DbgScriptCmdKind_StepInto: return str8_lit("step_into");
|
||||||
|
case T_DbgScriptCmdKind_StepOut: return str8_lit("step_out");
|
||||||
|
case T_DbgScriptCmdKind_StepOverInst: return str8_lit("step_over_inst");
|
||||||
|
case T_DbgScriptCmdKind_StepIntoInst: return str8_lit("step_into_inst");
|
||||||
|
case T_DbgScriptCmdKind_StepOverLine: return str8_lit("step_over_line");
|
||||||
|
case T_DbgScriptCmdKind_StepIntoLine: return str8_lit("step_into_line");
|
||||||
|
case T_DbgScriptCmdKind_KillAll: return str8_lit("kill_all");
|
||||||
|
case T_DbgScriptCmdKind_At: return str8_lit("at");
|
||||||
|
case T_DbgScriptCmdKind_Eval: return str8_lit("eval");
|
||||||
|
default: InvalidPath;
|
||||||
|
}
|
||||||
|
return str8_zero();
|
||||||
|
}
|
||||||
|
|
||||||
internal T_DbgScriptCmdKind
|
internal T_DbgScriptCmdKind
|
||||||
t_dbg_script_cmd_kind_from_string(String8 cmd)
|
t_dbg_script_cmd_kind_from_string(String8 cmd)
|
||||||
{
|
{
|
||||||
if (str8_matchi(cmd, str8_lit("bp"))) { return T_DbgScriptCmdKind_Breakpoint; }
|
for EachIndex(i, T_DbgScriptCmdKind_Count) {
|
||||||
else if (str8_matchi(cmd, str8_lit("bp_clear"))) { return T_DbgScriptCmdKind_ClearBreakpoints; }
|
if (str8_matchi(t_string_from_dbg_script_cmd_kind(i), cmd)) { return i; }
|
||||||
else if (str8_matchi(cmd, str8_lit("run"))) { return T_DbgScriptCmdKind_Run; }
|
}
|
||||||
else if (str8_matchi(cmd, str8_lit("halt"))) { return T_DbgScriptCmdKind_Halt; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("step_over"))) { return T_DbgScriptCmdKind_StepOver; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("step_into"))) { return T_DbgScriptCmdKind_StepInto; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("step_out"))) { return T_DbgScriptCmdKind_StepOut; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("step_over_inst"))) { return T_DbgScriptCmdKind_StepOverInst; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("step_into_inst"))) { return T_DbgScriptCmdKind_StepIntoInst; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("step_over_line"))) { return T_DbgScriptCmdKind_StepOverLine; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("step_into_line"))) { return T_DbgScriptCmdKind_StepIntoLine; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("at"))) { return T_DbgScriptCmdKind_At; }
|
|
||||||
else if (str8_matchi(cmd, str8_lit("eval"))) { return T_DbgScriptCmdKind_Eval; }
|
|
||||||
return T_DbgScriptCmdKind_Null;
|
return T_DbgScriptCmdKind_Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal T_DbgScript
|
internal B32
|
||||||
t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source)
|
t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source, T_DbgScript *script_out)
|
||||||
{
|
{
|
||||||
Temp scratch = scratch_begin(&arena, 1);
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
B32 is_ok = 0;
|
||||||
|
|
||||||
T_DbgScript script = { .file_path = push_str8_copy(arena, file_path) };
|
T_DbgScript script = { .file_path = push_str8_copy(arena, file_path) };
|
||||||
|
|
||||||
@@ -409,43 +424,44 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source)
|
|||||||
{
|
{
|
||||||
MD_Node *test = script_parse.root->first;
|
MD_Node *test = script_parse.root->first;
|
||||||
AssertAlways( ! md_node_is_nil(test));
|
AssertAlways( ! md_node_is_nil(test));
|
||||||
AssertAlways(str8_matchi(test->string, str8_lit("test")));
|
if (str8_matchi(test->string, str8_lit("test"))) {
|
||||||
|
for MD_EachNode(n, test->first) {
|
||||||
|
OperatingSystem os = operating_system_from_string(n->string);
|
||||||
|
AssertAlways(os != OperatingSystem_Null);
|
||||||
|
|
||||||
String8 os_name = string_from_operating_system(OperatingSystem_CURRENT);
|
for MD_EachNode(field, n->first) {
|
||||||
|
T_DbgScriptDirectiveKind kind = T_DbgScriptDirectiveKind_Null;
|
||||||
|
if (str8_matchi(field->string, str8_lit("compile"))) { kind = T_DbgScriptDirectiveKind_Compile; }
|
||||||
|
else if (str8_matchi(field->string, str8_lit("link"))) { kind = T_DbgScriptDirectiveKind_Link; }
|
||||||
|
else if (str8_matchi(field->string, str8_lit("launch"))) { kind = T_DbgScriptDirectiveKind_Launch; }
|
||||||
|
AssertAlways(kind != T_DbgScriptDirectiveKind_Null);
|
||||||
|
|
||||||
for MD_EachNode(n, test->first) {
|
// syntax check
|
||||||
OperatingSystem os = operating_system_from_string(n->string);
|
AssertAlways( !md_node_is_nil(field->first));
|
||||||
AssertAlways(os != OperatingSystem_Null);
|
AssertAlways(md_node_is_nil(field->first->next));
|
||||||
|
Assert(field->first->flags & MD_NodeFlag_StringLiteral);
|
||||||
|
|
||||||
for MD_EachNode(field, n->first) {
|
// src_offset -> line
|
||||||
T_DbgScriptDirectiveKind kind = T_DbgScriptDirectiveKind_Null;
|
//
|
||||||
if (str8_matchi(field->string, str8_lit("compile"))) { kind = T_DbgScriptDirectiveKind_Compile; }
|
// TODO: super silly!! mdesk should export line numbers
|
||||||
else if (str8_matchi(field->string, str8_lit("link"))) { kind = T_DbgScriptDirectiveKind_Link; }
|
U64 line = 1;
|
||||||
else if (str8_matchi(field->string, str8_lit("launch"))) { kind = T_DbgScriptDirectiveKind_Launch; }
|
String8 text_before_src = str8_prefix(source, field->src_offset);
|
||||||
AssertAlways(kind != T_DbgScriptDirectiveKind_Null);
|
for EachIndex(idx, text_before_src.size) { line += (text_before_src.str[idx] == '\n'); }
|
||||||
|
|
||||||
// syntax check
|
T_DbgScriptDirective *n = push_array(arena, T_DbgScriptDirective, 1);
|
||||||
AssertAlways( !md_node_is_nil(field->first));
|
n->kind = kind;
|
||||||
AssertAlways(md_node_is_nil(field->first->next));
|
n->line = line;
|
||||||
Assert(field->first->flags & MD_NodeFlag_StringLiteral);
|
n->args = str8_copy(arena, field->first->string);
|
||||||
|
// TODO: expand % in compile: and link: to current source file name and esacpe with %%
|
||||||
|
|
||||||
// src_offset -> line
|
T_DbgScriptDirectiveList *list = &script.directives[os][kind];
|
||||||
//
|
SLLQueuePush(list->first, list->last, n);
|
||||||
// TODO: super silly!! mdesk should export line numbers
|
list->count += 1;
|
||||||
U64 line = 1;
|
}
|
||||||
String8 text_before_src = str8_prefix(source, field->src_offset);
|
|
||||||
for EachIndex(idx, text_before_src.size) { line += (text_before_src.str[idx] == '\n'); }
|
|
||||||
|
|
||||||
T_DbgScriptDirective *n = push_array(arena, T_DbgScriptDirective, 1);
|
|
||||||
n->kind = kind;
|
|
||||||
n->line = line;
|
|
||||||
n->args = str8_copy(arena, field->first->string);
|
|
||||||
// TODO: expand % in compile: and link: to current source file name and esacpe with %%
|
|
||||||
|
|
||||||
T_DbgScriptDirectiveList *list = &script.directives[os][kind];
|
|
||||||
SLLQueuePush(list->first, list->last, n);
|
|
||||||
list->count += 1;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
t_errorf("ERROR: %S: missing test header\n", file_path);
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,28 +539,28 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source)
|
|||||||
p->file = file;
|
p->file = file;
|
||||||
hash_table_push_u64_raw(scratch.arena, ht, order, p);
|
hash_table_push_u64_raw(scratch.arena, ht, order, p);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "ERROR: duplicate order number %llu found on line %llu\n", (unsigned long long)order, (unsigned long long)p->line);
|
t_errorf("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;
|
for MD_EachNode(cmd_n, n->first) {
|
||||||
MD_Node *cmd_arg = cmd_name->first;
|
// push new cmd
|
||||||
AssertAlways(!md_node_is_nil(cmd_name));
|
T_DbgScriptCmd *cmd = push_array(arena, T_DbgScriptCmd, 1);
|
||||||
|
cmd->kind = t_dbg_script_cmd_kind_from_string(cmd_n->string);
|
||||||
|
cmd->line = line;
|
||||||
|
Assert(cmd->kind != T_DbgScriptCmdKind_Null);
|
||||||
|
SLLQueuePush(p->first, p->last, cmd);
|
||||||
|
p->count += 1;
|
||||||
|
|
||||||
// push new cmd
|
// parse cmd args
|
||||||
T_DbgScriptCmd *cmd = push_array(arena, T_DbgScriptCmd, 1);
|
MD_Node *cmd_arg = cmd_n->first;
|
||||||
cmd->kind = t_dbg_script_cmd_kind_from_string(cmd_name->string);
|
if ( ! md_node_is_nil(cmd_arg)) {
|
||||||
AssertAlways(cmd->kind != T_DbgScriptCmdKind_Null);
|
if (cmd->kind == T_DbgScriptCmdKind_At) {
|
||||||
SLLQueuePush(p->first, p->first, cmd);
|
AssertAlways(try_s64_from_str8_c_rules(cmd_arg->string, &cmd->at.delta));
|
||||||
p->count += 1;
|
} else if (cmd->kind == T_DbgScriptCmdKind_Eval) {
|
||||||
|
NotImplemented;
|
||||||
// parse cmd args
|
} else if (cmd->kind == T_DbgScriptCmdKind_Breakpoint) {
|
||||||
if ( ! md_node_is_nil(cmd_arg)) {
|
NotImplemented;
|
||||||
if (cmd->kind == T_DbgScriptCmdKind_At) {
|
}
|
||||||
AssertAlways(try_s64_from_str8_c_rules(cmd_arg->string, &cmd->at.delta));
|
|
||||||
} else if (cmd->kind == T_DbgScriptCmdKind_Eval) {
|
|
||||||
NotImplemented;
|
|
||||||
} else if (cmd->kind == T_DbgScriptCmdKind_Breakpoint) {
|
|
||||||
NotImplemented;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -555,8 +571,13 @@ t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source)
|
|||||||
radsort(script.programs, script.program_count, t_dbg_script_program_is_before);
|
radsort(script.programs, script.program_count, t_dbg_script_program_is_before);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_ok = 1;
|
||||||
|
exit:;
|
||||||
|
if (script_out) {
|
||||||
|
*script_out = script;
|
||||||
|
}
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
return script;
|
return is_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal B32
|
internal B32
|
||||||
@@ -571,6 +592,8 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
|
|||||||
|
|
||||||
if (program->os == OperatingSystem_CURRENT) {
|
if (program->os == OperatingSystem_CURRENT) {
|
||||||
for EachNode(cmd, T_DbgScriptCmd, program->first) {
|
for EachNode(cmd, T_DbgScriptCmd, program->first) {
|
||||||
|
t_infof("[%llu] Command: %S:%llu %S\n", program->order, script->file_path, (unsigned long long)cmd->line, t_string_from_dbg_script_cmd_kind(cmd->kind));
|
||||||
|
|
||||||
switch (cmd->kind) {
|
switch (cmd->kind) {
|
||||||
case T_DbgScriptCmdKind_Null: break;
|
case T_DbgScriptCmdKind_Null: break;
|
||||||
case T_DbgScriptCmdKind_Halt: t_dbg_send_cmd_and_wait_stop(str8_lit("halt"), timeout_us); break; // NOTE: does not auto-magically select main thread on stop
|
case T_DbgScriptCmdKind_Halt: t_dbg_send_cmd_and_wait_stop(str8_lit("halt"), timeout_us); break; // NOTE: does not auto-magically select main thread on stop
|
||||||
@@ -597,7 +620,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
|
|||||||
#else
|
#else
|
||||||
T_DbgStatus temp_status = {0};
|
T_DbgStatus temp_status = {0};
|
||||||
if ( ! t_dbg_status(&temp_status, T_Dbg_DefaultTimeout)) {
|
if ( ! t_dbg_status(&temp_status, T_Dbg_DefaultTimeout)) {
|
||||||
fprintf(stderr, "ERROR: failed to query IP\n");
|
t_errorf("ERROR: %S:%llu: failed to query IP\n", script->file_path, (unsigned long long)cmd->line);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
U64 ip = temp_status.ip;
|
U64 ip = temp_status.ip;
|
||||||
@@ -606,7 +629,7 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
|
|||||||
// map IP -> source location
|
// map IP -> source location
|
||||||
T_DbgSourceLocation loc = {0};
|
T_DbgSourceLocation loc = {0};
|
||||||
if (t_dbg_src_line(scratch.arena, ip, &loc, T_Dbg_DefaultTimeout) == 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);
|
t_errorf("ERROR: %S:%llu: IP (0x%llx) does not map to a source line\n", script->file_path, (unsigned long long)cmd->line, (unsigned long long)ip);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,11 +642,10 @@ t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
|
|||||||
B32 mismatch = loc.pt.line != at_line_u64 ||
|
B32 mismatch = loc.pt.line != at_line_u64 ||
|
||||||
!str8_match(loc.file_path, program->file->path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive);
|
!str8_match(loc.file_path, program->file->path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive);
|
||||||
if (mismatch) {
|
if (mismatch) {
|
||||||
fprintf(stderr, "ERROR: location check did not pass:\n");
|
t_errorf("ERROR: %S:%llu: location check did not pass:\n", script->file_path, (unsigned long long)cmd->line);
|
||||||
fprintf(stderr, " Script : %.*s\n", str8_varg(script->file_path));
|
t_errorf(" Expected: %S:%llu\n", program->file->path, (unsigned long long)at_line_u64);
|
||||||
fprintf(stderr, " Expected: %.*s:%llu\n", str8_varg(program->file->path), (unsigned long long)at_line_u64);
|
t_errorf(" Got : %S:%llu\n", loc.file_path, (unsigned long long)loc.pt.line);
|
||||||
fprintf(stderr, " Got : %.*s:%llu\n", str8_varg(loc.file_path), (unsigned long long)loc.pt.line);
|
t_errorf(" IP : 0x%llx\n", (unsigned long long)ip);
|
||||||
fprintf(stderr, " IP : 0x%llx\n", (unsigned long long)ip);
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -645,24 +667,27 @@ internal
|
|||||||
T_RunSig(dbg_script_runner)
|
T_RunSig(dbg_script_runner)
|
||||||
{
|
{
|
||||||
if ( ! file_path_exists(t_raddbg_path())) {
|
if ( ! file_path_exists(t_raddbg_path())) {
|
||||||
fprintf(stderr, "ERROR: failed to find debugger \"%.*s\"\n", str8_varg(t_raddbg_path()));
|
t_errorf("ERROR: failed to find debugger \"%S\"\n", t_raddbg_path());
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// read source file
|
// read source file
|
||||||
String8 source = data_from_file_path(arena, user_data);
|
String8 source = data_from_file_path(arena, user_data);
|
||||||
if (source.size == 0) {
|
if (source.size == 0) {
|
||||||
fprintf(stderr, "ERROR: failed to read script: \"%.*s\"\n", str8_varg(user_data));
|
t_errorf("ERROR: failed to read script: \"%S\"\n", user_data);
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// source -> script
|
// source -> script
|
||||||
T_DbgScript script = t_dbg_script_from_source(arena, user_data, source);
|
T_DbgScript script = {0};
|
||||||
|
if ( ! t_dbg_script_from_source(arena, user_data, source, &script)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
// write source files to test folder
|
// write source files to test folder
|
||||||
for EachNode(file, T_DbgScriptFile, script.files.first) {
|
for EachNode(file, T_DbgScriptFile, script.files.first) {
|
||||||
if (write_data_to_file_path(file->path, file->source) == 0) {
|
if (write_data_to_file_path(file->path, file->source) == 0) {
|
||||||
fprintf(stderr, "ERROR: %.*s:%llu: failed to write: \"%.*s\"\n", str8_varg(user_data), (unsigned long long)file->line, str8_varg(file->path));
|
t_errorf("ERROR: %S:%llu: failed to write: \"%S\"\n", user_data, (unsigned long long)file->line, file->path);
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -695,11 +720,11 @@ T_RunSig(dbg_script_runner)
|
|||||||
// invoke compiler with arguments from directive
|
// invoke compiler with arguments from directive
|
||||||
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
||||||
if (t_invoke(compiler_path, expanded_args, max_U64) == 0) {
|
if (t_invoke(compiler_path, expanded_args, max_U64) == 0) {
|
||||||
fprintf(stderr, "ERROR: failed to launch compiler: \"%.*s %.*s\"\n", str8_varg(compiler_path), str8_varg(expanded_args));
|
t_errorf("ERROR: failed to launch compiler: \"%S %S\"\n", compiler_path, expanded_args);
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
if (g_last_exit_code) {
|
if (g_last_exit_code) {
|
||||||
fprintf(stderr, "ERROR: %.*s\n", str8_varg(g_output));
|
t_errorf("ERROR: %S:%llu: %S\n", script.file_path, (unsigned long long)directive->line, g_errors);
|
||||||
if (g_stop_on_first_fail_or_crash) {
|
if (g_stop_on_first_fail_or_crash) {
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
@@ -711,7 +736,11 @@ T_RunSig(dbg_script_runner)
|
|||||||
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Link].first) {
|
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Link].first) {
|
||||||
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
||||||
if (t_invoke(linker_path, expanded_args, max_U64) == 0) {
|
if (t_invoke(linker_path, expanded_args, max_U64) == 0) {
|
||||||
fprintf(stderr, "ERROR: failed to launch linker: \"%.*s %.*s\"\n", str8_varg(linker_path), str8_varg(expanded_args));
|
t_errorf("ERROR: failed to launch linker: \"%S %S\"\n", linker_path, expanded_args);
|
||||||
|
T_Ok(0);
|
||||||
|
}
|
||||||
|
if (g_last_exit_code != 0) {
|
||||||
|
t_errorf("ERROR: %S:%llu: %S\n", script.file_path, (unsigned long long)directive->line, g_errors);
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -721,14 +750,14 @@ T_RunSig(dbg_script_runner)
|
|||||||
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
String8 expanded_args = lnk_expand_env_vars_windows(arena, script_vars, directive->args);
|
||||||
String8 cmdl = str8f(arena, "--user:%S.raddbg_user %S", t_make_file_path(arena, str8_lit("temp")), expanded_args);
|
String8 cmdl = str8f(arena, "--user:%S.raddbg_user %S", t_make_file_path(arena, str8_lit("temp")), expanded_args);
|
||||||
if (t_dbg_launch(cmdl, T_Dbg_DefaultTimeout) == 0) {
|
if (t_dbg_launch(cmdl, T_Dbg_DefaultTimeout) == 0) {
|
||||||
fprintf(stderr, "ERROR: failed to launch debugger with command line \"%.*s %.*s\"; work dir \"%.*s\"\n", str8_varg(t_raddbg_path()), str8_varg(cmdl), str8_varg(g_wdir));
|
t_errorf("ERROR: failed to launch debugger with command line \"%S %S\"; work dir \"%S\"\n", t_raddbg_path(), cmdl, g_wdir);
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// debugger is ready -- now invoke script
|
// debugger is ready -- now invoke script
|
||||||
if (t_dbg_script_invoke(&script, T_Dbg_DefaultTimeout) == 0) {
|
if (t_dbg_script_invoke(&script, T_Dbg_DefaultTimeout) == 0) {
|
||||||
fprintf(stderr, "ERROR: %.*s: failed to run to completion\n", str8_varg(user_data));
|
t_errrof("ERROR: %S: failed to run to completion\n", user_data);
|
||||||
T_Ok(0);
|
T_Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -742,6 +771,8 @@ T_RunSig(dbg_script_runner)
|
|||||||
#else
|
#else
|
||||||
kill(g_dbg_pid, SIGKILL);
|
kill(g_dbg_pid, SIGKILL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
exit:;
|
||||||
g_dbg_pid = 0;
|
g_dbg_pid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -751,7 +782,7 @@ t_dbg_register_script_tests(Arena *arena, String8 folder_path)
|
|||||||
Temp scratch = scratch_begin(&arena, 1);
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
if ( ! folder_path_exists(folder_path)) {
|
if ( ! folder_path_exists(folder_path)) {
|
||||||
fprintf(stderr, "ERROR: this folder does not exists: %.*s\n", str8_varg(folder_path));
|
t_errof("ERROR: this folder does not exists: %S\n", folder_path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ typedef enum
|
|||||||
T_DbgScriptCmdKind_KillAll,
|
T_DbgScriptCmdKind_KillAll,
|
||||||
T_DbgScriptCmdKind_At,
|
T_DbgScriptCmdKind_At,
|
||||||
T_DbgScriptCmdKind_Eval,
|
T_DbgScriptCmdKind_Eval,
|
||||||
|
T_DbgScriptCmdKind_Count
|
||||||
} T_DbgScriptCmdKind;
|
} T_DbgScriptCmdKind;
|
||||||
|
|
||||||
typedef struct T_DbgScriptCmd
|
typedef struct T_DbgScriptCmd
|
||||||
@@ -107,6 +108,7 @@ typedef struct
|
|||||||
T_DbgScriptFileList files;
|
T_DbgScriptFileList files;
|
||||||
U64 program_count;
|
U64 program_count;
|
||||||
T_DbgScriptProgram **programs;
|
T_DbgScriptProgram **programs;
|
||||||
|
B32 skip;
|
||||||
} T_DbgScript;
|
} T_DbgScript;
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
|
|||||||
Reference in New Issue
Block a user