mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-10-09 17:35:41 +00:00
sketch out stepping/eval exemplar testing; step-to-exit first pass
This commit is contained in:
@@ -62,7 +62,7 @@ str8_lit_comp(""),
|
||||
str8_lit_comp(""),
|
||||
};
|
||||
|
||||
RD_VocabInfo rd_vocab_info_table[364] =
|
||||
RD_VocabInfo rd_vocab_info_table[365] =
|
||||
{
|
||||
{str8_lit_comp("type_view"), str8_lit_comp("type_views"), str8_lit_comp("Type View"), str8_lit_comp("Type Views"), RD_IconKind_Binoculars},
|
||||
{str8_lit_comp("file_path_map"), str8_lit_comp("file_path_maps"), str8_lit_comp("File Path Map"), str8_lit_comp("File Path Maps"), RD_IconKind_FileOutline},
|
||||
@@ -188,6 +188,7 @@ RD_VocabInfo rd_vocab_info_table[364] =
|
||||
{str8_lit_comp("step_into_line"), str8_lit_comp(""), str8_lit_comp("Step Into (Line)"), str8_lit_comp(""), RD_IconKind_StepInto},
|
||||
{str8_lit_comp("step_over_line"), str8_lit_comp(""), str8_lit_comp("Step Over (Line)"), str8_lit_comp(""), RD_IconKind_StepOver},
|
||||
{str8_lit_comp("step_out"), str8_lit_comp(""), str8_lit_comp("Step Out"), str8_lit_comp(""), RD_IconKind_StepOut},
|
||||
{str8_lit_comp("step_to_exit"), str8_lit_comp(""), str8_lit_comp("Step To Exit"), str8_lit_comp(""), RD_IconKind_StepOut},
|
||||
{str8_lit_comp("halt"), str8_lit_comp(""), str8_lit_comp("Halt"), str8_lit_comp(""), RD_IconKind_Pause},
|
||||
{str8_lit_comp("soft_halt_refresh"), str8_lit_comp(""), str8_lit_comp("Soft Halt Refresh"), str8_lit_comp(""), RD_IconKind_Refresh},
|
||||
{str8_lit_comp("set_thread_ip"), str8_lit_comp(""), str8_lit_comp("Set Thread IP"), str8_lit_comp(""), RD_IconKind_Null},
|
||||
@@ -473,7 +474,7 @@ RD_NameSchemaInfo rd_name_schema_info_table[39] =
|
||||
{str8_lit_comp("thread"), 0, str8_lit_comp("x:{'label':code_string, 'id':u64, @no_expand 'active':bool, 'call_stack':set}")},
|
||||
};
|
||||
|
||||
String8 rd_reg_slot_code_name_table[49] =
|
||||
String8 rd_reg_slot_code_name_table[50] =
|
||||
{
|
||||
{0},
|
||||
str8_lit_comp("machine"),
|
||||
@@ -520,13 +521,14 @@ str8_lit_comp("all_windows"),
|
||||
str8_lit_comp("non_graphical"),
|
||||
str8_lit_comp("prefer_new_tab"),
|
||||
str8_lit_comp("activate_with_single_click"),
|
||||
str8_lit_comp("disable_addresses"),
|
||||
str8_lit_comp("dir2"),
|
||||
str8_lit_comp("string"),
|
||||
str8_lit_comp("cmd_name"),
|
||||
str8_lit_comp("wm_event"),
|
||||
};
|
||||
|
||||
Rng1U64 rd_reg_slot_range_table[49] =
|
||||
Rng1U64 rd_reg_slot_range_table[50] =
|
||||
{
|
||||
{0},
|
||||
{OffsetOf(RD_Regs, machine), OffsetOf(RD_Regs, machine) + sizeof(D_Handle)},
|
||||
@@ -573,13 +575,14 @@ Rng1U64 rd_reg_slot_range_table[49] =
|
||||
{OffsetOf(RD_Regs, non_graphical), OffsetOf(RD_Regs, non_graphical) + sizeof(B32)},
|
||||
{OffsetOf(RD_Regs, prefer_new_tab), OffsetOf(RD_Regs, prefer_new_tab) + sizeof(B32)},
|
||||
{OffsetOf(RD_Regs, activate_with_single_click), OffsetOf(RD_Regs, activate_with_single_click) + sizeof(B32)},
|
||||
{OffsetOf(RD_Regs, disable_addresses), OffsetOf(RD_Regs, disable_addresses) + sizeof(B32)},
|
||||
{OffsetOf(RD_Regs, dir2), OffsetOf(RD_Regs, dir2) + sizeof(Dir2)},
|
||||
{OffsetOf(RD_Regs, string), OffsetOf(RD_Regs, string) + sizeof(String8)},
|
||||
{OffsetOf(RD_Regs, cmd_name), OffsetOf(RD_Regs, cmd_name) + sizeof(String8)},
|
||||
{OffsetOf(RD_Regs, wm_event), OffsetOf(RD_Regs, wm_event) + sizeof(WM_Event *)},
|
||||
};
|
||||
|
||||
RD_CmdKindInfo rd_cmd_kind_info_table[252] =
|
||||
RD_CmdKindInfo rd_cmd_kind_info_table[253] =
|
||||
{
|
||||
{0},
|
||||
{ str8_lit_comp("launch_and_run"), str8_lit_comp("Starts debugging a new instance of a target, then runs."), str8_lit_comp("launch,start,run,target"), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*1)|(RD_QueryFlag_Required*1), RD_RegSlot_Cfg, str8_lit_comp("query:targets"), str8_lit_comp(""), D_EntityKind_Null}},
|
||||
@@ -593,6 +596,7 @@ RD_CmdKindInfo rd_cmd_kind_info_table[252] =
|
||||
{ str8_lit_comp("step_into_line"), str8_lit_comp("Performs a step that goes into calls, at the source code line level."), str8_lit_comp("step,thread"), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}},
|
||||
{ str8_lit_comp("step_over_line"), str8_lit_comp("Performs a step that skips calls, at the source code line level."), str8_lit_comp("step,thread"), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}},
|
||||
{ str8_lit_comp("step_out"), str8_lit_comp("Runs to the first instruction after the current scope is exited."), str8_lit_comp(""), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}},
|
||||
{ str8_lit_comp("step_to_exit"), str8_lit_comp("Runs to the first encountered instruction which will exit the current scope."), str8_lit_comp(""), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}},
|
||||
{ str8_lit_comp("halt"), str8_lit_comp("Halts all attached processes."), str8_lit_comp("pause"), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*1)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}},
|
||||
{ str8_lit_comp("soft_halt_refresh"), str8_lit_comp("Interrupts all attached processes to collect data, and then resumes them."), str8_lit_comp(""), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*0)|(RD_CmdKindFlag_ListInIPCDocs*0)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*0)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*0)|(RD_QueryFlag_Required*0), RD_RegSlot_Null, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}},
|
||||
{ str8_lit_comp("set_thread_ip"), str8_lit_comp("Sets the specified thread's instruction pointer at the specified address."), str8_lit_comp(""), str8_lit_comp(""), (RD_CmdKindFlag_ListInUI*0)|(RD_CmdKindFlag_ListInIPCDocs*1)|(RD_CmdKindFlag_ListInTextPt*0)|(RD_CmdKindFlag_ListInTextRng*0), {(RD_QueryFlag_AllowFiles*0)|(RD_QueryFlag_AllowFolders*0)|(RD_QueryFlag_CodeInput*1)|(RD_QueryFlag_KeepOldInput*0)|(RD_QueryFlag_SelectOldInput*0)|(RD_QueryFlag_Floating*1)|(RD_QueryFlag_Required*1), RD_RegSlot_Vaddr, str8_lit_comp(""), str8_lit_comp(""), D_EntityKind_Null}},
|
||||
|
||||
@@ -53,6 +53,7 @@ RD_RegSlot_AllWindows,
|
||||
RD_RegSlot_NonGraphical,
|
||||
RD_RegSlot_PreferNewTab,
|
||||
RD_RegSlot_ActivateWithSingleClick,
|
||||
RD_RegSlot_DisableAddresses,
|
||||
RD_RegSlot_Dir2,
|
||||
RD_RegSlot_String,
|
||||
RD_RegSlot_CmdName,
|
||||
@@ -74,6 +75,7 @@ RD_CmdKind_StepOverInst,
|
||||
RD_CmdKind_StepIntoLine,
|
||||
RD_CmdKind_StepOverLine,
|
||||
RD_CmdKind_StepOut,
|
||||
RD_CmdKind_StepToExit,
|
||||
RD_CmdKind_Halt,
|
||||
RD_CmdKind_SoftHaltRefresh,
|
||||
RD_CmdKind_SetThreadIP,
|
||||
@@ -498,6 +500,7 @@ B32 all_windows;
|
||||
B32 non_graphical;
|
||||
B32 prefer_new_tab;
|
||||
B32 activate_with_single_click;
|
||||
B32 disable_addresses;
|
||||
Dir2 dir2;
|
||||
String8 string;
|
||||
String8 cmd_name;
|
||||
@@ -598,6 +601,7 @@ Z(getting_started)\
|
||||
.non_graphical = rd_regs()->non_graphical,\
|
||||
.prefer_new_tab = rd_regs()->prefer_new_tab,\
|
||||
.activate_with_single_click = rd_regs()->activate_with_single_click,\
|
||||
.disable_addresses = rd_regs()->disable_addresses,\
|
||||
.dir2 = rd_regs()->dir2,\
|
||||
.string = rd_regs()->string,\
|
||||
.cmd_name = rd_regs()->cmd_name,\
|
||||
@@ -606,10 +610,10 @@ Z(getting_started)\
|
||||
C_LINKAGE_BEGIN
|
||||
extern String8 rd_tab_fast_path_view_name_table[25];
|
||||
extern String8 rd_tab_fast_path_query_name_table[25];
|
||||
extern RD_VocabInfo rd_vocab_info_table[364];
|
||||
extern RD_VocabInfo rd_vocab_info_table[365];
|
||||
extern RD_NameSchemaInfo rd_name_schema_info_table[39];
|
||||
extern String8 rd_reg_slot_code_name_table[49];
|
||||
extern Rng1U64 rd_reg_slot_range_table[49];
|
||||
extern String8 rd_reg_slot_code_name_table[50];
|
||||
extern Rng1U64 rd_reg_slot_range_table[50];
|
||||
extern String8 rd_binding_version_remap_old_name_table[9];
|
||||
extern String8 rd_binding_version_remap_new_name_table[9];
|
||||
extern String8 rd_icon_kind_text_table[75];
|
||||
|
||||
@@ -891,6 +891,7 @@ RD_RegTable:
|
||||
{B32 non_graphical NonGraphical }
|
||||
{B32 prefer_new_tab PreferNewTab }
|
||||
{B32 activate_with_single_click ActivateWithSingleClick }
|
||||
{B32 disable_addresses DisableAddresses }
|
||||
{Dir2 dir2 Dir2 }
|
||||
{String8 string String }
|
||||
{String8 cmd_name CmdName }
|
||||
|
||||
+57
-10
@@ -10302,6 +10302,7 @@ rd_init(CmdLine *cmdln)
|
||||
rd_state->arena = arena;
|
||||
rd_state->quit_after_success = (cmd_line_has_flag(cmdln, str8_lit("quit_after_success")) ||
|
||||
cmd_line_has_flag(cmdln, str8_lit("q")));
|
||||
rd_state->override_focus_on_stop = (cmd_line_has_flag(cmdln, s("no_focus_on_stop")));
|
||||
rd_state->user_path_arena = arena_alloc();
|
||||
rd_state->project_path_arena = arena_alloc();
|
||||
rd_state->theme_path_arena = arena_alloc();
|
||||
@@ -12584,6 +12585,7 @@ rd_frame(void)
|
||||
{
|
||||
String8 msg = rd_regs()->string;
|
||||
String8List msg_parts = str8_split(scratch.arena, msg, (U8 *)" ", 1, 0);
|
||||
str8_list_push_front(scratch.arena, &msg_parts, s("ipc_command"));
|
||||
CmdLine msg_cmd_line = cmd_line_from_string_list(scratch.arena, msg_parts);
|
||||
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);
|
||||
@@ -12609,8 +12611,8 @@ rd_frame(void)
|
||||
primary_args_string = str8_list_join(scratch.arena, &primary_args_strings, &(StringJoin){.sep = str8_lit(" ")});
|
||||
}
|
||||
rd_regs_fill_slot_from_string(cmd_kind_info->query.slot, cmd_kind_info->query.expr, primary_args_string);
|
||||
rd_regs()->disable_addresses = 1;
|
||||
rd_push_cmd(cmd_kind_name, rd_regs());
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, "ack:{cmd:%S}\n", cmd_kind_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -12622,15 +12624,32 @@ rd_frame(void)
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, "state:\n{\n");
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " running: %i\n", d_ctrl_targets_running());
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " run_gen: %I64u\n", d_run_gen());
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " ip: 0x%I64x\n", d_ctrl_last_stop_event().rip_vaddr);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " stop_count: %I64u\n", d_stop_count());
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " ip: 0x%I64x\n", d_ctrl_last_stop_event().rip_vaddr);
|
||||
{
|
||||
Access *access = access_open();
|
||||
D_Event evt = d_ctrl_last_stop_event();
|
||||
U64 vaddr = evt.rip_vaddr;
|
||||
D_Entity *thread = d_entity_from_handle(rd_base_regs()->thread);
|
||||
D_Entity *process = d_entity_ancestor_from_kind(thread, D_EntityKind_Process);
|
||||
D_Entity *module = d_module_from_process_vaddr(process, vaddr);
|
||||
U64 voff = d_voff_from_vaddr(module, vaddr);
|
||||
DI_Key dbgi_key = d_dbgi_key_from_module(module);
|
||||
RDI_Parsed *rdi = di_rdi_from_key(access, dbgi_key, 0, 0);
|
||||
RDI_Symbol *procedure = rdi_procedure_from_voff(rdi, voff);
|
||||
String8 name = fully_qualified_str8_from_rdi_symbol(scratch.arena, rdi, procedure);
|
||||
name = escaped_from_raw_str8(scratch.arena, name);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " ip_voff: 0x%I64x\n", voff);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " ip_voff_symbol: \"%S\"\n", name);
|
||||
access_close(access);
|
||||
}
|
||||
{
|
||||
D_Event evt = d_ctrl_last_stop_event();
|
||||
DR_FStrList explanation_fstrs = rd_stop_explanation_fstrs_from_ctrl_event(scratch.arena, &evt);
|
||||
String8 explanation_string = dr_string_from_fstrs(scratch.arena, &explanation_fstrs);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " stop_event:\n {\n");
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " arch: %S\n", string_from_arch(evt.arch));
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " vaddr_range_min: 0x%I64x\n", evt.vaddr_rng.min);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " vaddr_range_max: 0x%I64x\n", evt.vaddr_rng.max);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " vaddr_range: [0x%I64x, 0x%I64x)\n", evt.vaddr_rng.min, evt.vaddr_rng.max);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " ip_vaddr: 0x%I64x\n", evt.rip_vaddr);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " stack_base: 0x%I64x\n", evt.stack_base);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " tls_root: 0x%I64x\n", evt.tls_root);
|
||||
@@ -12643,6 +12662,25 @@ rd_frame(void)
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " explanation: \"%S\"\n", escaped_from_raw_str8(scratch.arena, explanation_string));
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " }\n");
|
||||
}
|
||||
{
|
||||
Access *access = access_open();
|
||||
D_Event evt = d_ctrl_last_stop_event();
|
||||
U64 vaddr = evt.rip_vaddr;
|
||||
D_Entity *thread = d_entity_from_handle(rd_base_regs()->thread);
|
||||
D_Entity *process = d_entity_ancestor_from_kind(thread, D_EntityKind_Process);
|
||||
D_Entity *module = d_module_from_process_vaddr(process, vaddr);
|
||||
U64 voff = d_voff_from_vaddr(module, vaddr);
|
||||
DI_Key dbgi_key = d_dbgi_key_from_module(module);
|
||||
RDI_Parsed *rdi = di_rdi_from_key(access, dbgi_key, 0, 0);
|
||||
E_String2NumMap *locals_map = e_push_locals_map_from_rdi_voff(scratch.arena, rdi, voff);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " locals:\n {\n");
|
||||
for(E_String2NumMapNode *n = locals_map->first; n != 0; n = n->order_next)
|
||||
{
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " %S\n", n->string);
|
||||
}
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " }\n");
|
||||
access_close(access);
|
||||
}
|
||||
{
|
||||
D_Event evt = d_ctrl_last_stop_event();
|
||||
U64 vaddr = evt.rip_vaddr;
|
||||
@@ -12656,12 +12694,12 @@ rd_frame(void)
|
||||
for EachNode(n, D_LineNode, lines.first)
|
||||
{
|
||||
D_Line line = n->v;
|
||||
String8 line_file_path_relative = path_relative_dst_from_absolute_dst_src(scratch.arena, line.file_path, module->string);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " {\n");
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " file_path: \"%S\"\n", line.file_path);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " file_path: \"%S\"\n", line_file_path_relative);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " line_num: %I64d\n", line.pt.line);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " column_num: %I64d\n", line.pt.column);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " voff_range_min: 0x%I64x\n", line.voff_range.min);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " voff_range_max: 0x%I64x\n", line.voff_range.max);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " voff_range: [0x%I64x, 0x%I64x)\n", line.voff_range.min, line.voff_range.max);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " }\n");
|
||||
}
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " }\n");
|
||||
@@ -12688,8 +12726,7 @@ rd_frame(void)
|
||||
D_Entity *module = modules.v[idx];
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " {\n");
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " name: \"%S\"\n", escaped_from_raw_str8(scratch.arena, module->string));
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " vaddr_range_min: 0x%I64x\n", module->vaddr_range.min);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " vaddr_range_max: 0x%I64x\n", module->vaddr_range.max);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " vaddr_range: [0x%I64x, 0x%I64x)\n", module->vaddr_range.min, module->vaddr_range.max);
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " }\n");
|
||||
}
|
||||
str8_list_pushf(rd_state->cmd_output_arena, &rd_state->cmd_outputs, " }\n");
|
||||
@@ -12698,6 +12735,12 @@ rd_frame(void)
|
||||
}break;
|
||||
case RD_CmdKind_Eval:
|
||||
{
|
||||
// TODO(rjf): to use this for evaluation testing, we need some way to temporarily
|
||||
// call the eval system with ctrl-thread-like evaluation rules (e.g. waiting for
|
||||
// memory, never accepting stale results, not looking into caches, etc.). currently
|
||||
// we'd only be able to do that by setting up an entirely new evaluation context,
|
||||
// which is currently a ton of code & laborious & duplicative, so we need to tighten
|
||||
// that up first. @eval_regressions
|
||||
String8 expr = rd_regs()->expr;
|
||||
E_Eval eval = e_eval_from_string(expr);
|
||||
E_Eval type_eval = e_eval_from_stringf("typeof(%S)", expr);
|
||||
@@ -12706,6 +12749,10 @@ rd_frame(void)
|
||||
.flags = EV_StringFlag_ReadOnlyDisplayRules | rd_state->eval_viz_base_string_flags,
|
||||
.radix = 10,
|
||||
};
|
||||
if(rd_regs()->disable_addresses)
|
||||
{
|
||||
string_params.flags |= EV_StringFlag_DisableAddresses;
|
||||
}
|
||||
String8 value_string = ev_value_string_from_eval(scratch.arena, &string_params, eval, 512);
|
||||
String8 type_value_string = ev_value_string_from_eval(scratch.arena, &string_params, type_eval, 512);
|
||||
String8List msgs_strings = {0};
|
||||
@@ -16903,7 +16950,7 @@ rd_frame(void)
|
||||
}
|
||||
|
||||
// rjf: focus window if none focused, and if we have a thread to snap to
|
||||
if(need_refocus && (selected_thread != &d_entity_nil || thread != &d_entity_nil))
|
||||
if(!rd_state->override_focus_on_stop && need_refocus && (selected_thread != &d_entity_nil || thread != &d_entity_nil))
|
||||
{
|
||||
B32 any_window_is_focused = 0;
|
||||
for(RD_WindowState *ws = rd_state->first_window_state; ws != &rd_nil_window_state; ws = ws->order_next)
|
||||
|
||||
@@ -388,6 +388,7 @@ struct RD_State
|
||||
Arena *arena;
|
||||
B32 quit;
|
||||
B32 quit_after_success;
|
||||
B32 override_focus_on_stop;
|
||||
S32 frame_depth;
|
||||
U64 frame_eval_memread_endt_us;
|
||||
|
||||
@@ -444,7 +445,7 @@ struct RD_State
|
||||
Access *frame_access;
|
||||
String8 last_window_title;
|
||||
|
||||
// rjf: evaluation cache
|
||||
// rjf: evaluation caches
|
||||
E_Cache *eval_cache;
|
||||
|
||||
// rjf: ambiguous path table (constructed from-scratch each frame)
|
||||
|
||||
@@ -426,7 +426,7 @@ struct IPCInfo
|
||||
//~ rjf: Globals
|
||||
|
||||
//- rjf: IPC resources
|
||||
#define IPC_SHARED_MEMORY_BUFFER_SIZE MB(4)
|
||||
#define IPC_SHARED_MEMORY_BUFFER_SIZE KB(64)
|
||||
StaticAssert(IPC_SHARED_MEMORY_BUFFER_SIZE > sizeof(IPCInfo), ipc_buffer_size_requirement);
|
||||
global Semaphore ipc_sender2main_signal_semaphore = {0};
|
||||
global Semaphore ipc_sender2main_lock_semaphore = {0};
|
||||
@@ -434,7 +434,7 @@ global U8 *ipc_sender2main_shared_memory_base = 0;
|
||||
global Semaphore ipc_main2sender_signal_semaphore = {0};
|
||||
global Semaphore ipc_main2sender_lock_semaphore = {0};
|
||||
global U8 *ipc_main2sender_shared_memory_base = 0;
|
||||
global U8 ipc_s2m_ring_buffer[MB(4)] = {0};
|
||||
global U8 ipc_s2m_ring_buffer[KB(64)] = {0};
|
||||
global U64 ipc_s2m_ring_write_pos = 0;
|
||||
global U64 ipc_s2m_ring_read_pos = 0;
|
||||
global Mutex ipc_s2m_ring_mutex = {0};
|
||||
@@ -666,6 +666,7 @@ entry_point(CmdLine *cmd_line)
|
||||
if(msg.size != 0)
|
||||
{
|
||||
log_infof("ipc_msg: \"%S\"", msg);
|
||||
String8List cmd_parts_of_msg = str8_split(scratch.arena, msg, (U8 *)";", 1, 0);
|
||||
RD_WindowState *dst_ws = rd_state->first_window_state;
|
||||
for(RD_WindowState *ws = dst_ws; ws != &rd_nil_window_state; ws = ws->order_next)
|
||||
{
|
||||
@@ -690,7 +691,10 @@ entry_point(CmdLine *cmd_line)
|
||||
rd_regs()->view = panel_tree.focused->selected_tab->id;
|
||||
scratch_end(scratch);
|
||||
}
|
||||
rd_cmd(RD_CmdKind_RunExternalDriverTextCommand, .string = msg);
|
||||
for EachNode(n, String8Node, cmd_parts_of_msg.first)
|
||||
{
|
||||
rd_cmd(RD_CmdKind_RunExternalDriverTextCommand, .string = n->string);
|
||||
}
|
||||
rd_request_frame();
|
||||
}
|
||||
}
|
||||
|
||||
+235
-40
@@ -1,6 +1,16 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debugger Testing IPC Driving Helpers
|
||||
|
||||
internal String8
|
||||
rd_test__mule_main_path(Arena *arena)
|
||||
{
|
||||
String8 mule_main_exe_path = str8f(arena, "%S/%S%S", get_process_info()->binary_path, s("mule_main"), program_ext_postfix_from_os(OperatingSystem_CURRENT));
|
||||
return mule_main_exe_path;
|
||||
}
|
||||
|
||||
internal String8
|
||||
rd_test__raddbg_path(Arena *arena, CmdLine *cmdline)
|
||||
{
|
||||
@@ -14,9 +24,9 @@ rd_test__open_debugger(CmdLine *cmdline, String8 test_artifacts_path, String8 ta
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
String8 raddbg_path = rd_test__raddbg_path(scratch.arena, cmdline);
|
||||
String8 user_file_path = str8f(scratch.arena, "%S/test.raddbg_user", test_artifacts_path);\
|
||||
String8 user_file_path = str8f(scratch.arena, "%S/test.raddbg_user", test_artifacts_path);
|
||||
delete_file_at_path(user_file_path);
|
||||
Process process = launch_cmd_linef("%S --gen_crash_dump --user:test.raddbg_user --logs:logs %S", raddbg_path, target_cmd_line);
|
||||
Process process = launch_cmd_linef("%S --gen_crash_dump --no_focus_on_stop --user:%S --logs:logs %S", raddbg_path, user_file_path, target_cmd_line);
|
||||
scratch_end(scratch);
|
||||
return process;
|
||||
}
|
||||
@@ -41,64 +51,249 @@ rd_test__ipc_cmd(Arena *arena, CmdLine *cmdline, String8 test_artifacts_path, U6
|
||||
return response;
|
||||
}
|
||||
|
||||
Test(stepping)
|
||||
////////////////////////////////
|
||||
//~ rjf: Exemplar Test Helpers
|
||||
|
||||
internal void
|
||||
rd_test__exemplar_test_finish(Arena *arena, String8List test_log_strings, String8 test_exemplars_path, String8 test_artifacts_path, TestResult *result_out, String8List *test_out)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8 mule_main_exe_path = str8f(scratch.arena, "%S/%S%S", get_process_info()->binary_path, s("mule_main"), program_ext_postfix_from_os(OperatingSystem_CURRENT));
|
||||
struct
|
||||
|
||||
// rjf: combine all current test log output; write to 'current' data
|
||||
String8 test_log_current = str8_list_join(scratch.arena, &test_log_strings, 0);
|
||||
String8 test_log_current_path = str8f(scratch.arena, "%S/current", test_artifacts_path);
|
||||
write_data_to_file_path(test_log_current_path, test_log_current);
|
||||
|
||||
// rjf: load test log exemplar
|
||||
make_directory(test_exemplars_path);
|
||||
String8 exemplar_path = str8f(scratch.arena, "%S/exemplar", test_exemplars_path);
|
||||
String8 exemplar_data = data_from_file_path(scratch.arena, exemplar_path);
|
||||
|
||||
// rjf: if exemplar data is empty -> just save our output as the new exemplar
|
||||
if(exemplar_data.size == 0)
|
||||
{
|
||||
String8 cmdline;
|
||||
String8 start_symbol;
|
||||
B32 step_over_on_even;
|
||||
copy_file_path(exemplar_path, test_log_current_path);
|
||||
}
|
||||
test_cases[] =
|
||||
|
||||
// rjf: if we have exemplar data, then we need to check that it matches
|
||||
if(exemplar_data.size != 0)
|
||||
{
|
||||
{ mule_main_exe_path, s("mule_main"), 0 },
|
||||
{ mule_main_exe_path, s("mule_main"), 1 },
|
||||
{ mule_main_exe_path, s("control_flow_stepping_tests"), 0 },
|
||||
{ mule_main_exe_path, s("control_flow_stepping_tests"), 1 },
|
||||
};
|
||||
for EachElement(test_case_idx, test_cases)
|
||||
{
|
||||
String8 test_cmdline = test_cases[test_case_idx].cmdline;
|
||||
String8 start_symbol = test_cases[test_case_idx].start_symbol;
|
||||
B32 step_over_on_even = test_cases[test_case_idx].step_over_on_even;
|
||||
Process debugger = rd_test__open_debugger(cmdline, test_artifacts_path, test_cmdline);
|
||||
U64 debugger_pid = pid_from_process(debugger);
|
||||
String8 next_step_cmd = {0};
|
||||
rd_test__ipc_cmd(scratch.arena, cmdline, test_artifacts_path, debugger_pid, str8f(scratch.arena, "run_to_name %S", start_symbol));
|
||||
for(U64 cmd_idx = 0;; cmd_idx += 1)
|
||||
B32 current_matches_exemplar = str8_match(test_log_current, exemplar_data, 0);
|
||||
if(!current_matches_exemplar)
|
||||
{
|
||||
String8 step_cmd = s("step_into");
|
||||
if((cmd_idx % 2 == 0 && step_over_on_even) || (cmd_idx % 2 == 1 && !step_over_on_even))
|
||||
String8 diff_cmd = str8f(scratch.arena, "diff %S %S",
|
||||
path_normalized_from_string(scratch.arena, exemplar_path),
|
||||
path_normalized_from_string(scratch.arena, test_log_current_path));
|
||||
test_outf("Current log does not match exemplar; run `%S`\n", diff_cmd);
|
||||
}
|
||||
TestCheck(current_matches_exemplar);
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Test Helpers
|
||||
|
||||
internal void
|
||||
rd_test__stepping_regressions(Arena *arena, CmdLine *cmdline, String8 test_exemplars_path, String8 test_artifacts_path, TestResult *result_out, String8List *test_out, String8 target_cmdline, String8 start_symbol, B32 step_over_on_even)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8List test_log_strings = {0};
|
||||
|
||||
// rjf: start debugger
|
||||
Process debugger = rd_test__open_debugger(cmdline, test_artifacts_path, target_cmdline);
|
||||
U64 debugger_pid = pid_from_process(debugger);
|
||||
|
||||
// rjf: step & gather info
|
||||
{
|
||||
U64 last_stop_count = max_U64;
|
||||
B32 ready_for_next_operation = 0;
|
||||
String8 next_step_cmd = {0};
|
||||
for(U64 cmd_idx = 0;;)
|
||||
{
|
||||
Temp scratch2 = scratch_begin(&scratch.arena, 1);
|
||||
|
||||
// rjf: need next operation -> do op
|
||||
if(ready_for_next_operation)
|
||||
{
|
||||
step_cmd = s("step_over");
|
||||
ready_for_next_operation = 0;
|
||||
String8 step_cmd = s("step_into");
|
||||
if(cmd_idx == 0)
|
||||
{
|
||||
step_cmd = str8f(scratch2.arena, "run_to_name %S", start_symbol);
|
||||
}
|
||||
else if((cmd_idx % 2 == 0 && step_over_on_even) || (cmd_idx % 2 == 1 && !step_over_on_even))
|
||||
{
|
||||
step_cmd = s("step_over");
|
||||
}
|
||||
if(next_step_cmd.size != 0)
|
||||
{
|
||||
step_cmd = next_step_cmd;
|
||||
MemoryZeroStruct(&next_step_cmd);
|
||||
}
|
||||
String8 step_response = rd_test__ipc_cmd(scratch2.arena, cmdline, test_artifacts_path, debugger_pid, step_cmd);
|
||||
(void)step_response;
|
||||
cmd_idx += 1;
|
||||
}
|
||||
if(next_step_cmd.size != 0)
|
||||
{
|
||||
step_cmd = next_step_cmd;
|
||||
MemoryZeroStruct(&next_step_cmd);
|
||||
}
|
||||
String8 step_response = rd_test__ipc_cmd(scratch.arena, cmdline, test_artifacts_path, debugger_pid, step_cmd);
|
||||
String8 state_response = rd_test__ipc_cmd(scratch.arena, cmdline, test_artifacts_path, debugger_pid, s("state"));
|
||||
MD_Node *state_response_tree = md_tree_from_string(scratch.arena, state_response);
|
||||
|
||||
// rjf: query state, parse
|
||||
String8 state_response = rd_test__ipc_cmd(scratch2.arena, cmdline, test_artifacts_path, debugger_pid, s("state"));
|
||||
MD_Node *state_response_tree = md_tree_from_string(scratch2.arena, state_response);
|
||||
MD_Node *state = md_child_from_string(state_response_tree, s("state"), 0);
|
||||
U64 ip_vaddr = 0;
|
||||
try_u64_from_str8_c_rules(md_child_from_string(state, s("ip"), 0)->first->string, &ip_vaddr);
|
||||
if(ip_vaddr != 0 && md_child_from_string(state, s("lines"), 0)->first == &md_nil_node)
|
||||
|
||||
// rjf: we are only ready for next step command when the stop count has changed
|
||||
{
|
||||
next_step_cmd = s("step_out");
|
||||
U64 stop_count = 0;
|
||||
try_u64_from_str8_c_rules(md_child_from_string(state, s("stop_count"), 0)->first->string, &stop_count);
|
||||
if(stop_count != last_stop_count)
|
||||
{
|
||||
ready_for_next_operation = 1;
|
||||
last_stop_count = stop_count;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: if we are ready for the next operation, then accumulate deterministic log info
|
||||
if(ready_for_next_operation)
|
||||
{
|
||||
MD_Node *lines = md_child_from_string(state, s("lines"), 0);
|
||||
MD_Node *ip_voff = md_child_from_string(state, s("ip_voff"), 0);
|
||||
MD_Node *ip_voff_symbol = md_child_from_string(state, s("ip_voff_symbol"), 0);
|
||||
String8 lines_dump = md_string_from_tree(scratch.arena, lines);
|
||||
String8 ip_voff_dump = md_string_from_tree(scratch.arena, ip_voff);
|
||||
String8 ip_voff_symbol_dump = md_string_from_tree(scratch.arena, ip_voff_symbol);
|
||||
str8_list_push(scratch.arena, &test_log_strings, lines_dump);
|
||||
str8_list_push(scratch.arena, &test_log_strings, ip_voff_dump);
|
||||
str8_list_push(scratch.arena, &test_log_strings, ip_voff_symbol_dump);
|
||||
}
|
||||
|
||||
// rjf: are we without line info? -> step out
|
||||
if(ready_for_next_operation)
|
||||
{
|
||||
U64 ip_vaddr = 0;
|
||||
try_u64_from_str8_c_rules(md_child_from_string(state, s("ip"), 0)->first->string, &ip_vaddr);
|
||||
if(ip_vaddr != 0 && md_child_from_string(state, s("lines"), 0)->first == &md_nil_node)
|
||||
{
|
||||
next_step_cmd = s("step_out");
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch2);
|
||||
if(cmd_idx >= 100)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
rd_test__close_debugger(debugger);
|
||||
}
|
||||
|
||||
// rjf: end exemplar test
|
||||
rd_test__exemplar_test_finish(arena, test_log_strings, test_exemplars_path, test_artifacts_path, result_out, test_out);
|
||||
|
||||
// rjf: close debugger
|
||||
rd_test__close_debugger(debugger);
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
internal void
|
||||
rd_test__eval_regressions(Arena *arena, CmdLine *cmdline, String8 test_exemplars_path, String8 test_artifacts_path, TestResult *result_out, String8List *test_out, String8 target_cmdline, String8 target_line)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
String8List test_log_strings = {0};
|
||||
|
||||
// rjf: start debugger
|
||||
Process debugger = rd_test__open_debugger(cmdline, test_artifacts_path, target_cmdline);
|
||||
U64 debugger_pid = pid_from_process(debugger);
|
||||
|
||||
// rjf: query state
|
||||
U64 initial_stop_count = 0;
|
||||
{
|
||||
String8 response = rd_test__ipc_cmd(scratch.arena, cmdline, test_artifacts_path, debugger_pid, s("state"));
|
||||
MD_Node *state = md_child_from_string(md_tree_from_string(scratch.arena, response), s("state"), 0);
|
||||
MD_Node *stop_count = md_child_from_string(state, s("stop_count"), 0);
|
||||
try_u64_from_str8_c_rules(stop_count->first->string, &initial_stop_count);
|
||||
}
|
||||
|
||||
// rjf: run to target line
|
||||
rd_test__ipc_cmd(scratch.arena, cmdline, test_artifacts_path, debugger_pid, str8f(scratch.arena, "run_to_line %S", target_line));
|
||||
|
||||
// rjf: wait for stop, collect locals
|
||||
String8List locals = {0};
|
||||
for(;;)
|
||||
{
|
||||
Temp scratch2 = scratch_begin(&scratch.arena, 1);
|
||||
String8 response = rd_test__ipc_cmd(scratch.arena, cmdline, test_artifacts_path, debugger_pid, s("state"));
|
||||
MD_Node *state = md_child_from_string(md_tree_from_string(scratch.arena, response), s("state"), 0);
|
||||
MD_Node *stop_count = md_child_from_string(state, s("stop_count"), 0);
|
||||
U64 new_stop_count = 0;
|
||||
try_u64_from_str8_c_rules(stop_count->first->string, &new_stop_count);
|
||||
scratch_end(scratch2);
|
||||
if(new_stop_count != initial_stop_count)
|
||||
{
|
||||
MD_Node *locals_root = md_child_from_string(state, s("locals"), 0);
|
||||
for MD_EachNode(c, locals_root->first)
|
||||
{
|
||||
str8_list_push(scratch.arena, &locals, c->string);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: evaluate all locals
|
||||
String8List eval_cmd_strings = {0};
|
||||
for EachNode(n, String8Node, locals.first)
|
||||
{
|
||||
str8_list_pushf(scratch.arena, &eval_cmd_strings, "eval %S", n->string);
|
||||
}
|
||||
StringJoin join = {.sep = s(" ; ")};
|
||||
String8 eval_msg = str8_list_join(scratch.arena, &eval_cmd_strings, &join);
|
||||
String8 response = rd_test__ipc_cmd(scratch.arena, cmdline, test_artifacts_path, debugger_pid, eval_msg);
|
||||
MD_Node *response_tree = md_tree_from_string(scratch.arena, response);
|
||||
for MD_EachNode(n, response_tree->first)
|
||||
{
|
||||
if(str8_match(n->string, s("eval"), 0))
|
||||
{
|
||||
String8 eval_dump = md_string_from_tree(scratch.arena, n);
|
||||
str8_list_push(scratch.arena, &test_log_strings, eval_dump);
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: end exemplar test
|
||||
rd_test__exemplar_test_finish(arena, test_log_strings, test_exemplars_path, test_artifacts_path, result_out, test_out);
|
||||
|
||||
// rjf: close debugger
|
||||
rd_test__close_debugger(debugger);
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Tests
|
||||
|
||||
#if 0
|
||||
Test(mule_main_step_regressions_0)
|
||||
{
|
||||
String8 exe_path = {0};
|
||||
rd_test__stepping_regressions(arena, cmdline, test_exemplars_path, test_artifacts_path, result_out, test_out, rd_test__mule_main_path(arena), s("mule_main"), 0);
|
||||
}
|
||||
|
||||
Test(mule_main_step_regressions_1)
|
||||
{
|
||||
rd_test__stepping_regressions(arena, cmdline, test_exemplars_path, test_artifacts_path, result_out, test_out, rd_test__mule_main_path(arena), s("mule_main"), 1);
|
||||
}
|
||||
|
||||
Test(control_flow_step_regressions)
|
||||
{
|
||||
rd_test__stepping_regressions(arena, cmdline, test_exemplars_path, test_artifacts_path, result_out, test_out, rd_test__mule_main_path(arena), s("mule_main"), 0);
|
||||
}
|
||||
|
||||
SkippedTest(type_coverage_eval_regressions)
|
||||
{
|
||||
// TODO(rjf): see @eval_regressions
|
||||
rd_test__eval_regressions(arena, cmdline, test_exemplars_path, test_artifacts_path, result_out, test_out, rd_test__mule_main_path(arena), s("C:/devel/raddebugger/src/mule/mule_main.cpp:723"));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
////////////////////////////////
|
||||
// IPC Controller
|
||||
|
||||
Reference in New Issue
Block a user