mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-08-14 17:28:07 +00:00
basic debugger stepping test run to completion
This commit is contained in:
committed by
Ryan Fleury
parent
58e6430955
commit
a645622b10
@@ -200,6 +200,7 @@ internal String8 backslashed_from_str8(Arena *arena, String8 string);
|
|||||||
#define str8_match_lit(a_lit, b, flags) str8_match(str8_lit(a_lit), (b), (flags))
|
#define str8_match_lit(a_lit, b, flags) str8_match(str8_lit(a_lit), (b), (flags))
|
||||||
#define str8_match_cstr(a_cstr, b, flags) str8_match(str8_cstring(a_cstr), (b), (flags))
|
#define str8_match_cstr(a_cstr, b, flags) str8_match(str8_cstring(a_cstr), (b), (flags))
|
||||||
internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags);
|
internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags);
|
||||||
|
#define str8_matchi(a, b) str8_match(a, b, StringMatchFlag_CaseInsensitive)
|
||||||
internal B32 str8_match_wildcard(String8 string, String8 pattern, StringMatchFlags flags);
|
internal B32 str8_match_wildcard(String8 string, String8 pattern, StringMatchFlags flags);
|
||||||
internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
|
internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
|
||||||
internal U64 str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
|
internal U64 str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
|
||||||
|
|||||||
+360
-1
@@ -9226,6 +9226,26 @@ rd_value_string_from_eval(Arena *arena, String8 filter, EV_StringParams *params,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
rd_value_string_from_eval2(Arena *arena, String8 filter, EV_StringParams *params, U64 cap, E_Eval eval)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
String8List strs = {0};
|
||||||
|
EV_StringIter *iter = ev_string_iter_begin(scratch.arena, eval, params);
|
||||||
|
for(String8 string = {0}; ev_string_iter_next(scratch.arena, iter, &string);)
|
||||||
|
{
|
||||||
|
if(strs.total_size + string.size > cap)
|
||||||
|
{
|
||||||
|
str8_list_push(scratch.arena, &strs, str8_lit("..."));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
str8_list_push(scratch.arena, &strs, string);
|
||||||
|
}
|
||||||
|
String8 result = str8_list_join(arena, &strs, 0);
|
||||||
|
scratch_end(scratch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Hover Eval
|
//~ rjf: Hover Eval
|
||||||
|
|
||||||
@@ -10281,6 +10301,269 @@ rd_next_view_cmd(RD_Cmd **cmd)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ IPC
|
||||||
|
|
||||||
|
internal RD_IpcReply
|
||||||
|
rd_ipc_mdesk_reply_from_string(Arena *arena, String8 string)
|
||||||
|
{
|
||||||
|
MD_ParseResult parse = md_parse_from_text(arena, str8_lit("ipc_reply"), string);
|
||||||
|
return (RD_IpcReply){ .parse = parse, .root = parse.root, parse.root->first };
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
rd_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out)
|
||||||
|
{
|
||||||
|
MD_Node *child = md_child_from_string(node, child_name, 0);
|
||||||
|
if (!md_node_is_nil(child) && !md_node_is_nil(child->first))
|
||||||
|
{
|
||||||
|
if (out) { *out = child->first->string; }
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
rd_ipc_parse_u32(MD_Node *node, String8 child_name, U32 *out)
|
||||||
|
{
|
||||||
|
String8 value = {0};
|
||||||
|
if (rd_ipc_parse_string(node, child_name, &value))
|
||||||
|
{
|
||||||
|
U64 v64 = 0;
|
||||||
|
if (try_u64_from_str8_c_rules(value, &v64))
|
||||||
|
{
|
||||||
|
if (out) { *out = safe_cast_u32(v64); }
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
rd_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out)
|
||||||
|
{
|
||||||
|
String8 value = {0};
|
||||||
|
if (rd_ipc_parse_string(node, child_name, &value))
|
||||||
|
{
|
||||||
|
U64 v64 = 0;
|
||||||
|
if (try_u64_from_str8_c_rules(value, &v64))
|
||||||
|
{
|
||||||
|
if (out) { MemoryCopy(out, &v64, out_size); }
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
rd_ipc_parse_b32(MD_Node *node, String8 child_name, B32 *out)
|
||||||
|
{
|
||||||
|
B32 is_ok = 0;
|
||||||
|
String8 s = {0};
|
||||||
|
U64 value = 0;
|
||||||
|
if (rd_ipc_parse_string(node, child_name, &s))
|
||||||
|
{
|
||||||
|
if (str8_matchi(s, str8_lit("true"))) { value = 1; is_ok = 1; }
|
||||||
|
else if (str8_matchi(s, str8_lit("false"))) { value = 0; is_ok = 1; }
|
||||||
|
else
|
||||||
|
{
|
||||||
|
is_ok = try_u64_from_str8_c_rules(s, &value);
|
||||||
|
value = !!value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_ok && out) { *out = value; }
|
||||||
|
return is_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
rd_ipc_reply_is_ok(RD_IpcReply *reply)
|
||||||
|
{
|
||||||
|
return reply->parse.msgs.worst_message_kind < MD_MsgKind_Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
rd_ipc_reply_push(Arena *arena, String8List *out, char *name, String8 value)
|
||||||
|
{
|
||||||
|
str8_list_pushf(arena, out, "%s: \"%S\"\n", name, escaped_from_raw_str8(arena, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
rd_ipc_reply_block_begin(Arena *arena, String8List *out, char *name)
|
||||||
|
{
|
||||||
|
str8_list_pushf(arena, out, "%s: {\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
rd_ipc_reply_block_end(Arena *arena, String8List *out)
|
||||||
|
{
|
||||||
|
str8_list_pushf(arena, out, "}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
rd_ipc_reply_push_u64(Arena *arena, String8List *out, char *name, U64 value)
|
||||||
|
{
|
||||||
|
str8_list_pushf(arena, out, "%s: %I64u\n", name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
rd_ipc_reply_push_str8(Arena *arena, String8List *out, char *name, String8 value)
|
||||||
|
{
|
||||||
|
str8_list_pushf(arena, out, "%s: \"%S\"\n", name, escaped_from_raw_str8(arena, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
rd_ipc_reply_push_b32(Arena *arena, String8List *out, char *name, B32 value)
|
||||||
|
{
|
||||||
|
rd_ipc_reply_push_u64(arena, out, name, !!value);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
rd_ipc_make_reply_ack(Arena *arena, String8 cmd, B32 ok)
|
||||||
|
{
|
||||||
|
String8List s = {0};
|
||||||
|
rd_ipc_reply_block_begin(arena, &s, "cmd_reply");
|
||||||
|
rd_ipc_reply_push_b32(arena, &s, "ok", ok);
|
||||||
|
rd_ipc_reply_push (arena, &s, "cmd", cmd);
|
||||||
|
rd_ipc_reply_block_end(arena, &s);
|
||||||
|
return str8_list_join(arena, &s, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
rd_ipc_make_reply_status(Arena *arena)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
String8List s = {0};
|
||||||
|
rd_ipc_reply_block_begin(arena, &s, "status");
|
||||||
|
rd_ipc_reply_push_b32 (arena, &s, "ok", 1);
|
||||||
|
rd_ipc_reply_push_b32 (arena, &s, "running", d_ctrl_targets_running());
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "run_gen", d_run_gen());
|
||||||
|
rd_ipc_reply_block_end(arena, &s);
|
||||||
|
scratch_end(scratch);
|
||||||
|
return str8_list_join(arena, &s, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
rd_ipc_make_reply_stop_event(Arena *arena)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
|
D_Event e = d_ctrl_last_stop_event();
|
||||||
|
|
||||||
|
String8List stop_cause_list = {0};
|
||||||
|
DR_FStrList stop_cause_fstr = rd_stop_explanation_fstrs_from_ctrl_event(scratch.arena, &e);
|
||||||
|
for EachNode (n, DR_FStrNode, stop_cause_fstr.first) { str8_list_push(scratch.arena, &stop_cause_list, n->v.string); }
|
||||||
|
String8 stop_cause = str8_list_join(arena, &stop_cause_list, &(StringJoin){.sep=str8_lit(" ")});
|
||||||
|
|
||||||
|
String8List s = {0};
|
||||||
|
rd_ipc_reply_block_begin(arena, &s, "stop_event");
|
||||||
|
rd_ipc_reply_push_b32 (arena, &s, "ok", 1);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "arch", e.arch);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "vaddr_min", e.vaddr_rng.min);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "vaddr_max", e.vaddr_rng.max);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "ip_vaddr", e.rip_vaddr);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "sp_base", e.stack_base);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "tls_root", e.tls_root);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "tls_index", e.tls_index);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "tls_offset", e.tls_offset);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "timestamp", e.timestamp);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "exception_code", e.exception_code);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "bp_flags", e.bp_flags);
|
||||||
|
rd_ipc_reply_push_str8(arena, &s, "string", e.string);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "target_os", e.target_os);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &s, "tls_model", e.tls_model);
|
||||||
|
rd_ipc_reply_push_str8(arena, &s, "stop_cause", stop_cause);
|
||||||
|
rd_ipc_reply_block_end(arena, &s);
|
||||||
|
|
||||||
|
String8 result = str8_list_join(arena, &s, 0);
|
||||||
|
scratch_end(scratch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
rd_ipc_make_reply_eval(Arena *arena, String8 expr, E_Eval eval, U64 cap)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
|
// gather value & type
|
||||||
|
EV_StringParams string_params =
|
||||||
|
{
|
||||||
|
.flags = EV_StringFlag_ReadOnlyDisplayRules|rd_state->eval_viz_base_string_flags,
|
||||||
|
.radix = 10,
|
||||||
|
};
|
||||||
|
String8 value_string = rd_value_string_from_eval2(scratch.arena, str8_zero(), &string_params, cap, eval);
|
||||||
|
String8 type_string = e_type_string_from_key(scratch.arena, eval.irtree.type_key);
|
||||||
|
|
||||||
|
String8List errors = {0};
|
||||||
|
for EachNode(msg, E_Msg, eval.msgs.first) { str8_list_push(scratch.arena, &errors, msg->text); }
|
||||||
|
String8 error_string = str8_list_join(scratch.arena, &errors, &(StringJoin){.sep = str8_lit("; ")});
|
||||||
|
|
||||||
|
// format reply
|
||||||
|
String8List reply = {0};
|
||||||
|
rd_ipc_reply_block_begin(arena, &reply, "eval");
|
||||||
|
rd_ipc_reply_push_b32 (arena, &reply, "ok", 1);
|
||||||
|
rd_ipc_reply_push (arena, &reply, "cmd", str8_lit("eval"));
|
||||||
|
rd_ipc_reply_push (arena, &reply, "expr", expr);
|
||||||
|
rd_ipc_reply_push (arena, &reply, "value", value_string);
|
||||||
|
rd_ipc_reply_push (arena, &reply, "type", type_string);
|
||||||
|
rd_ipc_reply_push (arena, &reply, "error", error_string);
|
||||||
|
rd_ipc_reply_block_end(arena, &reply);
|
||||||
|
String8 result = str8_list_join(arena, &reply, 0);
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
rd_ipc_make_reply_source_location_from_address(Arena *arena, U64 vaddr)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
|
D_Entity *process = d_entity_from_handle(&d_user_state->ctrl_entity_store->ctx, rd_base_regs()->process);
|
||||||
|
if(process == &d_entity_nil)
|
||||||
|
{
|
||||||
|
D_Entity *thread = d_entity_from_handle(&d_user_state->ctrl_entity_store->ctx, rd_base_regs()->thread);
|
||||||
|
process = d_entity_ancestor_from_kind(thread, D_EntityKind_Process);
|
||||||
|
}
|
||||||
|
|
||||||
|
D_Entity *module = &d_entity_nil;
|
||||||
|
DI_Key dbgi_key = {0};
|
||||||
|
U64 voff = 0;
|
||||||
|
D_LineList lines = {0};
|
||||||
|
if(process != &d_entity_nil)
|
||||||
|
{
|
||||||
|
module = d_module_from_process_vaddr(process, vaddr);
|
||||||
|
if(module != &d_entity_nil)
|
||||||
|
{
|
||||||
|
dbgi_key = d_dbgi_key_from_module(module);
|
||||||
|
voff = d_voff_from_vaddr(module, vaddr);
|
||||||
|
lines = d_lines_from_dbgi_key_voff(scratch.arena, dbgi_key, voff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
D_Line line = {0};
|
||||||
|
if(lines.first != 0)
|
||||||
|
{
|
||||||
|
line = lines.first->v;
|
||||||
|
}
|
||||||
|
|
||||||
|
B32 is_ok = (process != &d_entity_nil && module != &d_entity_nil && lines.first != 0);
|
||||||
|
|
||||||
|
String8List reply = {0};
|
||||||
|
rd_ipc_reply_block_begin(arena, &reply, "source_location_from_address");
|
||||||
|
rd_ipc_reply_push_b32 (arena, &reply, "ok", is_ok);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &reply, "vaddr", vaddr);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &reply, "voff", voff);
|
||||||
|
rd_ipc_reply_push_str8(arena, &reply, "file_path", line.file_path);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &reply, "line", line.pt.line);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &reply, "column", line.pt.column);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &reply, "voff_min", line.voff_range.min);
|
||||||
|
rd_ipc_reply_push_u64 (arena, &reply, "voff_max", line.voff_range.max);
|
||||||
|
rd_ipc_reply_block_end(arena, &reply);
|
||||||
|
String8 result = str8_list_join(arena, &reply, 0);
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Main Layer Top-Level Calls
|
//~ rjf: Main Layer Top-Level Calls
|
||||||
|
|
||||||
@@ -12563,7 +12846,83 @@ rd_frame(void)
|
|||||||
CmdLine msg_cmd_line = cmd_line_from_string_list(scratch.arena, msg_parts);
|
CmdLine msg_cmd_line = cmd_line_from_string_list(scratch.arena, msg_parts);
|
||||||
String8 cmd_kind_name = str8_list_first(&msg_cmd_line.inputs);
|
String8 cmd_kind_name = str8_list_first(&msg_cmd_line.inputs);
|
||||||
RD_CmdKindInfo *cmd_kind_info = rd_cmd_kind_info_from_string(cmd_kind_name);
|
RD_CmdKindInfo *cmd_kind_info = rd_cmd_kind_info_from_string(cmd_kind_name);
|
||||||
if(cmd_kind_info != &rd_nil_cmd_kind_info) RD_RegsScope()
|
U64 cmd_args_pos = str8_find_needle(msg, 0, cmd_kind_name, StringMatchFlag_CaseInsensitive);
|
||||||
|
String8 cmd_args = str8_skip_chop_whitespace(str8_skip(msg, cmd_args_pos + cmd_kind_name.size));
|
||||||
|
if(str8_match(cmd_kind_name, str8_lit("status"), 0))
|
||||||
|
{
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_status(rd_state->cmd_output_arena));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("stop_event"), 0))
|
||||||
|
{
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_stop_event(rd_state->cmd_output_arena));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("run"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_Run);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("run"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("halt"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_Halt);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("halt"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("kill_all"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_KillAll);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("kill_all"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("step_over"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_StepOver);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("step_over"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("step_into"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_StepInto);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("step_into"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("step_out"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_StepOut);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("step_out"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("step_over_inst"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_StepOverInst);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("step_over_inst"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("step_into_inst"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_StepIntoInst);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("step_into_inst"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("step_over_line"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_StepOverLine);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("step_over_line"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("step_into_line"), 0))
|
||||||
|
{
|
||||||
|
rd_cmd(RD_CmdKind_StepIntoLine);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_ack(rd_state->cmd_output_arena, str8_lit("step_into_line"), 1));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("eval"), 0))
|
||||||
|
{
|
||||||
|
U64 cap_end = str8_find_needle(cmd_args, 0, str8_lit(" "), 0);
|
||||||
|
String8 cap_str = str8_substr(cmd_args, r1u64(0, cap_end));
|
||||||
|
U64 cap = 0;
|
||||||
|
try_u64_from_str8_c_rules(cap_str, &cap);
|
||||||
|
String8 expr = str8_skip(cmd_args, cap_str.size);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_eval(rd_state->cmd_output_arena, expr, e_eval_from_string(expr), cap));
|
||||||
|
}
|
||||||
|
else if(str8_match(cmd_kind_name, str8_lit("source_location_from_address"), 0))
|
||||||
|
{
|
||||||
|
String8 vaddr_str = str8_skip_chop_whitespace(cmd_args);
|
||||||
|
U64 vaddr = 0;
|
||||||
|
try_u64_from_str8_c_rules(vaddr_str, &vaddr);
|
||||||
|
str8_list_push(rd_state->cmd_output_arena, &rd_state->cmd_outputs, rd_ipc_make_reply_source_location_from_address(rd_state->cmd_output_arena, vaddr));
|
||||||
|
}
|
||||||
|
else if(cmd_kind_info != &rd_nil_cmd_kind_info) RD_RegsScope()
|
||||||
{
|
{
|
||||||
for EachNonZeroEnumVal(RD_RegSlot, s)
|
for EachNonZeroEnumVal(RD_RegSlot, s)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -93,6 +93,17 @@ enum
|
|||||||
RD_CmdKindFlag_ListInTextRng = (1<<4),
|
RD_CmdKindFlag_ListInTextRng = (1<<4),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ IPC
|
||||||
|
|
||||||
|
typedef struct RD_IpcReply RD_IpcReply;
|
||||||
|
struct RD_IpcReply
|
||||||
|
{
|
||||||
|
MD_ParseResult parse;
|
||||||
|
MD_Node *root;
|
||||||
|
MD_Node *msg;
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Autocompletion Cursor Info Type
|
//~ rjf: Autocompletion Cursor Info Type
|
||||||
|
|
||||||
@@ -751,6 +762,7 @@ internal void rd_window_frame(void);
|
|||||||
//~ rjf: Eval Visualization
|
//~ rjf: Eval Visualization
|
||||||
|
|
||||||
internal String8 rd_value_string_from_eval(Arena *arena, String8 filter, EV_StringParams *params, FNT_Tag font, F32 font_size, F32 max_size, E_Eval eval);
|
internal String8 rd_value_string_from_eval(Arena *arena, String8 filter, EV_StringParams *params, FNT_Tag font, F32 font_size, F32 max_size, E_Eval eval);
|
||||||
|
internal String8 rd_value_string_from_eval2(Arena *arena, String8 filter, EV_StringParams *params, U64 cap, E_Eval eval);
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Hover Eval
|
//~ rjf: Hover Eval
|
||||||
@@ -836,6 +848,16 @@ internal void rd_push_cmd(String8 name, RD_Regs *regs);
|
|||||||
internal B32 rd_next_cmd(RD_Cmd **cmd);
|
internal B32 rd_next_cmd(RD_Cmd **cmd);
|
||||||
internal B32 rd_next_view_cmd(RD_Cmd **cmd);
|
internal B32 rd_next_view_cmd(RD_Cmd **cmd);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
//~ IPC
|
||||||
|
|
||||||
|
internal RD_IpcReply rd_ipc_mdesk_reply_from_string(Arena *arena, String8 string);
|
||||||
|
internal B32 rd_ipc_parse_string(MD_Node *node, String8 child_name, String8 *out);
|
||||||
|
internal B32 rd_ipc_parse_int_(MD_Node *node, String8 child_name, U64 out_size, void *out);
|
||||||
|
#define rd_ipc_parse_int(a, b, c) rd_ipc_parse_int_(a, b, sizeof(*c), c)
|
||||||
|
internal B32 rd_ipc_parse_b32(MD_Node *node, String8 child_name, B32 *out);
|
||||||
|
internal B32 rd_ipc_reply_is_ok(RD_IpcReply *reply);
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
//~ rjf: Main Layer Top-Level Calls
|
//~ rjf: Main Layer Top-Level Calls
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/// test: {
|
||||||
|
/// windows: {
|
||||||
|
/// compile: "/Od /Z7 /c main.c /Fo:main.obj"
|
||||||
|
/// compile: "/Od /Z7 /c foo.c /Fo:foo.obj"
|
||||||
|
/// link: "/debug:full /dll foo.obj /out:foo.dll /implib:foo.lib"
|
||||||
|
/// link: "/debug:full main.obj foo.lib /out:main.exe"
|
||||||
|
/// launch: "main.exe"
|
||||||
|
/// }
|
||||||
|
/// linux: {
|
||||||
|
/// compile: "-O0 -g main.c foo.c -o main"
|
||||||
|
/// launch: "./main"
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
|
||||||
+77
-40
@@ -45,6 +45,17 @@ t_id_linker(void)
|
|||||||
return T_Linker_Null;
|
return T_Linker_Null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
t_break_if_debugger_present(void)
|
||||||
|
{
|
||||||
|
#if OS_WINDOWS
|
||||||
|
if(IsDebuggerPresent())
|
||||||
|
{
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
internal B32
|
internal B32
|
||||||
t_write_file_list(String8 name, String8List data)
|
t_write_file_list(String8 name, String8List data)
|
||||||
{
|
{
|
||||||
@@ -136,7 +147,7 @@ t_run_caller(void *raw_ctx)
|
|||||||
T_RunCtx *ctx = raw_ctx;
|
T_RunCtx *ctx = raw_ctx;
|
||||||
String8List test_out = {0};
|
String8List test_out = {0};
|
||||||
ctx->result.status = T_RunStatus_Pass;
|
ctx->result.status = T_RunStatus_Pass;
|
||||||
ctx->run(scratch.arena, &ctx->result, &test_out);
|
ctx->run(scratch.arena, ctx->user_data, &ctx->result, &test_out);
|
||||||
if (ctx->result.status == T_RunStatus_Fail) {
|
if (ctx->result.status == T_RunStatus_Fail) {
|
||||||
for EachNode(n, String8Node, test_out.first) {
|
for EachNode(n, String8Node, test_out.first) {
|
||||||
fprintf(stderr, "%.*s", str8_varg(n->string));
|
fprintf(stderr, "%.*s", str8_varg(n->string));
|
||||||
@@ -155,10 +166,9 @@ t_run_fail_handler(void *raw_ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal T_RunResult
|
internal T_RunResult
|
||||||
t_run(T_Run run)
|
t_run(T_Run run, String8 user_data)
|
||||||
{
|
{
|
||||||
T_RunCtx ctx = { .run = run };
|
T_RunCtx ctx = { .run = run, .user_data = user_data };
|
||||||
|
|
||||||
B32 do_safe_call = 1;
|
B32 do_safe_call = 1;
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
if (IsDebuggerPresent()) {
|
if (IsDebuggerPresent()) {
|
||||||
@@ -263,6 +273,24 @@ t_radlink_path(void)
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
t_raddbg_path(void)
|
||||||
|
{
|
||||||
|
local_persist String8 path = {0};
|
||||||
|
if (path.size == 0) {
|
||||||
|
local_persist U8 buffer[4096];
|
||||||
|
ArenaParams params = { .reserve_size = sizeof(buffer), .commit_size = sizeof(buffer), .optional_backing_buffer = buffer };
|
||||||
|
Arena *arena = arena_alloc_(¶ms);
|
||||||
|
#if OS_WINDOWS
|
||||||
|
path = os_full_path_from_path(arena, str8_lit("raddbg.exe"));
|
||||||
|
#else
|
||||||
|
path = os_full_path_from_path(arena, str8_lit("raddbg"));
|
||||||
|
#endif
|
||||||
|
AssertAlways(path.size);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
internal String8
|
internal String8
|
||||||
t_cwd_path(void)
|
t_cwd_path(void)
|
||||||
{
|
{
|
||||||
@@ -384,6 +412,30 @@ t_invoke(String8 exe_path, String8 cmdline, U64 timeout)
|
|||||||
return t_invoke_(exe_path, cmdline, timeout, 0, 0);
|
return t_invoke_(exe_path, cmdline, timeout, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
t_kill_all(String8 pattern)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(0,0);
|
||||||
|
DMN_ProcessIter it = {0};
|
||||||
|
dmn_process_iter_begin(&it);
|
||||||
|
DMN_ProcessInfo info = {0};
|
||||||
|
while (dmn_process_iter_next(scratch.arena, &it, &info)) {
|
||||||
|
if (str8_match_wildcard(info.name, pattern, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive)) {
|
||||||
|
#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"); }
|
||||||
|
#elif OS_LINUX
|
||||||
|
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"); }
|
||||||
|
#else
|
||||||
|
# error NotImplemented
|
||||||
|
#endif
|
||||||
|
if (g_last_exit_code != 0) { fprintf(stderr, "ERROR: failed to kill %u\n", info.pid); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dmn_process_iter_end(&it);
|
||||||
|
scratch_end(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
internal String8
|
internal String8
|
||||||
t_chop_line(String8 *output)
|
t_chop_line(String8 *output)
|
||||||
{
|
{
|
||||||
@@ -422,14 +474,20 @@ t_match_linef(String8 *output, char *fmt, ...)
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal int
|
internal int
|
||||||
t_test_is_before(void *raw_a, void *raw_b)
|
t_test_compar(const void *raw_a, const void *raw_b)
|
||||||
{
|
{
|
||||||
T_Test *a = raw_a, *b = raw_b;
|
const T_Test *a = raw_a, *b = raw_b;
|
||||||
int cmp = str8_compar(str8_cstring(a->group), str8_cstring(b->group), 0);
|
int cmp = str8_compar(str8_cstring(a->group), str8_cstring(b->group), 0);
|
||||||
if (cmp == 0) {
|
if (cmp == 0) {
|
||||||
cmp = u64_compar(&a->decl_line, &b->decl_line);
|
cmp = u64_compar(&a->decl_line, &b->decl_line);
|
||||||
}
|
}
|
||||||
return cmp < 0;
|
return cmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int
|
||||||
|
t_test_is_before(void *raw_a, void *raw_b)
|
||||||
|
{
|
||||||
|
return t_test_compar(raw_a, raw_b) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal String8List
|
internal String8List
|
||||||
@@ -549,32 +607,9 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
abort_self(0);
|
abort_self(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// register debugger tests
|
||||||
#if 0
|
String8 test_folder_path = str8f(scratch.arena, "%S/torture/dbg_tests", t_src_path());
|
||||||
//
|
t_dbg_register_script_tests(scratch.arena, test_folder_path);
|
||||||
// config
|
|
||||||
//
|
|
||||||
{
|
|
||||||
DateTime start_time_uni = os_now_universal_time();
|
|
||||||
DateTime start_time_loc = os_local_time_from_universal(&start_time_uni);
|
|
||||||
PrintHeader("Config");
|
|
||||||
fprintf(stderr, " Build %s\n", BUILD_TITLE_STRING_LITERAL);
|
|
||||||
fprintf(stderr, " Start %.*s\n", str8_varg(string_from_date_time(scratch.arena, &start_time_loc)));
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, " Tools\n");
|
|
||||||
#if OS_WINDOWS
|
|
||||||
AssertAlways(t_cl_path().size && t_cl_version().size);
|
|
||||||
fprintf(stderr, " MSVC %.*s\n", str8_varg(t_cl_version()));
|
|
||||||
fprintf(stderr, " %.*s\n", str8_varg(t_cl_path()));
|
|
||||||
#endif
|
|
||||||
fprintf(stderr, " radlink %.*s\n", str8_varg(t_radlink_version()));
|
|
||||||
fprintf(stderr, " %.*s\n", str8_varg(t_radlink_path()));
|
|
||||||
fprintf(stderr, " radbin %.*s\n", str8_varg(t_radbin_version()));
|
|
||||||
fprintf(stderr, " %.*s\n", str8_varg(t_radbin_path()));
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle -list
|
// Handle -list
|
||||||
//
|
//
|
||||||
@@ -631,8 +666,12 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
}
|
}
|
||||||
if (target_opt) {
|
if (target_opt) {
|
||||||
HashTable *ht = hash_table_init(scratch.arena, g_torture_test_count*2);
|
HashTable *ht = hash_table_init(scratch.arena, g_torture_test_count*2);
|
||||||
if (target_opt->value_strings.node_count > 0) {
|
|
||||||
for EachNode(pattern_n, String8Node, target_opt->value_strings.first) {
|
String8List targets = target_opt->value_strings;
|
||||||
|
str8_list_concat_in_place(&targets, &cmdline->inputs);
|
||||||
|
|
||||||
|
if (targets.node_count > 0) {
|
||||||
|
for EachNode(pattern_n, String8Node, targets.first) {
|
||||||
B32 do_namespace = str8_find_needle(pattern_n->string, 0, str8_lit("::"), 0) < pattern_n->string.size;
|
B32 do_namespace = str8_find_needle(pattern_n->string, 0, str8_lit("::"), 0) < pattern_n->string.size;
|
||||||
for EachIndex(test_idx, g_torture_test_count) {
|
for EachIndex(test_idx, g_torture_test_count) {
|
||||||
String8 name = str8_cstring(g_torture_tests[test_idx].label);
|
String8 name = str8_cstring(g_torture_tests[test_idx].label);
|
||||||
@@ -794,10 +833,9 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// run test
|
// run test
|
||||||
U64 run_start_time = now_time_us();
|
U64 run_start_time = os_now_microseconds();
|
||||||
T_RunResult result = t_run(g_torture_tests[target_idx].r);
|
T_RunResult result = t_run(g_torture_tests[target_idx].r, g_torture_tests[target_idx].user_data);
|
||||||
U64 run_end_time = now_time_us();
|
U64 run_end_time = os_now_microseconds();
|
||||||
|
|
||||||
// print result
|
// print result
|
||||||
if (result.status == T_RunStatus_Pass) {
|
if (result.status == T_RunStatus_Pass) {
|
||||||
fprintf(stdout, "\x1b[32m" "%s" "\x1b[0m", t_string_from_result(result.status));
|
fprintf(stdout, "\x1b[32m" "%s" "\x1b[0m", t_string_from_result(result.status));
|
||||||
@@ -889,4 +927,3 @@ t_entry_point(CmdLine *cmdline)
|
|||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -20,12 +20,13 @@ typedef struct
|
|||||||
char *fail_cond;
|
char *fail_cond;
|
||||||
} T_RunResult;
|
} T_RunResult;
|
||||||
|
|
||||||
#define T_RunSig(name) void t_##name(Arena *arena, T_RunResult *result_out, String8List *test_out)
|
#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, T_RunResult *result_out, String8List *test_out);
|
typedef void (*T_Run)(Arena *arena, String8 user_data, T_RunResult *result_out, String8List *test_out);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
T_Run run;
|
T_Run run;
|
||||||
|
String8 user_data;
|
||||||
T_RunResult result;
|
T_RunResult result;
|
||||||
} T_RunCtx;
|
} T_RunCtx;
|
||||||
|
|
||||||
@@ -35,10 +36,14 @@ typedef struct
|
|||||||
char *label;
|
char *label;
|
||||||
int decl_line;
|
int decl_line;
|
||||||
T_Run r;
|
T_Run r;
|
||||||
|
String8 user_data;
|
||||||
} T_Test;
|
} T_Test;
|
||||||
|
|
||||||
extern U64 g_torture_test_count;
|
extern U64 g_torture_test_count;
|
||||||
extern T_Test g_torture_tests[0xffffff];
|
extern T_Test g_torture_tests[0xffffff];
|
||||||
|
extern U64 g_torture_running_test_idx;
|
||||||
|
|
||||||
|
internal void t_break_if_debugger_present(void);
|
||||||
|
|
||||||
#define T_AddTest(name, l, ...) \
|
#define T_AddTest(name, l, ...) \
|
||||||
T_RunSig(name); \
|
T_RunSig(name); \
|
||||||
@@ -61,7 +66,7 @@ extern T_Test g_torture_tests[0xffffff];
|
|||||||
TEST_(name) \
|
TEST_(name) \
|
||||||
T_RunSig(name)
|
T_RunSig(name)
|
||||||
|
|
||||||
#define T_Ok(c) do { if (!(c)) { result_out->fail_file = __FILE__; result_out->fail_line = __LINE__; result_out->fail_cond = Stringify(c); result_out->status = T_RunStatus_Fail; return; } } while(0)
|
#define T_Ok(c) do { if (!(c)) { result_out->fail_file = __FILE__; result_out->fail_line = __LINE__; result_out->fail_cond = Stringify(c); result_out->status = T_RunStatus_Fail; t_break_if_debugger_present(); return; } } while(0)
|
||||||
#define T_MatchLinef(out, ...) T_Ok(t_match_linef(out, __VA_ARGS__))
|
#define T_MatchLinef(out, ...) T_Ok(t_match_linef(out, __VA_ARGS__))
|
||||||
#define t_outf(...) str8_list_pushf(arena, test_out, ## __VA_ARGS__)
|
#define t_outf(...) str8_list_pushf(arena, test_out, ## __VA_ARGS__)
|
||||||
|
|
||||||
@@ -77,7 +82,7 @@ internal String8 t_make_file_path(Arena *arena, String8 name);
|
|||||||
// test runner
|
// test runner
|
||||||
internal void t_run_caller(void *raw_ctx);
|
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);
|
internal T_RunResult t_run(T_Run run, String8 user_data);
|
||||||
|
|
||||||
internal String8 t_radbin_path(void);
|
internal String8 t_radbin_path(void);
|
||||||
internal String8 t_cl_path(void);
|
internal String8 t_cl_path(void);
|
||||||
@@ -87,6 +92,5 @@ 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_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)
|
#define t_invoke_cl(f, ...) t_invoke_(t_cl_path(), str8f(scratch.arena, f, ## __VA_ARGS__), max_U64, 0, 0)
|
||||||
|
internal void t_kill_all(String8 pattern);
|
||||||
|
|||||||
@@ -0,0 +1,692 @@
|
|||||||
|
// Copyright (c) Epic Games Tools
|
||||||
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||||
|
|
||||||
|
#define T_Group "Dbg"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#define TIMEOUT_US(x) (x)
|
||||||
|
#define TIMEOUT_MS(x) TIMEOUT_US((x)*1000ull)
|
||||||
|
#define TIMEOUT_SEC(x) TIMEOUT_MS((x)*1000ull)
|
||||||
|
|
||||||
|
#define ENDT_US(x) (os_now_microseconds() + (x))
|
||||||
|
#define ENDT_MS(x) TIMEOUT_US((x)*1000ull)
|
||||||
|
#define ENDT_SEC(x) TIMEOUT_MS((x)*1000ull)
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// IPC Controller
|
||||||
|
|
||||||
|
internal OS_Handle g_dbg_handle;
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_send_cmd(String8 cmd, U64 timeout_us, Arena *reply_arena, RD_IpcReply *reply_out)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&reply_arena, 1);
|
||||||
|
B32 is_sent = 0;
|
||||||
|
|
||||||
|
#if OS_WINDOWS
|
||||||
|
U32 dbg_pid = GetProcessId((HANDLE)g_dbg_handle.u64[0]);
|
||||||
|
#elif OS_LINUX
|
||||||
|
U32 dbg_pid = safe_cast_u32(handle.u64[0]);
|
||||||
|
#else
|
||||||
|
# error NotImplemented
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// send command
|
||||||
|
String8 cmdline = str8f(scratch.arena, "--ipc --pid:%u %S", dbg_pid, cmd);
|
||||||
|
Arena *output_arena = reply_arena ? reply_arena : scratch.arena;
|
||||||
|
String8 output = {0};
|
||||||
|
if (t_invoke_(t_raddbg_path(), cmdline, timeout_us, output_arena, &output) == 0) { goto exit; }
|
||||||
|
|
||||||
|
B32 has_reply = 1;
|
||||||
|
|
||||||
|
char *no_reply_cmds[] = {
|
||||||
|
"add_breakpoint",
|
||||||
|
"add_function_breakpoint",
|
||||||
|
"add_address_breakpoint",
|
||||||
|
"clear_breakpoints",
|
||||||
|
};
|
||||||
|
for EachElement(i, no_reply_cmds) {
|
||||||
|
if (str8_match_wildcard(cmd, str8f(scratch.arena, "%s*", no_reply_cmds[i]), 0)) {
|
||||||
|
has_reply = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_reply) {
|
||||||
|
// parse reply
|
||||||
|
RD_IpcReply reply = rd_ipc_mdesk_reply_from_string(output_arena, output);
|
||||||
|
if (rd_ipc_reply_is_ok(&reply) == 0) { goto exit; }
|
||||||
|
if (md_node_is_nil(reply.msg)) { goto exit; }
|
||||||
|
if (reply_arena && reply_out) { *reply_out = reply; }
|
||||||
|
}
|
||||||
|
|
||||||
|
is_sent = 1;
|
||||||
|
exit:;
|
||||||
|
scratch_end(scratch);
|
||||||
|
return is_sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_send_cmdf(U64 timeout_us, Arena *reply_arena, RD_IpcReply *reply_out, char *fmt, ...)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&reply_arena, 1);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
String8 cmd = str8fv(scratch.arena, fmt, args);
|
||||||
|
B32 is_ok = t_dbg_send_cmd(cmd, timeout_us, reply_arena, reply_out);
|
||||||
|
va_end(args);
|
||||||
|
scratch_end(scratch);
|
||||||
|
return is_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_status(T_DbgStatus *status_out, U64 timeout_us)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(0, 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 (status_out != 0) { *status_out = status; }
|
||||||
|
|
||||||
|
exit:;
|
||||||
|
return is_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_stop_event(Arena *arena, T_DbgStopEvent *out, U64 timeout_us)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(0, 0);
|
||||||
|
|
||||||
|
T_DbgStopEvent v = {0};
|
||||||
|
B32 is_ok = 0;
|
||||||
|
|
||||||
|
// send status request
|
||||||
|
RD_IpcReply reply = {0};
|
||||||
|
if ( ! t_dbg_send_cmd(str8_lit("stop_event"), 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_int (reply.msg, str8_lit("arch"), &v.arch)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("vaddr_min"), &v.vaddr_min)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("vaddr_max"), &v.vaddr_max)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("ip_vaddr"), &v.ip_vaddr)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("sp_base"), &v.sp_base)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("tls_root"), &v.tls_root)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("tls_index"), &v.tls_index)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("tls_offset"), &v.tls_offset)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("timestamp"), &v.timestamp)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("exception_code"), &v.exception_code)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("bp_flags"), &v.bp_flags)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_string (reply.msg, str8_lit("string"), &v.string)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("target_os"), &v.target_os)) { 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 (out != 0) { *out = v; }
|
||||||
|
|
||||||
|
exit:;
|
||||||
|
return is_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgSourceLocation *loc_out, U64 timeout_us)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
|
B32 is_ok = 0;
|
||||||
|
|
||||||
|
RD_IpcReply reply = {0};
|
||||||
|
if(!t_dbg_send_cmdf(timeout_us, arena, &reply, "source_location_from_address 0x%llx", vaddr)) { goto exit; }
|
||||||
|
|
||||||
|
T_DbgSourceLocation loc = {0};
|
||||||
|
|
||||||
|
if ( ! rd_ipc_parse_b32 (reply.msg, str8_lit("ok"), &is_ok)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("vaddr"), &loc.vaddr)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("voff"), &loc.voff)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_string(reply.msg, str8_lit("file_path"), &loc.file_path)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("line"), &loc.pt.line)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("column"), &loc.pt.column)) { AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("voff_min"), &loc.voff_range.min)){ AssertAlways(0); goto exit; }
|
||||||
|
if ( ! rd_ipc_parse_int (reply.msg, str8_lit("voff_max"), &loc.voff_range.max)){ AssertAlways(0); goto exit; }
|
||||||
|
|
||||||
|
if (loc_out != 0) { *loc_out = loc; }
|
||||||
|
|
||||||
|
exit:;
|
||||||
|
scratch_end(scratch);
|
||||||
|
return is_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
t_dbg_value_from_expr(Arena *arena, String8 expr)
|
||||||
|
{
|
||||||
|
T_Eval eval = {0};
|
||||||
|
if ( ! t_dbg_eval(arena, expr, &eval)) { AssertAlways("failed on eval"); }
|
||||||
|
return eval.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String8
|
||||||
|
t_dbg_value_from_exprf(Arena *arena, char *fmt, ...)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
String8 expr = push_str8fv(scratch.arena, fmt, args);
|
||||||
|
String8 result = t_dbg_value_from_expr(arena, expr);
|
||||||
|
va_end(args);
|
||||||
|
scratch_end(scratch);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_send_cmd_and_wait_stop(String8 cmd, U64 timeout_us)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(0,0);
|
||||||
|
B32 is_stopped = 0;
|
||||||
|
|
||||||
|
// snapshot status
|
||||||
|
T_DbgStatus status_before = {0};
|
||||||
|
if (t_dbg_status(&status_before, max_U64) == 0) { Assert(0 && "failed to snapshot status"); goto exit; }
|
||||||
|
|
||||||
|
// send command
|
||||||
|
if (t_dbg_send_cmd(cmd, max_U64, 0, 0) == 0) { Assert(0 && "failed to the command"); goto exit; }
|
||||||
|
|
||||||
|
// wait for debugger to stop
|
||||||
|
U64 t = timeout_us;
|
||||||
|
do {
|
||||||
|
// query debugger status
|
||||||
|
U64 begint_us = os_now_microseconds();
|
||||||
|
T_DbgStatus status = {0};
|
||||||
|
if (t_dbg_status(&status, t) == 0) { Assert(0 && "failed to acquire debugger status"); goto exit; }
|
||||||
|
U64 endt_us = os_now_microseconds();
|
||||||
|
|
||||||
|
// did state change? -> break
|
||||||
|
if (!status.running && status.run_gen != status_before.run_gen) {
|
||||||
|
is_stopped = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
U64 dt_us = (endt_us - begint_us) + TIMEOUT_MS(10);
|
||||||
|
if (dt_us >= t) { break; }
|
||||||
|
t -= dt_us;
|
||||||
|
|
||||||
|
// "solve" the wait problem
|
||||||
|
os_sleep_milliseconds(10);
|
||||||
|
} while (t > 0);
|
||||||
|
|
||||||
|
//--- Status ---------------------
|
||||||
|
if (is_stopped) {
|
||||||
|
T_DbgStatus status = {0};
|
||||||
|
AssertAlways(t_dbg_status(&status, 0));
|
||||||
|
|
||||||
|
String8 process_id = str8_skip(str8_chop(t_dbg_value_from_exprf(scratch.arena, "query:current_process.id"), 2), 2);
|
||||||
|
String8 process_label = str8_chop(str8_skip(t_dbg_value_from_exprf(scratch.arena, "query:current_process.label"), 2), 2);
|
||||||
|
String8 process_active = t_dbg_value_from_exprf(scratch.arena, "query:current_process.active");
|
||||||
|
String8 thread_id = t_dbg_value_from_exprf(scratch.arena, "query:current_thread.id");
|
||||||
|
String8 thread_active = t_dbg_value_from_exprf(scratch.arena, "query:current_thread.active");
|
||||||
|
String8 thread_label = str8_skip(str8_chop(t_dbg_value_from_exprf(scratch.arena, "query:current_thread.label"), 2), 2);
|
||||||
|
String8 ip = t_dbg_value_from_exprf(scratch.arena, "hex(reg:rip)");
|
||||||
|
String8 sp = t_dbg_value_from_exprf(scratch.arena, "hex(reg:rsp)");
|
||||||
|
|
||||||
|
T_DbgStopEvent last_stop = {0};
|
||||||
|
AssertAlways(t_dbg_stop_event(scratch.arena, &last_stop, 0));
|
||||||
|
|
||||||
|
T_DbgSourceLocation loc = {0};
|
||||||
|
AssertAlways(t_dbg_src_line(scratch.arena, last_stop.ip_vaddr, &loc, 0));
|
||||||
|
|
||||||
|
printf("------------------------------------------------------------------------------------------------------------------------\n");
|
||||||
|
printf(" Process: %.*s [%.*s] (Active: %.*s)\n", str8_varg(process_id), str8_varg(process_label), str8_varg(process_active));
|
||||||
|
printf(" Thread: %.*s [%.*s] (Active: %.*s)\n", str8_varg(thread_id), str8_varg(thread_label), str8_varg(thread_active));
|
||||||
|
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(" Stop Cause: \"%.*s\"\n", str8_varg(last_stop.stop_cause));
|
||||||
|
}
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
|
exit:;
|
||||||
|
scratch_end(scratch);
|
||||||
|
return is_stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32 t_dbg_ping (U64 timeout_us) { return t_dbg_status(0, timeout_us); }
|
||||||
|
internal B32 t_dbg_bp_add_line(String8 file, U64 line) { return t_dbg_send_cmdf(0,0,0, "add_breakpoint \"%S\":%llu", file, line); }
|
||||||
|
internal B32 t_dbg_bp_add_func(String8 func_name) { return t_dbg_send_cmdf(0,0,0, "add_function_breakpoint %S", func_name); }
|
||||||
|
internal B32 t_dbg_bp_add_addr(U64 addr) { return t_dbg_send_cmdf(0,0,0, "add_address_breakpoint 0x%llx", addr); }
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_launch(String8 cmdline, U64 timeout_us)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(0, 0);
|
||||||
|
B32 dbg_ready = 0;
|
||||||
|
|
||||||
|
String8 user_path = t_make_file_path(scratch.arena, str8_lit("test.raddbg_user"));
|
||||||
|
cmdline = str8f(scratch.arena, "--user:\"%S\" %S", user_path, cmdline);
|
||||||
|
|
||||||
|
// launch debugger
|
||||||
|
OS_ProcessLaunchParams launch_opts = {
|
||||||
|
.path = g_wdir,
|
||||||
|
.inherit_env = 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());
|
||||||
|
g_dbg_handle = os_process_launch(&launch_opts);
|
||||||
|
if (os_handle_match(g_dbg_handle, os_handle_zero())) { AssertAlways(0 && "failed to launch debugger"); goto exit; }
|
||||||
|
os_process_join(g_dbg_handle, 0, 0);
|
||||||
|
|
||||||
|
// now wait for debugger to init
|
||||||
|
U64 t = timeout_us;
|
||||||
|
do {
|
||||||
|
// time the ping
|
||||||
|
U64 ping_begint_us = os_now_microseconds();
|
||||||
|
dbg_ready = t_dbg_ping(t);
|
||||||
|
if (dbg_ready) { break; }
|
||||||
|
U64 ping_endt_us = os_now_microseconds();
|
||||||
|
|
||||||
|
// dbg did not pong -> compute remaining timeout and loop back
|
||||||
|
U64 ping_dt_us = (ping_endt_us - ping_begint_us) + TIMEOUT_MS(10);
|
||||||
|
if (ping_dt_us >= t) { break; }
|
||||||
|
t -= ping_dt_us;
|
||||||
|
|
||||||
|
// "solve" the wait problem
|
||||||
|
os_sleep_milliseconds(10);
|
||||||
|
} while (t > 0);
|
||||||
|
|
||||||
|
exit:;
|
||||||
|
scratch_end(scratch);
|
||||||
|
return dbg_ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_eval(Arena *arena, String8 expr, T_Eval *eval_out)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
|
RD_IpcReply reply = {0};
|
||||||
|
String8 cmd = str8f(scratch.arena, "eval %llu %S", /* value char cap: */ 10000, expr);
|
||||||
|
B32 is_ok = t_dbg_send_cmd(cmd, 0, 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 (eval_out) { *eval_out = e; }
|
||||||
|
|
||||||
|
exit:;
|
||||||
|
scratch_end(scratch);
|
||||||
|
return is_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Dbg Script
|
||||||
|
|
||||||
|
force_inline int
|
||||||
|
t_dbg_script_program_compar(const void *raw_a, const void *raw_b)
|
||||||
|
{
|
||||||
|
T_DbgScriptProgram * const *a = raw_a, * const *b = raw_b;
|
||||||
|
return u64_compar(&(*a)->order, &(*b)->order);
|
||||||
|
}
|
||||||
|
|
||||||
|
force_inline int
|
||||||
|
t_dbg_script_program_is_before(void *raw_a, void *raw_b)
|
||||||
|
{
|
||||||
|
return t_dbg_script_program_compar(raw_a, raw_b) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal T_DbgScriptCmdKind
|
||||||
|
t_dbg_script_cmd_kind_from_string(String8 cmd)
|
||||||
|
{
|
||||||
|
if (str8_matchi(cmd, str8_lit("bp"))) { return T_DbgScriptCmdKind_Breakpoint; }
|
||||||
|
else if (str8_matchi(cmd, str8_lit("bp_clear"))) { return T_DbgScriptCmdKind_ClearBreakpoints; }
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal T_DbgScript
|
||||||
|
t_dbg_script_from_source(Arena *arena, String8 file_path, String8 source)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
|
||||||
|
T_DbgScript script = { .file_path = push_str8_copy(arena, file_path) };
|
||||||
|
|
||||||
|
// scrape MD comment tokens out of source while preserving original source offsets
|
||||||
|
MD_TokenArray script_tokens = {0};
|
||||||
|
{
|
||||||
|
MD_TokenizeResult source_tokens = md_tokenize_from_text(scratch.arena, source);
|
||||||
|
MD_TokenChunkList script_token_chunks = {0};
|
||||||
|
for EachIndex(token_idx, source_tokens.tokens.count) {
|
||||||
|
MD_Token token = source_tokens.tokens.v[token_idx];
|
||||||
|
if (token.flags & MD_TokenFlag_Comment) {
|
||||||
|
String8 token_string = str8_substr(source, token.range);
|
||||||
|
String8 comment = str8_skip_chop_whitespace(token_string);
|
||||||
|
String8 prefix = str8_lit("///");
|
||||||
|
if (str8_matchi(str8_prefix(comment, prefix.size), prefix)) {
|
||||||
|
String8 script_part = str8_skip(comment, prefix.size);
|
||||||
|
U64 script_part_base_off = (U64)(script_part.str - source.str);
|
||||||
|
MD_TokenizeResult script_part_tokenize = md_tokenize_from_text(scratch.arena, script_part);
|
||||||
|
for EachIndex(script_token_idx, script_part_tokenize.tokens.count) {
|
||||||
|
MD_Token script_token = script_part_tokenize.tokens.v[script_token_idx];
|
||||||
|
script_token.range.min += script_part_base_off;
|
||||||
|
script_token.range.max += script_part_base_off;
|
||||||
|
md_token_chunk_list_push(scratch.arena, &script_token_chunks, 4096, script_token);
|
||||||
|
}
|
||||||
|
MD_Token newline_token = md_token_make(r1u64(token.range.max, token.range.max), MD_TokenFlag_Newline);
|
||||||
|
md_token_chunk_list_push(scratch.arena, &script_token_chunks, 4096, newline_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
script_tokens = md_token_array_from_chunk_list(scratch.arena, &script_token_chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
// script tokens -> mdesk tree
|
||||||
|
MD_ParseResult script_parse = md_parse_from_text_tokens(scratch.arena, file_path, source, script_tokens);
|
||||||
|
AssertAlways(script_parse.msgs.worst_message_kind < MD_MsgKind_Error);
|
||||||
|
|
||||||
|
// test
|
||||||
|
{
|
||||||
|
MD_Node *test = script_parse.root->first;
|
||||||
|
AssertAlways( ! md_node_is_nil(test));
|
||||||
|
AssertAlways(str8_matchi(test->string, str8_lit("test")));
|
||||||
|
|
||||||
|
String8 os_name = string_from_operating_system(OperatingSystem_CURRENT);
|
||||||
|
|
||||||
|
for MD_EachNode(n, test->first) {
|
||||||
|
OperatingSystem os = operating_system_from_string(n->string);
|
||||||
|
AssertAlways(os != OperatingSystem_Null);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// syntax check
|
||||||
|
AssertAlways( !md_node_is_nil(field->first));
|
||||||
|
AssertAlways(md_node_is_nil(field->first->next));
|
||||||
|
Assert(field->first->flags & MD_NodeFlag_StringLiteral);
|
||||||
|
|
||||||
|
// src_offset -> line
|
||||||
|
//
|
||||||
|
// TODO: super silly!! mdesk should export line numbers
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// file
|
||||||
|
{
|
||||||
|
MD_Node *last_file = 0;
|
||||||
|
for MD_EachNode(n, script_parse.root->first->next) {
|
||||||
|
B32 is_end = (md_node_is_nil(n->next) && last_file != 0);
|
||||||
|
if (str8_matchi(n->string, str8_lit("file")) || is_end) {
|
||||||
|
if (!is_end) {
|
||||||
|
AssertAlways( ! md_node_is_nil(n->first));
|
||||||
|
AssertAlways(md_node_is_nil(n->first->next));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_file) {
|
||||||
|
// src_offset -> base_line
|
||||||
|
//
|
||||||
|
// TODO: super silly!! mdesk should export line numbers
|
||||||
|
U64 line = 1;
|
||||||
|
for EachIndex(idx, last_file->src_offset) { line += (source.str[idx] == '\n'); }
|
||||||
|
|
||||||
|
String8 sub_source = str8_substr(source, r1u64(last_file->src_offset, is_end ? source.size : n->src_offset));
|
||||||
|
U64 file_dir_end = str8_find_needle(sub_source, 0, str8_lit("\n"), 0);
|
||||||
|
U64 next_file_dir_begin = str8_find_needle_reverse(sub_source, 0, str8_lit("\n"), n->src_offset);
|
||||||
|
sub_source = str8_substr(sub_source, r1u64(file_dir_end + 1, next_file_dir_begin));
|
||||||
|
|
||||||
|
T_DbgScriptFile *file = push_array(arena, T_DbgScriptFile, 1);
|
||||||
|
file->path = t_make_file_path(arena, last_file->first->string);
|
||||||
|
file->source = sub_source;
|
||||||
|
file->line = line;
|
||||||
|
SLLQueuePush(script.files.first, script.files.last, file);
|
||||||
|
script.files.count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_file = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no file directives? assume script file as main source file
|
||||||
|
if (last_file == 0) {
|
||||||
|
T_DbgScriptFile *file = push_array(arena, T_DbgScriptFile, 1);
|
||||||
|
file->path = file_path;
|
||||||
|
file->source = source;
|
||||||
|
SLLQueuePush(script.files.first, script.files.last, file);
|
||||||
|
script.files.count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// programs
|
||||||
|
{
|
||||||
|
HashTable *ht = hash_table_init(scratch.arena, 256); // <U64, T_DbgScriptProgram>
|
||||||
|
for MD_EachNode(n, script_parse.root->first->next) {
|
||||||
|
U64 order = 0;
|
||||||
|
if (try_u64_from_str8_c_rules(n->string, &order)) {
|
||||||
|
T_DbgScriptFile *file = 0;
|
||||||
|
for (file = script.files.first; file != 0; file = file->next) {
|
||||||
|
if (file->source.str <= n->string.str && n->string.str < (file->source.str + file->source.size)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssertAlways(file != 0);
|
||||||
|
|
||||||
|
// src_offset -> line
|
||||||
|
//
|
||||||
|
// TODO: super silly!! mdesk should export line numbers
|
||||||
|
U64 line = 1;
|
||||||
|
for EachIndex(i, n->src_offset) { line += (source.str[i] == '\n'); }
|
||||||
|
|
||||||
|
T_DbgScriptProgram *p = hash_table_search_u64_raw(ht, order);
|
||||||
|
if (p == 0) {
|
||||||
|
p = push_array(arena, T_DbgScriptProgram, 1);
|
||||||
|
p->line = line;
|
||||||
|
p->order = order;
|
||||||
|
p->os = OperatingSystem_CURRENT;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
MD_Node *cmd_name = n->first;
|
||||||
|
MD_Node *cmd_arg = cmd_name->first;
|
||||||
|
AssertAlways(!md_node_is_nil(cmd_name));
|
||||||
|
|
||||||
|
// push new cmd
|
||||||
|
T_DbgScriptCmd *cmd = push_array(arena, T_DbgScriptCmd, 1);
|
||||||
|
cmd->kind = t_dbg_script_cmd_kind_from_string(cmd_name->string);
|
||||||
|
AssertAlways(cmd->kind != T_DbgScriptCmdKind_Null);
|
||||||
|
SLLQueuePush(p->first, p->first, cmd);
|
||||||
|
p->count += 1;
|
||||||
|
|
||||||
|
// parse cmd args
|
||||||
|
if ( ! md_node_is_nil(cmd_arg)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script.program_count = ht->count;
|
||||||
|
script.programs = values_from_hash_table_raw(arena, ht);
|
||||||
|
radsort(script.programs, script.program_count, t_dbg_script_program_is_before);
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal B32
|
||||||
|
t_dbg_script_invoke(T_DbgScript *script, U64 timeout_us)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(0,0);
|
||||||
|
|
||||||
|
B32 is_ok = 0;
|
||||||
|
|
||||||
|
for EachIndex(i, script->program_count) {
|
||||||
|
T_DbgScriptProgram *program = script->programs[i];
|
||||||
|
|
||||||
|
if (program->os == OperatingSystem_CURRENT) {
|
||||||
|
for EachNode(cmd, T_DbgScriptCmd, program->first) {
|
||||||
|
switch (cmd->kind) {
|
||||||
|
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_StepOver: t_dbg_send_cmd_and_wait_stop(str8_lit("step_over"), timeout_us); break;
|
||||||
|
case T_DbgScriptCmdKind_StepInto: t_dbg_send_cmd_and_wait_stop(str8_lit("step_into"), timeout_us); break;
|
||||||
|
case T_DbgScriptCmdKind_StepOut: t_dbg_send_cmd_and_wait_stop(str8_lit("step_out"), timeout_us); break;
|
||||||
|
case T_DbgScriptCmdKind_StepOverInst: t_dbg_send_cmd_and_wait_stop(str8_lit("step_over_inst"), timeout_us); break;
|
||||||
|
case T_DbgScriptCmdKind_StepIntoInst: t_dbg_send_cmd_and_wait_stop(str8_lit("step_into_inst"), timeout_us); break;
|
||||||
|
case T_DbgScriptCmdKind_StepOverLine: t_dbg_send_cmd_and_wait_stop(str8_lit("step_over_line"), timeout_us); break;
|
||||||
|
case T_DbgScriptCmdKind_StepIntoLine: t_dbg_send_cmd_and_wait_stop(str8_lit("step_into_line"), timeout_us); break;
|
||||||
|
case T_DbgScriptCmdKind_KillAll: t_dbg_send_cmd_and_wait_stop(str8_lit("kill_all"), timeout_us); break;
|
||||||
|
case T_DbgScriptCmdKind_Breakpoint: NotImplemented; 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_At: {
|
||||||
|
// map IP -> source location
|
||||||
|
U64 ip = u64_from_str8(t_dbg_value_from_exprf(scratch.arena, "reg:rip"), 10);
|
||||||
|
T_DbgSourceLocation loc = {0};
|
||||||
|
AssertAlways(t_dbg_src_line(scratch.arena, ip, &loc, TIMEOUT_SEC(5)));
|
||||||
|
|
||||||
|
// compute line where debugger must be
|
||||||
|
S64 at_line_s64 = (S64)(program->line - program->file->line) + cmd->at.delta;
|
||||||
|
U64 at_line_u64 = at_line_s64 >= 0 ? (U64)at_line_s64 : 0;
|
||||||
|
AssertAlways(at_line_u64 > 0);
|
||||||
|
|
||||||
|
// match expected vs current debugger locations
|
||||||
|
B32 mismatch = loc.pt.line != at_line_u64 ||
|
||||||
|
!str8_match(loc.file_path, program->file->path, StringMatchFlag_CaseInsensitive|StringMatchFlag_SlashInsensitive);
|
||||||
|
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);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case T_DbgScriptCmdKind_Eval: NotImplemented; break;
|
||||||
|
default: { InvalidPath; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is_ok = (i+1 == script->program_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:;
|
||||||
|
scratch_end(scratch);
|
||||||
|
return is_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal
|
||||||
|
T_RunSig(dbg_script_runner)
|
||||||
|
{
|
||||||
|
t_kill_all(str8_lit("*raddbg*"));
|
||||||
|
|
||||||
|
// read source file
|
||||||
|
String8 source = os_data_from_file_path(arena, user_data);
|
||||||
|
T_Ok(source.size != 0);
|
||||||
|
|
||||||
|
// source -> script
|
||||||
|
T_DbgScript script = t_dbg_script_from_source(arena, user_data, source);
|
||||||
|
|
||||||
|
// write source files to test folder
|
||||||
|
for EachNode(file, T_DbgScriptFile, script.files.first) { T_Ok(os_write_data_to_file_path(file->path, file->source)); }
|
||||||
|
|
||||||
|
String8 compiler_path = t_cl_path();
|
||||||
|
String8 linker_path = t_radlink_path();
|
||||||
|
|
||||||
|
// run compilers
|
||||||
|
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Compile].first) {
|
||||||
|
T_Ok(t_invoke(compiler_path, directive->args, max_U64));
|
||||||
|
}
|
||||||
|
|
||||||
|
// run linkers
|
||||||
|
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Link].first) {
|
||||||
|
T_Ok(t_invoke(linker_path, directive->args, max_U64));
|
||||||
|
}
|
||||||
|
|
||||||
|
// launch targets
|
||||||
|
for EachNode(directive, T_DbgScriptDirective, script.directives[OperatingSystem_CURRENT][T_DbgScriptDirectiveKind_Launch].first) {
|
||||||
|
String8 cmdl = str8f(arena, "--user:%S.raddbg_user %S ", t_make_file_path(arena, str8_lit("temp")), directive->args);
|
||||||
|
T_Ok(t_dbg_launch(cmdl, ENDT_SEC(10)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// debugger is ready, now call script
|
||||||
|
t_dbg_script_invoke(&script, ENDT_SEC(60*3));
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
os_process_kill(g_dbg_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
t_dbg_register_script_tests(Arena *arena, String8 folder_path)
|
||||||
|
{
|
||||||
|
Temp scratch = scratch_begin(&arena, 1);
|
||||||
|
AssertAlways(os_folder_path_exists(folder_path));
|
||||||
|
|
||||||
|
// gather file paths in a folder
|
||||||
|
String8List paths = t_file_paths_from_dir(arena, folder_path);
|
||||||
|
|
||||||
|
// register a test for each file
|
||||||
|
for EachNode(n, String8Node, paths.first) {
|
||||||
|
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);
|
||||||
|
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,
|
||||||
|
.r = t_dbg_script_runner,
|
||||||
|
.user_data = str8_copy(arena, file_path),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
scratch_end(scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef T_Group
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
// Copyright (c) Epic Games Tools
|
||||||
|
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Dbg Script
|
||||||
|
|
||||||
|
typedef struct T_DbgScriptFile
|
||||||
|
{
|
||||||
|
struct T_DbgScriptFile *next;
|
||||||
|
String8 path;
|
||||||
|
String8 source;
|
||||||
|
U64 line;
|
||||||
|
} T_DbgScriptFile;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
U64 count;
|
||||||
|
T_DbgScriptFile *first;
|
||||||
|
T_DbgScriptFile *last;
|
||||||
|
} T_DbgScriptFileList;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
T_DbgScriptCmdKind_Null,
|
||||||
|
T_DbgScriptCmdKind_Breakpoint,
|
||||||
|
T_DbgScriptCmdKind_ClearBreakpoints,
|
||||||
|
T_DbgScriptCmdKind_Run,
|
||||||
|
T_DbgScriptCmdKind_Halt,
|
||||||
|
T_DbgScriptCmdKind_StepOver,
|
||||||
|
T_DbgScriptCmdKind_StepInto,
|
||||||
|
T_DbgScriptCmdKind_StepOut,
|
||||||
|
T_DbgScriptCmdKind_StepOverInst,
|
||||||
|
T_DbgScriptCmdKind_StepIntoInst,
|
||||||
|
T_DbgScriptCmdKind_StepOverLine,
|
||||||
|
T_DbgScriptCmdKind_StepIntoLine,
|
||||||
|
T_DbgScriptCmdKind_KillAll,
|
||||||
|
T_DbgScriptCmdKind_At,
|
||||||
|
T_DbgScriptCmdKind_Eval,
|
||||||
|
} T_DbgScriptCmdKind;
|
||||||
|
|
||||||
|
typedef struct T_DbgScriptCmd
|
||||||
|
{
|
||||||
|
struct T_DbgScriptCmd *next;
|
||||||
|
T_DbgScriptCmdKind kind;
|
||||||
|
U64 line;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
S64 delta;
|
||||||
|
} at;
|
||||||
|
};
|
||||||
|
} T_DbgScriptCmd;
|
||||||
|
|
||||||
|
typedef struct T_DbgScriptProgram
|
||||||
|
{
|
||||||
|
U64 line;
|
||||||
|
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 enum
|
||||||
|
{
|
||||||
|
T_DbgScriptDirectiveKind_Null,
|
||||||
|
T_DbgScriptDirectiveKind_Compile,
|
||||||
|
T_DbgScriptDirectiveKind_Link,
|
||||||
|
T_DbgScriptDirectiveKind_Launch,
|
||||||
|
T_DbgScriptDirectiveKind_Count,
|
||||||
|
} T_DbgScriptDirectiveKind;
|
||||||
|
|
||||||
|
typedef struct T_DbgScriptDirective
|
||||||
|
{
|
||||||
|
struct T_DbgScriptDirective *next;
|
||||||
|
T_DbgScriptDirectiveKind kind;
|
||||||
|
U64 line;
|
||||||
|
String8 args;
|
||||||
|
} 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;
|
||||||
|
} T_DbgScript;
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// IPC Controller
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
B32 running;
|
||||||
|
U64 run_gen;
|
||||||
|
} T_DbgStatus;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
U64 vaddr;
|
||||||
|
U64 voff;
|
||||||
|
String8 file_path;
|
||||||
|
TxtPt pt;
|
||||||
|
Rng1U64 voff_range;
|
||||||
|
} T_DbgSourceLocation;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
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_DbgStopEvent;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
String8 expr;
|
||||||
|
String8 value;
|
||||||
|
String8 type;
|
||||||
|
String8 error;
|
||||||
|
} T_Eval;
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Dbg Script
|
||||||
|
|
||||||
|
internal void t_dbg_register_script_tests(Arena *arena, String8 folder_path);
|
||||||
|
internal B32 t_dbg_run_script(Arena *arena, T_DbgScript *script, U64 timeout_us);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// Dbg Tester
|
||||||
|
|
||||||
|
internal B32 t_dbg_ping (U64 timeout_us);
|
||||||
|
internal B32 t_dbg_run (U64 timeout_us);
|
||||||
|
internal B32 t_dbg_kill_all (U64 timeout_us);
|
||||||
|
internal B32 t_dbg_halt (U64 timeout_us);
|
||||||
|
internal B32 t_dbg_step_over (U64 timeout_us);
|
||||||
|
internal B32 t_dbg_step_into (U64 timeout_us);
|
||||||
|
internal B32 t_dbg_step_out (U64 timeout_us);
|
||||||
|
internal B32 t_dbg_step_over_inst(U64 timeout_us);
|
||||||
|
internal B32 t_dbg_step_into_inst(U64 timeout_us);
|
||||||
|
internal B32 t_dbg_step_over_line(U64 timeout_us);
|
||||||
|
internal B32 t_dbg_step_into_line(U64 timeout_us);
|
||||||
|
internal B32 t_dbg_launch(String8 cmdline, U64 timeout_us);
|
||||||
|
internal B32 t_dbg_eval(Arena *arena, String8 expr, T_Eval *eval_out);
|
||||||
|
// TODO: need a source location query in eval
|
||||||
|
internal B32 t_dbg_src_line(Arena *arena, U64 vaddr, T_DbgSourceLocation *loc_out, U64 timeout_us);
|
||||||
|
|
||||||
@@ -94,6 +94,7 @@
|
|||||||
#include "linker/lnk_debug_helper.h"
|
#include "linker/lnk_debug_helper.h"
|
||||||
#include "torture.h"
|
#include "torture.h"
|
||||||
#include "torture_radlink.h"
|
#include "torture_radlink.h"
|
||||||
|
#include "torture_dbg.h"
|
||||||
|
|
||||||
#include "base/base_inc.c"
|
#include "base/base_inc.c"
|
||||||
#include "x64/x64.c"
|
#include "x64/x64.c"
|
||||||
@@ -172,6 +173,7 @@
|
|||||||
#include "torture_d2r.c"
|
#include "torture_d2r.c"
|
||||||
#include "torture_p2r.c"
|
#include "torture_p2r.c"
|
||||||
#include "torture_eval.c"
|
#include "torture_eval.c"
|
||||||
|
#include "torture_dbg.c"
|
||||||
|
|
||||||
internal B32 frame(void) { return 0; }
|
internal B32 frame(void) { return 0; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user