From 2696c96f75258a5b41d95e2d50b74ac96cfd04ab Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Sat, 31 Aug 2024 07:36:53 -0700 Subject: [PATCH] continue coalescing df frame --- src/base/base_entry_point.c | 16 +- src/dbg_frontend/dbg_frontend_core.c | 405 ++++++++++++++++--- src/dbg_frontend/dbg_frontend_core.h | 7 +- src/raddbg/raddbg.c | 317 --------------- src/raddbg/raddbg.h | 534 ------------------------- src/raddbg/raddbg_main.c | 564 ++++++++++++++++++++++++++- 6 files changed, 935 insertions(+), 908 deletions(-) delete mode 100644 src/raddbg/raddbg.c delete mode 100644 src/raddbg/raddbg.h diff --git a/src/base/base_entry_point.c b/src/base/base_entry_point.c index 176b4fff..a8a686ae 100644 --- a/src/base/base_entry_point.c +++ b/src/base/base_entry_point.c @@ -4,21 +4,29 @@ internal void main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count) { + Temp scratch = scratch_begin(0, 0); + ThreadNameF("[main thread]"); + + //- rjf: set up telemetry #if PROFILE_TELEMETRY local_persist U8 tm_data[MB(64)]; tmLoadLibrary(TM_RELEASE); tmSetMaxThreadCount(256); tmInitialize(sizeof(tm_data), (char *)tm_data); #endif - ThreadNameF("[main thread]"); - Temp scratch = scratch_begin(0, 0); + + //- rjf: parse command line String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, (int)arguments_count, arguments); CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings); + + //- rjf: begin captures B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture")); if(capture) { ProfBeginCapture(arguments[0]); } + + //- rjf: initialize all included layers #if defined(TASK_SYSTEM_H) && !defined(TS_INIT_MANUAL) ts_init(); #endif @@ -74,7 +82,11 @@ main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **argum #if defined(DBG_FRONTEND_CORE_H) && !defined(DF_INIT_MANUAL) df_init(update_and_render, d_state_delta_history()); #endif + + //- rjf: call into entry point entry_point(&cmdline); + + //- rjf: end captures if(capture) { ProfEndCapture(); diff --git a/src/dbg_frontend/dbg_frontend_core.c b/src/dbg_frontend/dbg_frontend_core.c index 534f3e29..f518cc69 100644 --- a/src/dbg_frontend/dbg_frontend_core.c +++ b/src/dbg_frontend/dbg_frontend_core.c @@ -5845,8 +5845,9 @@ df_window_update_and_render(Arena *arena, DF_Window *ws, D_CmdList *cmds) } } } + + ui_end_build(); } - ui_end_build(); ////////////////////////////// //- rjf: ensure hover eval is in-bounds @@ -7958,23 +7959,252 @@ df_init(OS_WindowRepaintFunctionType *window_repaint_entry_point, D_StateDeltaHi } internal void -df_begin_frame(Arena *arena, D_CmdList *cmds) -{ - ProfBeginFunction(); - - ProfEnd(); -} - -internal void -df_frame(D_CmdList *cmds, F32 dt) +df_frame(OS_Handle repaint_window_handle) { Temp scratch = scratch_begin(0, 0); DI_Scope *di_scope = di_scope_open(); + ////////////////////////////// + //- rjf: mark user-facing thread tick + // + ProfTick(0); + txt_user_clock_tick(); + dasm_user_clock_tick(); + geo_user_clock_tick(); + tex_user_clock_tick(); + + ////////////////////////////// + //- rjf: get events from the OS + // + OS_EventList events = {0}; + if(os_handle_match(repaint_window_handle, os_handle_zero())) + { + events = os_get_events(scratch.arena, df_state->num_frames_requested == 0); + } + + ////////////////////////////// + //- rjf: pick target hz + // + // TODO(rjf): maximize target, given all windows and their monitors + F32 target_hz = os_get_gfx_info()->default_refresh_rate; + if(df_state->frame_time_us_history_idx > 32) + { + // rjf: calculate average frame time out of the last N + U64 num_frames_in_history = Min(ArrayCount(df_state->frame_time_us_history), df_state->frame_time_us_history_idx); + U64 frame_time_history_sum_us = 0; + for(U64 idx = 0; idx < num_frames_in_history; idx += 1) + { + frame_time_history_sum_us += df_state->frame_time_us_history[idx]; + } + U64 frame_time_history_avg_us = frame_time_history_sum_us/num_frames_in_history; + + // rjf: pick among a number of sensible targets to snap to, given how well + // we've been performing + F32 possible_alternate_hz_targets[] = {target_hz, 60.f, 120.f, 144.f, 240.f}; + F32 best_target_hz = target_hz; + S64 best_target_hz_frame_time_us_diff = max_S64; + for(U64 idx = 0; idx < ArrayCount(possible_alternate_hz_targets); idx += 1) + { + F32 candidate = possible_alternate_hz_targets[idx]; + if(candidate <= target_hz) + { + U64 candidate_frame_time_us = 1000000/(U64)candidate; + S64 frame_time_us_diff = (S64)frame_time_history_avg_us - (S64)candidate_frame_time_us; + if(abs_s64(frame_time_us_diff) < best_target_hz_frame_time_us_diff) + { + best_target_hz = candidate; + best_target_hz_frame_time_us_diff = frame_time_us_diff; + } + } + } + target_hz = best_target_hz; + } + + ////////////////////////////// + //- rjf: target Hz -> delta time + // + F32 dt = 1.f/target_hz; + + ////////////////////////////// + //- rjf: begin measuring actual per-frame work + // + U64 begin_time_us = os_now_microseconds(); + + ////////////////////////////// + //- rjf: bind change + // + if(!df_state->confirm_active && df_state->bind_change_active) + { + if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Esc)) + { + df_request_frame(); + df_state->bind_change_active = 0; + } + if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Delete)) + { + df_request_frame(); + df_unbind_spec(df_state->bind_change_cmd_spec, df_state->bind_change_binding); + df_state->bind_change_active = 0; + d_cmd(d_cfg_src_write_cmd_kind_table[D_CfgSrc_User]); + } + for(OS_Event *event = events.first, *next = 0; event != 0; event = next) + { + if(event->kind == OS_EventKind_Press && + event->key != OS_Key_Esc && + event->key != OS_Key_Return && + event->key != OS_Key_Backspace && + event->key != OS_Key_Delete && + event->key != OS_Key_LeftMouseButton && + event->key != OS_Key_RightMouseButton && + event->key != OS_Key_MiddleMouseButton && + event->key != OS_Key_Ctrl && + event->key != OS_Key_Alt && + event->key != OS_Key_Shift) + { + df_state->bind_change_active = 0; + DF_Binding binding = zero_struct; + { + binding.key = event->key; + binding.flags = event->flags; + } + df_unbind_spec(df_state->bind_change_cmd_spec, df_state->bind_change_binding); + df_bind_spec(df_state->bind_change_cmd_spec, binding); + U32 codepoint = os_codepoint_from_event_flags_and_key(event->flags, event->key); + os_text(&events, os_handle_zero(), codepoint); + os_eat_event(&events, event); + d_cmd(d_cfg_src_write_cmd_kind_table[D_CfgSrc_User]); + df_request_frame(); + break; + } + } + } + + ////////////////////////////// + //- rjf: consume events + // + { + for(OS_Event *event = events.first, *next = 0; + event != 0; + event = next) + { + next = event->next; + DF_Window *window = df_window_from_os_handle(event->window); + D_CmdParams params = window ? df_cmd_params_from_window(window) : df_cmd_params_from_gfx(); + B32 take = 0; + + //- rjf: try window close + if(!take && event->kind == OS_EventKind_WindowClose && window != 0) + { + take = 1; + d_cmd(D_CmdKind_CloseWindow, .window = df_handle_from_window(window)); + } + + //- rjf: try menu bar operations + { + if(!take && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0) + { + take = 1; + df_request_frame(); + window->menu_bar_focused_on_press = window->menu_bar_focused; + window->menu_bar_key_held = 1; + window->menu_bar_focus_press_started = 1; + } + if(!take && event->kind == OS_EventKind_Release && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0) + { + take = 1; + df_request_frame(); + window->menu_bar_key_held = 0; + } + if(window->menu_bar_focused && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0) + { + take = 1; + df_request_frame(); + window->menu_bar_focused = 0; + } + else if(window->menu_bar_focus_press_started && !window->menu_bar_focused && event->kind == OS_EventKind_Release && event->flags == 0 && event->key == OS_Key_Alt && event->is_repeat == 0) + { + take = 1; + df_request_frame(); + window->menu_bar_focused = !window->menu_bar_focused_on_press; + window->menu_bar_focus_press_started = 0; + } + else if(event->kind == OS_EventKind_Press && event->key == OS_Key_Esc && window->menu_bar_focused && !ui_any_ctx_menu_is_open()) + { + take = 1; + df_request_frame(); + window->menu_bar_focused = 0; + } + } + + //- rjf: try hotkey presses + if(!take && event->kind == OS_EventKind_Press) + { + DF_Binding binding = {event->key, event->flags}; + D_CmdSpecList spec_candidates = df_cmd_spec_list_from_binding(scratch.arena, binding); + if(spec_candidates.first != 0 && !d_cmd_spec_is_nil(spec_candidates.first->spec)) + { + D_CmdSpec *run_spec = d_cmd_spec_from_kind(D_CmdKind_RunCommand); + D_CmdSpec *spec = spec_candidates.first->spec; + if(run_spec != spec) + { + params.cmd_spec = spec; + } + U32 hit_char = os_codepoint_from_event_flags_and_key(event->flags, event->key); + take = 1; + d_push_cmd(run_spec, ¶ms); + if(event->flags & OS_EventFlag_Alt) + { + window->menu_bar_focus_press_started = 0; + } + } + else if(OS_Key_F1 <= event->key && event->key <= OS_Key_F19) + { + window->menu_bar_focus_press_started = 0; + } + df_request_frame(); + } + + //- rjf: try text events + if(!take && event->kind == OS_EventKind_Text) + { + String32 insertion32 = str32(&event->character, 1); + String8 insertion8 = str8_from_32(scratch.arena, insertion32); + D_CmdSpec *spec = d_cmd_spec_from_kind(D_CmdKind_InsertText); + params.string = insertion8; + d_push_cmd(spec, ¶ms); + df_request_frame(); + take = 1; + if(event->flags & OS_EventFlag_Alt) + { + window->menu_bar_focus_press_started = 0; + } + } + + //- rjf: do fall-through + if(!take) + { + take = 1; + params.os_event = event; + d_push_cmd(d_cmd_spec_from_kind(D_CmdKind_OSEvent), ¶ms); + } + + //- rjf: take + if(take) + { + os_eat_event(&events, event); + } + } + } + + ////////////////////////////// + //- rjf: gather root-level commands + // + D_CmdList cmds = d_gather_root_cmds(scratch.arena); + ////////////////////////////// //- rjf: tick debug engine // - d_tick(scratch.arena, di_scope, cmds, dt); + d_tick(scratch.arena, di_scope, &cmds, dt); ////////////////////////////// //- rjf: apply new rich hover info @@ -8077,25 +8307,68 @@ df_frame(D_CmdList *cmds, F32 dt) default:{}break; //- rjf: meta - case DF_MsgKind_Null:{}break; - case DF_MsgKind_Exit:{}break; - case DF_MsgKind_RunCommand:{}break; - case DF_MsgKind_ToggleDevMenu:{}break; + case DF_MsgKind_Exit: + { + // TODO(rjf): current implementation is wrong - will lose multiple windows!!! + }break; + case DF_MsgKind_RunCommand: + { + // TODO(rjf): need to somehow pass the command kind down... + }break; + case DF_MsgKind_ToggleDevMenu: + { + // TODO(rjf) + }break; //- rjf: config reading/writing - case DF_MsgKind_ApplyUserData:{}break; - case DF_MsgKind_ApplyProjectData:{}break; - case DF_MsgKind_WriteUserData:{}break; - case DF_MsgKind_WriteProjectData:{}break; + case DF_MsgKind_ApplyUserData: + case DF_MsgKind_ApplyProjectData: + { + // TODO(rjf) + }break; + case DF_MsgKind_WriteUserData: + case DF_MsgKind_WriteProjectData: + { + // TODO(rjf) + }break; //- rjf: windows - case DF_MsgKind_OpenWindow:{}break; - case DF_MsgKind_CloseWindow:{}break; - case DF_MsgKind_ToggleFullscreen:{}break; + case DF_MsgKind_OpenWindow: + { + DF_Window *originating_window = df_window_from_handle(regs->window); + if(originating_window == 0) + { + originating_window = df_state->first_window; + } + DF_Window *new_ws = df_window_open(v2f32(1280, 720), os_handle_zero(), D_CfgSrc_User); + if(originating_window != 0) + { + MemoryCopy(new_ws->setting_vals, originating_window->setting_vals, sizeof(DF_SettingVal)*DF_SettingCode_COUNT); + } + }break; + case DF_MsgKind_CloseWindow: + { + // TODO(rjf): coordinate with Exit msg + }break; + case DF_MsgKind_ToggleFullscreen: + { + DF_Window *window = df_window_from_handle(regs->window); + if(window != 0) + { + os_window_set_fullscreen(window->os, !os_window_is_fullscreen(window->os)); + } + }break; //- rjf: confirmation - case DF_MsgKind_ConfirmAccept:{}break; - case DF_MsgKind_ConfirmCancel:{}break; + case DF_MsgKind_ConfirmAccept: + { + // TODO(rjf): confirm cmds -> msgs + }break; + case DF_MsgKind_ConfirmCancel: + { + df_state->confirm_active = 0; + df_state->confirm_key = ui_key_zero(); + }break; //- rjf: queries case DF_MsgKind_CompleteQuery:{}break; @@ -8186,7 +8459,7 @@ df_frame(D_CmdList *cmds, F32 dt) B32 panel_reset_done = 0; { B32 cfg_write_done[D_CfgSrc_COUNT] = {0}; - for(D_CmdNode *cmd_node = cmds->first; + for(D_CmdNode *cmd_node = cmds.first; cmd_node != 0; cmd_node = cmd_node->next) { @@ -8215,7 +8488,7 @@ df_frame(D_CmdList *cmds, F32 dt) { D_CmdParams p = *params; p.view_spec = view_spec; - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_OpenTab)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_OpenTab)); } }break; @@ -8380,7 +8653,7 @@ df_frame(D_CmdList *cmds, F32 dt) case D_CmdKind_SelectUnwind: thread_locator:; { - d_cmd_list_push(scratch.arena, cmds, params, d_cmd_spec_from_kind(D_CmdKind_FindThread)); + d_cmd_list_push(scratch.arena, &cmds, params, d_cmd_spec_from_kind(D_CmdKind_FindThread)); }break; //- rjf: loading/applying stateful config changes @@ -8983,11 +9256,11 @@ df_frame(D_CmdList *cmds, F32 dt) D_CmdParams blank_params = df_cmd_params_from_window(ws); if(monitor_dim.x < 1920) { - d_cmd_list_push(scratch.arena, cmds, &blank_params, d_cmd_spec_from_kind(D_CmdKind_ResetToCompactPanels)); + d_cmd_list_push(scratch.arena, &cmds, &blank_params, d_cmd_spec_from_kind(D_CmdKind_ResetToCompactPanels)); } else { - d_cmd_list_push(scratch.arena, cmds, &blank_params, d_cmd_spec_from_kind(D_CmdKind_ResetToDefaultPanels)); + d_cmd_list_push(scratch.arena, &cmds, &blank_params, d_cmd_spec_from_kind(D_CmdKind_ResetToDefaultPanels)); } } @@ -9064,13 +9337,13 @@ df_frame(D_CmdList *cmds, F32 dt) { D_CmdParams p = *params; p.string = df_push_search_string(scratch.arena); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FindTextForward)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FindTextForward)); }break; case D_CmdKind_FindPrev: { D_CmdParams p = *params; p.string = df_push_search_string(scratch.arena); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FindTextBackward)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FindTextBackward)); }break; //- rjf: font sizes @@ -9215,7 +9488,7 @@ df_frame(D_CmdList *cmds, F32 dt) move_tab_panel != new_panel->prev && move_tab_panel != new_panel->next) { D_CmdParams p = df_cmd_params_from_panel(ws, move_tab_panel); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_ClosePanel)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_ClosePanel)); } } }break; @@ -9265,7 +9538,7 @@ df_frame(D_CmdList *cmds, F32 dt) { D_CmdParams p = df_cmd_params_from_window(ws); p.panel = df_handle_from_panel(panel); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FocusPanel)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FocusPanel)); break; } } @@ -9323,7 +9596,7 @@ df_frame(D_CmdList *cmds, F32 dt) } D_CmdParams p = df_cmd_params_from_window(ws); p.panel = df_handle_from_panel(dst_panel); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FocusPanel)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FocusPanel)); } }break; @@ -9475,7 +9748,7 @@ df_frame(D_CmdList *cmds, F32 dt) p.dest_panel = df_handle_from_panel(panel); p.view = df_handle_from_view(view); p.prev_view = df_handle_from_view(prev_view); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_MoveTab)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_MoveTab)); } }break; case D_CmdKind_OpenTab: @@ -9543,7 +9816,7 @@ df_frame(D_CmdList *cmds, F32 dt) if(src_panel_is_empty && src_panel != ws->root_panel) { D_CmdParams p = df_cmd_params_from_panel(ws, src_panel); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_ClosePanel)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_ClosePanel)); } } }break; @@ -9569,7 +9842,7 @@ df_frame(D_CmdList *cmds, F32 dt) D_CmdParams p = *params; p.window = df_handle_from_window(ws); p.panel = df_handle_from_panel(ws->focused_panel); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_PendingFile)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_PendingFile)); } else { @@ -10066,7 +10339,7 @@ df_frame(D_CmdList *cmds, F32 dt) params.vaddr = rip_vaddr; params.unwind_index = unwind_index; params.inline_depth = inline_depth; - d_cmd_list_push(scratch.arena, cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_FindCodeLocation)); + d_cmd_list_push(scratch.arena, &cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_FindCodeLocation)); } // rjf: snap to resolved address w/o line info @@ -10078,7 +10351,7 @@ df_frame(D_CmdList *cmds, F32 dt) params.vaddr = rip_vaddr; params.unwind_index = unwind_index; params.inline_depth = inline_depth; - d_cmd_list_push(scratch.arena, cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_FindCodeLocation)); + d_cmd_list_push(scratch.arena, &cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_FindCodeLocation)); } // rjf: retry on stopped, pending debug info @@ -10097,7 +10370,7 @@ df_frame(D_CmdList *cmds, F32 dt) params.entity = d_handle_from_entity(selected_thread); params.unwind_index = d_base_regs()->unwind_count; params.inline_depth = d_base_regs()->inline_depth; - d_cmd_list_push(scratch.arena, cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_FindThread)); + d_cmd_list_push(scratch.arena, &cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_FindThread)); }break; //- rjf: name finding @@ -10245,7 +10518,7 @@ df_frame(D_CmdList *cmds, F32 dt) } } } - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FindCodeLocation)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FindCodeLocation)); } } @@ -10256,7 +10529,7 @@ df_frame(D_CmdList *cmds, F32 dt) D_CmdParams p = *params; p.file_path = path; p.text_point = txt_pt(1, 1); - d_cmd_list_push(scratch.arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FindCodeLocation)); + d_cmd_list_push(scratch.arena, &cmds, &p, d_cmd_spec_from_kind(D_CmdKind_FindCodeLocation)); } } }break; @@ -10270,7 +10543,7 @@ df_frame(D_CmdList *cmds, F32 dt) default: break; case D_EntityKind_Target: { - d_cmd_list_push(scratch.arena, cmds, params, d_cmd_spec_from_kind(D_CmdKind_EditTarget)); + d_cmd_list_push(scratch.arena, &cmds, params, d_cmd_spec_from_kind(D_CmdKind_EditTarget)); }break; } }break; @@ -10281,7 +10554,7 @@ df_frame(D_CmdList *cmds, F32 dt) D_Entity *entity = d_entity_from_handle(params->entity); if(!d_entity_is_nil(entity) && entity->kind == D_EntityKind_Target) { - d_cmd_list_push(scratch.arena, cmds, params, d_cmd_spec_from_kind(D_CmdKind_Target)); + d_cmd_list_push(scratch.arena, &cmds, params, d_cmd_spec_from_kind(D_CmdKind_Target)); } else { @@ -10322,7 +10595,7 @@ df_frame(D_CmdList *cmds, F32 dt) { D_CmdParams params = df_cmd_params_from_panel(ws, panel); params.entity = d_handle_from_entity(entity); - d_cmd_list_push(scratch.arena, cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_EditTarget)); + d_cmd_list_push(scratch.arena, &cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_EditTarget)); }break; } }break; @@ -10563,8 +10836,8 @@ df_frame(D_CmdList *cmds, F32 dt) dst_panel->selected_tab_view = df_handle_from_view(dst_view); D_CmdParams params = df_cmd_params_from_view(ws, dst_panel, dst_view); params.text_point = point; - d_cmd_list_push(scratch.arena, cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_GoToLine)); - d_cmd_list_push(scratch.arena, cmds, ¶ms, d_cmd_spec_from_kind(cursor_snap_kind)); + d_cmd_list_push(scratch.arena, &cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_GoToLine)); + d_cmd_list_push(scratch.arena, &cmds, ¶ms, d_cmd_spec_from_kind(cursor_snap_kind)); panel_used_for_src_code = dst_panel; } } @@ -10610,8 +10883,8 @@ df_frame(D_CmdList *cmds, F32 dt) D_CmdParams params = df_cmd_params_from_view(ws, dst_panel, dst_view); params.entity = d_handle_from_entity(process); params.vaddr = vaddr; - d_cmd_list_push(scratch.arena, cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_GoToAddress)); - d_cmd_list_push(scratch.arena, cmds, ¶ms, d_cmd_spec_from_kind(cursor_snap_kind)); + d_cmd_list_push(scratch.arena, &cmds, ¶ms, d_cmd_spec_from_kind(D_CmdKind_GoToAddress)); + d_cmd_list_push(scratch.arena, &cmds, ¶ms, d_cmd_spec_from_kind(cursor_snap_kind)); } } }break; @@ -10737,13 +11010,13 @@ df_frame(D_CmdList *cmds, F32 dt) B32 window_is_focused = os_window_is_focused(w->os); if(window_is_focused) { - last_focused_window = df_handle_from_window(w); + df_state->last_focused_window = df_handle_from_window(w); } d_push_regs(); d_regs()->window = df_handle_from_window(w); - df_window_update_and_render(scratch.arena, w, cmds); + df_window_update_and_render(scratch.arena, w, &cmds); D_Regs *window_regs = d_pop_regs(); - if(df_window_from_handle(last_focused_window) == w) + if(df_window_from_handle(df_state->last_focused_window) == w) { MemoryCopyStruct(d_regs(), window_regs); } @@ -10807,6 +11080,34 @@ df_frame(D_CmdList *cmds, F32 dt) r_end_frame(); } + ////////////////////////////// + //- rjf: show windows after first frame + // + if(os_handle_match(repaint_window_handle, os_handle_zero())) + { + D_HandleList windows_to_show = {0}; + for(DF_Window *w = df_state->first_window; w != 0; w = w->next) + { + if(w->frames_alive == 1) + { + d_handle_list_push(scratch.arena, &windows_to_show, df_handle_from_window(w)); + } + } + for(D_HandleNode *n = windows_to_show.first; n != 0; n = n->next) + { + DF_Window *window = df_window_from_handle(n->handle); + os_window_first_paint(window->os); + } + } + + ////////////////////////////// + //- rjf: determine frame time, record into history + // + U64 end_time_us = os_now_microseconds(); + U64 frame_time_us = end_time_us-begin_time_us; + df_state->frame_time_us_history[df_state->frame_time_us_history_idx%ArrayCount(df_state->frame_time_us_history)] = frame_time_us; + df_state->frame_time_us_history_idx += 1; + di_scope_close(di_scope); scratch_end(scratch); } diff --git a/src/dbg_frontend/dbg_frontend_core.h b/src/dbg_frontend/dbg_frontend_core.h index 45c6247e..a170b506 100644 --- a/src/dbg_frontend/dbg_frontend_core.h +++ b/src/dbg_frontend/dbg_frontend_core.h @@ -666,6 +666,7 @@ struct DF_State DF_Window *free_window; U64 window_count; B32 last_window_queued_save; + D_Handle last_focused_window; // rjf: view state DF_View *first_view; @@ -697,6 +698,10 @@ struct DF_State // rjf: icon texture R_Handle icon_texture; + + // rjf: frame time history + U64 frame_time_us_history[64]; + U64 frame_time_us_history_idx; }; //////////////////////////////// @@ -963,6 +968,6 @@ __VA_ARGS__\ //~ rjf: Main Layer Top-Level Calls internal void df_init(OS_WindowRepaintFunctionType *window_repaint_entry_point, D_StateDeltaHistory *hist); -internal void df_frame(D_CmdList *cmds, F32 dt); +internal void df_frame(OS_Handle repaint_window_handle); #endif // DBG_FRONTEND_CORE_H diff --git a/src/raddbg/raddbg.c b/src/raddbg/raddbg.c deleted file mode 100644 index d435fdf9..00000000 --- a/src/raddbg/raddbg.c +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Frontend Entry Points - -internal void -update_and_render(OS_Handle repaint_window_handle, void *user_data) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - - ////////////////////////////// - //- rjf: begin logging - // - if(main_thread_log == 0) - { - main_thread_log = log_alloc(); - String8 user_program_data_path = os_get_process_info()->user_program_data_path; - String8 user_data_folder = push_str8f(scratch.arena, "%S/raddbg/logs", user_program_data_path); - main_thread_log_path = push_str8f(d_state->arena, "%S/ui_thread.raddbg_log", user_data_folder); - os_make_directory(user_data_folder); - os_write_data_to_file_path(main_thread_log_path, str8_zero()); - } - log_select(main_thread_log); - log_scope_begin(); - - ////////////////////////////// - //- rjf: mark user-facing thread tick - // - ProfTick(0); - txt_user_clock_tick(); - dasm_user_clock_tick(); - geo_user_clock_tick(); - tex_user_clock_tick(); - - ////////////////////////////// - //- rjf: get events from the OS - // - OS_EventList events = {0}; - if(os_handle_match(repaint_window_handle, os_handle_zero())) - { - events = os_get_events(scratch.arena, df_state->num_frames_requested == 0); - } - - ////////////////////////////// - //- rjf: pick target hz - // - // TODO(rjf): maximize target, given all windows and their monitors - F32 target_hz = os_get_gfx_info()->default_refresh_rate; - if(frame_time_us_history_idx > 32) - { - // rjf: calculate average frame time out of the last N - U64 num_frames_in_history = Min(ArrayCount(frame_time_us_history), frame_time_us_history_idx); - U64 frame_time_history_sum_us = 0; - for(U64 idx = 0; idx < num_frames_in_history; idx += 1) - { - frame_time_history_sum_us += frame_time_us_history[idx]; - } - U64 frame_time_history_avg_us = frame_time_history_sum_us/num_frames_in_history; - - // rjf: pick among a number of sensible targets to snap to, given how well - // we've been performing - F32 possible_alternate_hz_targets[] = {target_hz, 60.f, 120.f, 144.f, 240.f}; - F32 best_target_hz = target_hz; - S64 best_target_hz_frame_time_us_diff = max_S64; - for(U64 idx = 0; idx < ArrayCount(possible_alternate_hz_targets); idx += 1) - { - F32 candidate = possible_alternate_hz_targets[idx]; - if(candidate <= target_hz) - { - U64 candidate_frame_time_us = 1000000/(U64)candidate; - S64 frame_time_us_diff = (S64)frame_time_history_avg_us - (S64)candidate_frame_time_us; - if(abs_s64(frame_time_us_diff) < best_target_hz_frame_time_us_diff) - { - best_target_hz = candidate; - best_target_hz_frame_time_us_diff = frame_time_us_diff; - } - } - } - target_hz = best_target_hz; - } - - ////////////////////////////// - //- rjf: target Hz -> delta time - // - F32 dt = 1.f/target_hz; - - ////////////////////////////// - //- rjf: begin measuring actual per-frame work - // - U64 begin_time_us = os_now_microseconds(); - - ////////////////////////////// - //- rjf: bind change - // - if(!df_state->confirm_active && df_state->bind_change_active) - { - if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Esc)) - { - df_request_frame(); - df_state->bind_change_active = 0; - } - if(os_key_press(&events, os_handle_zero(), 0, OS_Key_Delete)) - { - df_request_frame(); - df_unbind_spec(df_state->bind_change_cmd_spec, df_state->bind_change_binding); - df_state->bind_change_active = 0; - d_cmd(d_cfg_src_write_cmd_kind_table[D_CfgSrc_User]); - } - for(OS_Event *event = events.first, *next = 0; event != 0; event = next) - { - if(event->kind == OS_EventKind_Press && - event->key != OS_Key_Esc && - event->key != OS_Key_Return && - event->key != OS_Key_Backspace && - event->key != OS_Key_Delete && - event->key != OS_Key_LeftMouseButton && - event->key != OS_Key_RightMouseButton && - event->key != OS_Key_MiddleMouseButton && - event->key != OS_Key_Ctrl && - event->key != OS_Key_Alt && - event->key != OS_Key_Shift) - { - df_state->bind_change_active = 0; - DF_Binding binding = zero_struct; - { - binding.key = event->key; - binding.flags = event->flags; - } - df_unbind_spec(df_state->bind_change_cmd_spec, df_state->bind_change_binding); - df_bind_spec(df_state->bind_change_cmd_spec, binding); - U32 codepoint = os_codepoint_from_event_flags_and_key(event->flags, event->key); - os_text(&events, os_handle_zero(), codepoint); - os_eat_event(&events, event); - d_cmd(d_cfg_src_write_cmd_kind_table[D_CfgSrc_User]); - df_request_frame(); - break; - } - } - } - - ////////////////////////////// - //- rjf: consume events - // - { - for(OS_Event *event = events.first, *next = 0; - event != 0; - event = next) - { - next = event->next; - DF_Window *window = df_window_from_os_handle(event->window); - D_CmdParams params = window ? df_cmd_params_from_window(window) : df_cmd_params_from_gfx(); - B32 take = 0; - - //- rjf: try window close - if(!take && event->kind == OS_EventKind_WindowClose && window != 0) - { - take = 1; - d_cmd(D_CmdKind_CloseWindow, .window = df_handle_from_window(window)); - } - - //- rjf: try menu bar operations - { - if(!take && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0) - { - take = 1; - df_request_frame(); - window->menu_bar_focused_on_press = window->menu_bar_focused; - window->menu_bar_key_held = 1; - window->menu_bar_focus_press_started = 1; - } - if(!take && event->kind == OS_EventKind_Release && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0) - { - take = 1; - df_request_frame(); - window->menu_bar_key_held = 0; - } - if(window->menu_bar_focused && event->kind == OS_EventKind_Press && event->key == OS_Key_Alt && event->flags == 0 && event->is_repeat == 0) - { - take = 1; - df_request_frame(); - window->menu_bar_focused = 0; - } - else if(window->menu_bar_focus_press_started && !window->menu_bar_focused && event->kind == OS_EventKind_Release && event->flags == 0 && event->key == OS_Key_Alt && event->is_repeat == 0) - { - take = 1; - df_request_frame(); - window->menu_bar_focused = !window->menu_bar_focused_on_press; - window->menu_bar_focus_press_started = 0; - } - else if(event->kind == OS_EventKind_Press && event->key == OS_Key_Esc && window->menu_bar_focused && !ui_any_ctx_menu_is_open()) - { - take = 1; - df_request_frame(); - window->menu_bar_focused = 0; - } - } - - //- rjf: try hotkey presses - if(!take && event->kind == OS_EventKind_Press) - { - DF_Binding binding = {event->key, event->flags}; - D_CmdSpecList spec_candidates = df_cmd_spec_list_from_binding(scratch.arena, binding); - if(spec_candidates.first != 0 && !d_cmd_spec_is_nil(spec_candidates.first->spec)) - { - D_CmdSpec *run_spec = d_cmd_spec_from_kind(D_CmdKind_RunCommand); - D_CmdSpec *spec = spec_candidates.first->spec; - if(run_spec != spec) - { - params.cmd_spec = spec; - } - U32 hit_char = os_codepoint_from_event_flags_and_key(event->flags, event->key); - take = 1; - d_push_cmd(run_spec, ¶ms); - if(event->flags & OS_EventFlag_Alt) - { - window->menu_bar_focus_press_started = 0; - } - } - else if(OS_Key_F1 <= event->key && event->key <= OS_Key_F19) - { - window->menu_bar_focus_press_started = 0; - } - df_request_frame(); - } - - //- rjf: try text events - if(!take && event->kind == OS_EventKind_Text) - { - String32 insertion32 = str32(&event->character, 1); - String8 insertion8 = str8_from_32(scratch.arena, insertion32); - D_CmdSpec *spec = d_cmd_spec_from_kind(D_CmdKind_InsertText); - params.string = insertion8; - d_push_cmd(spec, ¶ms); - df_request_frame(); - take = 1; - if(event->flags & OS_EventFlag_Alt) - { - window->menu_bar_focus_press_started = 0; - } - } - - //- rjf: do fall-through - if(!take) - { - take = 1; - params.os_event = event; - d_push_cmd(d_cmd_spec_from_kind(D_CmdKind_OSEvent), ¶ms); - } - - //- rjf: take - if(take) - { - os_eat_event(&events, event); - } - } - } - - ////////////////////////////// - //- rjf: gather root-level commands - // - D_CmdList cmds = d_gather_root_cmds(scratch.arena); - - ////////////////////////////// - //- rjf: do frontend frame - // - df_frame(&cmds, dt); - - ////////////////////////////// - //- rjf: show windows after first frame - // - if(os_handle_match(repaint_window_handle, os_handle_zero())) - { - D_HandleList windows_to_show = {0}; - for(DF_Window *w = df_state->first_window; w != 0; w = w->next) - { - if(w->frames_alive == 1) - { - d_handle_list_push(scratch.arena, &windows_to_show, df_handle_from_window(w)); - } - } - for(D_HandleNode *n = windows_to_show.first; n != 0; n = n->next) - { - DF_Window *window = df_window_from_handle(n->handle); - os_window_first_paint(window->os); - } - } - - ////////////////////////////// - //- rjf: determine frame time, record into history - // - U64 end_time_us = os_now_microseconds(); - U64 frame_time_us = end_time_us-begin_time_us; - frame_time_us_history[frame_time_us_history_idx%ArrayCount(frame_time_us_history)] = frame_time_us; - frame_time_us_history_idx += 1; - - ////////////////////////////// - //- rjf: end logging - // - { - LogScopeResult log = log_scope_end(scratch.arena); - os_append_data_to_file_path(main_thread_log_path, log.strings[LogMsgKind_Info]); - if(log.strings[LogMsgKind_UserError].size != 0) - { - d_error(log.strings[LogMsgKind_UserError]); - } - } - - scratch_end(scratch); - ProfEnd(); -} - -internal CTRL_WAKEUP_FUNCTION_DEF(wakeup_hook_ctrl) -{ - os_send_wakeup_event(); -} diff --git a/src/raddbg/raddbg.h b/src/raddbg/raddbg.h deleted file mode 100644 index 1e961e87..00000000 --- a/src/raddbg/raddbg.h +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Frontend/UI Pass Tasks -// -// [ ] empty user file causing failure to launch -// -// [ ] engine/frontend commands situation -// - currently, there is an interesting bifurcation of commands in the -// frontend; you can either push a command *at a root level*, or push a -// command to a locally-accessible list if you want that command to run -// on the same frame (root level commands are deferred by a frame, since -// the engine must see them first). -// - things would be simpler if there was only a single "push command" -// mechanism, and codepaths only ever saw these commands at most once. -// this would require an alternate strategy of the initial "gather" of -// commands, and instead it would just be a global queue, or something... -// it is a little weird since commands are not just consumed in order... -// - this will clean up the various different ways that codepaths -// parameterize commands. -// [ ] frontend entities vs. engine entities -// - currently, the engine has entities like "watch", and the frontend -// has entities like "windows", "panels", and "views". -// - because "watch" entities ideally have a hierarchical relationship -// with windows, panels, and views (enabling things like drag/drop -// from watch window -> tab, or tab -> watch window, trivially), it -// would be much better if all entities could collapse into engine -// entities. -// - now, the frontend requires various specialized resources for things -// like windows, so what I am thinking is that the engine just controls -// all of the stateful windows/panel/view/watch mechanisms, and then -// the frontend pure-functionally queries stuff like os/r handles -// on-demand, and then prunes them, immediate-mode cache style. -// [ ] command params -> d_regs -// - currently there are two almost-identical concepts relating to commands: -// the parameters struct, and D_Regs. D_Regs is a registers struct which -// is used to manage a stack of contextual information in various debugger -// codepaths. it is used so that codepaths can register information they -// know about, without passing it down to everyone explicitly - but those -// later codepaths can still pass that information along. e.g. a window -// calls into a watch window, watch window calls into visualizer, visualizer -// pushes command, which needs to pass which window it occurred on along. -// - i think D_Regs needs to expand a bit in order to encompass all of the -// things that the command parameters were being used for, but at that point -// commands can just be a spec * regs, and then the push-command API can -// just have ways of overriding regs values explicitly, when the codepath -// needs to be opinionated about which things are affected by which commands -// -// [ ] transient view timeout releasing -// -// [ ] save view column pcts; generalize to being a first-class thing in -// DF_View, e.g. by just having a string -> f32 store -// [ ] decay arrays to pointers in pointer/value comparison -// [ ] EVAL LOOKUP RULES -> currently going 0 -> rdis_count, but we need -// to prioritize the primary rdi -// -// [ ] file overrides -> always pick most specific one! found with conflicting -// overrides, e.g. C:/devel/ -> D:/devel/, but also C:/devel/foo -> -// C:/devel/bar, etc. -// -// [ ] auto-scroll output window -// [ ] theme lister -> fonts & font sizes -// [ ] "Browse..." buttons should adopt a more relevant starting search path, -// if possible -// [ ] visualize all breakpoints everywhere - source view should show up in -// disasm, disasm should show up in source view, function should show up in -// both, etc. -// [ ] ** Function breakpoints should show up in the source listing. Without -// them being visible, it is confusing when you run and you stop there, -// because you're like "wait why did it stop" and then you later remember -// that's because there was a function breakpoint there. -// -// [ ] font lister -// [ ] per-panel font size overrides -// -// [ ] For the Scheduler window, it would be nice if you could dim or -// folderize threads that are not your threads - eg., if a thread doesn't -// have any resolved stack pointers in your executable code, then you can -// ignore it when you are focusing on your own code. I don't know what the -// best way to detect this is, other than by walking the call stack... one -// way might be to just have a way to separate threads you've named from -// threads you haven't? Or, there could even be a debugger-specific API -// that you use to tag them. Just some way that would make it easier to -// focus on your own threads. -// -// [ ] "concept key stack"; basically, any point in UI builder path has a stack -// of active "concept keys", which can be used to e.g. build context menus -// automatically (could just be a per-box attachment; right-click any -// point, search up the tree and see the concept keys) - -//////////////////////////////// -//~ rjf: Hot, Medium Priority Tasks (Low-Hanging-Fruit Features, UI Jank, Cleanup) -// -// [ ] Setting the code_font/main_font values to a font name doesn't work. -// Should probably make note that you have to set it to a path to a TTF, -// since that's not normally how Windows fonts work. -// -// [ ] "root" concept in hash store, which buckets keys & allows usage code to -// jettison a collection of keys in retained mode fashion -// -// [ ] Jeff Notes -// [ ] sort locals by appearance in source code (or maybe just debug info) -// [ ] sum view rule -// [ ] plot view rule -// [ ] histogram view rule -// [ ] max view rule -// [ ] min view rule -// -// [ ] filesystem drag/drop support -// [ ] double-click vs. single-click for folder navigation, see if we can infer -// [ ] use backslashes on windows by default, forward slashes elsewhere -// -// [ ] investigate /DEBUG:FASTLINK - can we somehow alert that we do not -// support it? -// -// [ ] ** Converter performance & heuristics for asynchronously doing it early -// -// [ ] visualize conversion failures -// -// [ ] I was a little confused about what a profile file was. I understood -// what the user file was, but the profile file sounded like it should -// perhaps be per-project, yet it sounded like it was meant to be somewhat -// global? I don't have any feedback here because it probably will make -// sense once I use the debugger more, but I just thought I'd make a note -// to say that I was confused about it after reading the manual, so -// perhaps you could elaborate a little more on it in there. -// [ ] It wasn't clear to me how you save a user or project file. I can see -// how to load them, but not how you save them. Obviously I can just copy -// the files myself in the shell, but it seemed weird that there was no -// "save" option in the menus. -// -// [ ] Right-clicking on a thread in the Scheduler window pops up a context -// menu, but you can't actually see it because the tooltip for the thread -// draws on top of it, so you can't see the menu. -// -// [ ] In a "hover watch" (where you hover over a variable and it shows a pop- -// up watch window), if you expand an item near the bottom of the listing, -// it will be clipped to the bottom of the listing instead of showing the -// actual items (ie., it doesn't resize the listing based on what's -// actually visible) -// -// [ ] ** One very nice feature of RemedyBG that I use all the time is the -// ability to put "$err, hr" into the watch window, which will just show -// the value of GetLastError() as a string. This is super useful for -// debugging, so you don't have to litter your own code with it. -// -// [ ] Tooltip Coverage: -// [ ] lock icon -// [ ] "rotation arrow" icon next to executables -// -// [ ] For theme editing, when you hove the mouse over a theme color entry and -// it highlights that entry, it might help to temporarily change that -// color to white (or the inverse of the background color, or whatever) so -// that the user can see what things on the screen use that theme color. -// -// [ ] I had to go into the user file to change the font. That should probably -// be in the theme window? -// -// [ ] It'd be nice to have a "goto byte" option for source views, for jumping -// to error messages that are byte-based instead of line-based. -// -// [ ] @feature debug info overrides (both path-based AND module-based) -// -// [ ] C++ virtual inheritance member visualization in watch window - -//////////////////////////////// -//~ rjf: Hot, Low Priority Tasks (UI Opinions, Less-Serious Jank, Preferences, Cleanup) -// -// [ ] The hex format for color values in the config file was a real -// mindbender. It's prefixed with "0x", so I was assuming it was either -// Windows Big Endian (0xAARRGGBB) or Mac Little Endian (0xAABBGGRR). To -// my surprise, it was neither - it was actually web format (RRGGBBAA), -// which I was not expecting because that is normally written with a -// number sign (#AARRGGBB) not an 0x. -// -// [ ] Clicking on either side of a scroll bar is idiosyncratic. Normally, -// that is "page up" / "page down", but here it is "smooth scroll upward" -// / "smooth scroll downward" for some reason? -// -// [ ] can it ignore stepping into _RTC_CheckStackVars generated functions? -// [ ] mouse back button should make view to go back after I double clicked -// on function to open it -// [ ] Alt+8 to switch to disassembly would be nice (regardless on which -// panel was previous, don't want to use ctrl+, multiple times) -// Alt+8 for disasm and Alt+6 for memory view are shortcuts I often use -// in VS -// [ ] default font size is too small for me - not only source code, but -// menus/tab/watch names (which don't resize). Maybe you could query -// Windows for initial font size? -// [ ] icon fonts glyphs sometimes disappear for specific font size, but they -// reappear if you go +1 higher or -1 lower. Mostly red triangle in watch -// values for "unknown identifier". But also yellow arrow in call stack -// disappears if font size gets too large. -// [ ] undo close tab would be nice. If not for everything, then at least -// just for source files -// [ ] Jump table thunks, on code w/o /INCREMENTAL:NO - -//////////////////////////////// -//~ rjf: Hot, Feature Tasks (Not really "low priority" but less urgent than fixes) -// -// [ ] @eval_upgrade -// [ ] new eval system; support strings, many address spaces, many debug -// infos, wide/async transforms (e.g. diff(blob1, blob2)) -// -// [ ] Fancy View Rules -// [ ] table column boundaries should be checked against *AFTER* table -// contents, not before -// [ ] `array:(x, y)` - multidimensional array -// -// [ ] search-in-all-files -// -// [ ] Memory View -// [ ] memory view mutation controls -// [ ] memory view user-made annotations -// -// [ ] undo/redo -// [ ] proper "go back" + "go forward" history navigations -// -// [ ] globally disable/configure default view rule-like things (string -// viz for u8s in particular) -// -// [ ] @feature processor/data breakpoints -// [ ] @feature automatically snap to search matches when searching source files -// [ ] automatically start search query with selected text -// [ ] @feature entity views: filtering & reordering - -//////////////////////////////// -//~ rjf: Cold, Clean-up Tasks That Probably Only Ryan Notices -// (E.G. Because They Are Code-Related Or Because Nobody Cares) -// -// [ ] @bug view-snapping in scroll-lists, accounting for mapping between -// visual positions & logical positions (variably sized rows in watch, -// table headers, etc.) -// [ ] @cleanup straighten out index/number space & types & terminology for -// scroll lists -// [ ] @cleanup central worker thread pool - eliminate per-layer thread pools -// [ ] @cleanup eliminate explicit font parameters in the various ui paths (e.g. -// code slice params) - -//////////////////////////////// -//~ rjf: Cold, Unsorted Notes (Deferred Until Existing Lists Mostly Exhausted) -// -// [ ] @feature types -> auto view rules (don't statefully fill view rules -// given types, just query if no other view rule is present, & autofill -// when editing) -// [ ] @feature eval system -> somehow evaluate breakpoint hit counts? "meta" -// variables? -// -// [ ] @feature disasm view improvement features -// [ ] visualize jump destinations in disasm -// -// [ ] @feature eval ui improvement features -// [ ] serializing eval view maps -// [ ] view rule editors in hover-eval -// [ ] view rule hook coverage -// [ ] `each:(expr addition)` - apply some additional expression to all -// elements in an array/linked list would be useful to look at only a -// subset of an array of complex structs -// [ ] `slider:(min max)` view rule -// [ ] `v2f32` view rule -// [ ] `v3` view rule -// [ ] `quat` view rule -// [ ] `matrix` view rule -// [ ] `audio` waveform view rule -// [ ] smart scopes - expression operators for "grab me the first type X" -// [ ] "pinning" watch expressions, to attach it to a particular ctrl_ctx -// -// [ ] @feature header file for target -> debugger communication; printf, log, -// etc. -// [ ] @feature just-in-time debugging -// [ ] @feature step-out-of-loop -// -//-[ ] long-term future notes from martins -// [ ] core dump saving/loading -// [ ] parallel call stacks view -// [ ] parallel watch view -// [ ] mixed native/interpreted/jit debugging -// - it seems python has a top-level linked list of interpreter states, -// which should allow the debugger to map native callstacks to python -// code -// -// [ ] fancy string runs can include "weakness" information for text truncation -// ... can prioritize certain parts of strings to be truncated before -// others. would be good for e.g. the middle of a path -// [ ] font cache eviction (both for font tags, closing fp handles, and -// rasterizations) -// [ ] frontend speedup opportunities -// [ ] tables in UI -> currently building per-row, could probably cut down on -// # of boxes and # of draws by doing per-column in some cases? -// [ ] font cache layer -> can probably cache (string*font*size) -> (run) too -// (not just rasterization)... would save a *lot*, there is a ton of work -// just in looking up & stitching stuff repeatedly -// [ ] convert UI layout pass to not be naive recursive version -// [ ] (big change) parallelize window ui build codepaths per-panel - -//////////////////////////////// -//~ rjf: Recently Completed Task Log -// -// [x] UI_NavActions, OS_Event -> UI_Event (single event stream) -// [x] better discoverability for view rules - have better help hover tooltip, -// info on arguments, and better autocomplete lister -// [x] source view -> floating margin/line-nums -// [x] watch window reordering -// [x] standard way to filter -// [x] autocomplete lister should respect position in edited expression, -// tabbing through should autocomplete but not exit, etc. -// [x] pipe failure-to-launch errors back to frontend -// [x] bit more padding on the tabs -// [x] unified top-level cursor/typing/lister helper -// [x] collapse text cells & command lister & etc. into same codepath (?) -// [x] page-up & page-down correct handling in keyboard nav -// [x] interleaved src/dasm view -// [x] in watch window when I enter some new expression and then click mouse -// away from cell, then it should behave the same as if I pressed enter. -// Currently it does the same as if I have pressed esc and I have lost my -// expression -// [x] pressing random keyboard keys in source code advances text cursor like -// you were inputting text, very strange. -// [x] It's confusing that ENTER is the way you expand and collapse things in -// the watch window, but then also how you edit them if they are not -// expandable? It seems like this should be consistent (one way to edit, -// one way to expand/collapse, that are distinct) -// [x] Dragging a window tab (like Locals or Registers or whatnot) and -// canceling with ESC should revert the window tab to where it was. -// Currently, it leaves the window tab reordered if you dragged over its -// window and shuffled its position. -// [x] ** I couldn't figure out how to really view threads in the debugger. -// The only place I found a thread list was in "The Scheduler", but it -// only lists threads by ID, which is hard to use. I can hover over them -// to get the stack, which helps, but it would be much nicer if the top -// function was displayed in the window by default next to the thread. -// [x] ** It would be nice if thread listings displayed the name of the -// thread, instead of just the ID. -// [x] TLS eval -> in-process-memory EXE info -// [x] unwinding -> in-process-memory EXE info -// [x] new fuzzy searching layer -// [x] robustify dbgi layer to renames (cache should not be based only on -// path - must invalidate naturally when new filetime occurs) -// [x] rdi file regeneration too strict -// [x] raddbg jai.exe my_file.jai -- foobar -> raddbg consumes `--` incorrectly -// [x] mouse-driven way to complete file/folder selection, or more generally -// query completion -// [x] it would be nice to have "show in explorer" for right click on source -// file tab (opens explorer & selects the file) -// [x] asan stepping breakage -// [x] what's up with decimal number coloring where every group of 3 are in -// different color? can I turn it off? And why sometimes digits in number -// start with brighter color, but sometimes with darker - shouldn't it -// always have the same color ordering? -// [x] fix tabs-on-bottom positioning -// [x] colors: consistent tooltip styles (colors, font flags, etc.) -// [x] colors: scroll bars -// [x] colors: watch window navigation visuals -// [x] floating source view margin background/placement -// [x] "interaction root", or "group" ui_key, or something; used for menu bar interactions -// [x] theme colors -> more explicit about e.g. opaque backgrounds vs. floating -// & scrollbars etc. -// [x] Pressing the left mouse button on the menu bar and dragging does not -// move through the menus as expected - instead, it opens the one you -// clicked down on, then does nothing until you release, at which point it -// opens the menu you released on. -// [x] Similarly, pressing the left mouse button on a menu and dragging to an -// item, then releasing, does not trigger that item as expected. Instead, -// it is a nop, and it waits for you to click again on the item. -// [x] Using the word "symbol" in "Code (Symbol)" seems like a bad idea, since -// you're referring to non-identifier characters, but in a debugger -// "symbol" usually means something defined in the debug information. -// [x] I couldn't figure out how to affect the "dim" color in constants that -// have alternating bright/dim letters to show sections of a number. Is -// this in the theme colors somewhere? -// -// [x] ** Scrollbars are barely visible for me, for some reason. I could not -// find anything in the theme that would fill them with a solid, bright -// color. Instead they are just a thin outline and the same color as the -// scroll bar background. -// -// [x] Many of the UI elements, like the menus, would like better if they had -// a little bit of margin. Having the text right next to the edges, and -// with no line spacing, makes it harder to read things quickly. -// [x] colors: memory view -// [x] Hitting ESC during a color picker drag should abort the color picking -// and revert to the previous color. Currently, it just accepts the last -// drag result as the new color. -// [x] It was not clear to me why a small "tab picker" appeared when I got to -// a certain number of tabs. It seemed to appear even if the tabs were -// quite large, and there was no need to a drop-down menu to pick them. It -// feels like either it should always be there, or it should only show up -// if at least one tab gets small enough to have its name cut off? -// [x] I found the "context menu" convention to be confusing. For example, if -// I left-click on a tab, it selects the tab. If I right-click on a tab, -// it opens the context menu. However, if I left-click on a module, it -// opens the context window. It seems like maybe menus should be right, -// and left should do the default action, more consistently? -// -// [x] double click on procedure in procedures tab to jump to source -// [x] highlighted text & ctrl+f -> auto-fill search query -// [x] double-click any part of frame in callstack view -> snap to function -// [x] Menus take too long to show up. I would prefer it if they were instant. -// The animation doesn't really provide any useful cues, since I know -// where the menu came from. -// [x] user settings (ui & functionality - generally need a story for it) -// [x] hover animations -// [x] press animations -// [x] focus animations -// [x] tooltip animations -// [x] context menu animations -// [x] scrolling animations -// [x] background blur -// [x] tab width -// [x] ** In the call stack, I would like to be able to click quickly and move -// around the stack. Right now, you can do that with the first and third -// column, but the second column drops down a context menu. Since right -// click is already for context menus, can it not just be that double- -// clicking any column jumps to that stack frame? -// -// [x] ** I find it really hard to read the code with the heavyweight lines -// running through it for breakpoints and stepping and things. Is there a -// way to turn the lines off? AFAICT they are based on thread and -// breakpoint color, so you can't really control the line drawing? I might -// be fine with them, but they would have to be much more light (like -// alpha 0.1 or something) -// [x] zooming behaves very strangely - sometimes it zooms source code, -// sometimes both source code and menu/tab/watch font size, sometimes -// just menu/tab/watch font size not source size. -// [x] colors: fill out rest of theme presets for new theme setup -// [x] I LOVE ALT-W to add watch under cursor, but I would prefer to have it -// add what's under the MOUSE cursor instead of the keyboard cursor. Can -// we get a command for that so I can bind ALT-W to that instead? -// [x] editing multiple bindings for commands -// [x] inline breakpoint hit_count -// [x] to count hit counts, resolve all bps to addresses, check addresses -// against stopper thread's -// -// [x] PDB files distributed with the build are not found by DbgHelp!!! -// [x] Jai compiler debugging crash -// -//- 2024/8/29 -// -// [x] fix HRESULTs -// [x] fix escape char literals -// [x] eval: indexing into string literals -// [x] fix incorrectly consuming keyboard inputs, preventing fallback-to-filtering, when -// selecting null selection in watch views -// [x] ui_next_event(...), built-in focus filtering, no need to manually check -// if(ui_is_focus_active()) -// [x] Theme window should include font scaling. I was able to find the -// command for increasing the font scale, but I imagine most people -// wouldn't think to look there. -// [x] n-row table selection, in watch window & other UIs, multi-selection -// ctrl+C -// [x] target/breakpoint/watch-pin reordering -// [x] move breakpoints to being a global thing, not nested to particular files -// [x] EVAL SPACES - each rdi gets an rdi space, rdi space is passed to -// memory reads & so on, used to resolve to value space; REPLACES "mode" -// [x] fix selecting hover eval, then hover eval disappearing, causing -// busted focus, until a new hover eval is opened -// [x] `text[:lang]` - interpret memory as text, in lang `lang` -// [x] `disasm:arch` - interpret memory as machine code for isa `arch` -// [x] `memory` - view memory in usual memory hex-editor view -// NOTE(rjf): When the visualization system is solid, layers like dasm, txti, -// and so on can be dispensed with, as things like the source view, disasm -// view, or memory view will simply be specializations of the general purpose -// viz system. -// [x] view rule hook for standalone visualization ui, granted its own -// tab -// [x] collapse frontend visualization systems - source view, disasm view, -// callstack, modules, scheduler, should *all* be flavors of watch view -// [x] globally disable/configure bp/ip lines in source view -// [x] @cleanup naming pass over eval visualization part of the frontend, -// "blocks" vs. "canvas" vs. "expansion" - etc. -// [x] @cleanup collapse DF_CfgNodes into just being MD trees, find another way -// to encode config source - don't need it at every node -// [x] @cleanup in the frontend, we are starting to have to pass down "DF_Window" -// everywhere, because of per-window parameters (e.g. font rendering settings). -// this is really better solved by implicit thread-local parameters, similar to -// interaction registers, so that one window can "pick" all of the implicit -// parameters, and then 99% of the UI code does not have to care. -// [x] @cleanup simplification pass over eval visualization pipeline & types, -// including view rule hooks - -#ifndef RADDBG_H -#define RADDBG_H - -//////////////////////////////// -//~ rjf: Top-Level Execution Types - -typedef enum ExecMode -{ - ExecMode_Normal, - ExecMode_IPCSender, - ExecMode_Converter, - ExecMode_Help, -} -ExecMode; - -typedef struct IPCInfo IPCInfo; -struct IPCInfo -{ - U64 msg_size; -}; - -//////////////////////////////// -//~ rjf: Globals - -//- rjf: IPC resources -#define IPC_SHARED_MEMORY_BUFFER_SIZE MB(4) -StaticAssert(IPC_SHARED_MEMORY_BUFFER_SIZE > sizeof(IPCInfo), ipc_buffer_size_requirement); -global OS_Handle ipc_signal_semaphore = {0}; -global OS_Handle ipc_lock_semaphore = {0}; -global U8 *ipc_shared_memory_base = 0; -global U8 ipc_s2m_ring_buffer[MB(4)] = {0}; -global U64 ipc_s2m_ring_write_pos = 0; -global U64 ipc_s2m_ring_read_pos = 0; -global OS_Handle ipc_s2m_ring_mutex = {0}; -global OS_Handle ipc_s2m_ring_cv = {0}; - -//- rjf: last focused window -global D_Handle last_focused_window = {0}; - -//- rjf: frame time history -global U64 frame_time_us_history[64] = {0}; -global U64 frame_time_us_history_idx = 0; - -//- rjf: main thread log -global Log *main_thread_log = 0; -global String8 main_thread_log_path = {0}; - -//////////////////////////////// -//~ rjf: Frontend Entry Points - -internal void update_and_render(OS_Handle repaint_window_handle, void *user_data); - -#endif // RADDBG_H diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index 778d882d..26a6f017 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -1,6 +1,484 @@ // Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) +//////////////////////////////// +//~ rjf: Frontend/UI Pass Tasks +// +// [ ] empty user file causing failure to launch +// +// [ ] engine/frontend commands situation +// - currently, there is an interesting bifurcation of commands in the +// frontend; you can either push a command *at a root level*, or push a +// command to a locally-accessible list if you want that command to run +// on the same frame (root level commands are deferred by a frame, since +// the engine must see them first). +// - things would be simpler if there was only a single "push command" +// mechanism, and codepaths only ever saw these commands at most once. +// this would require an alternate strategy of the initial "gather" of +// commands, and instead it would just be a global queue, or something... +// it is a little weird since commands are not just consumed in order... +// - this will clean up the various different ways that codepaths +// parameterize commands. +// [ ] frontend entities vs. engine entities +// - currently, the engine has entities like "watch", and the frontend +// has entities like "windows", "panels", and "views". +// - because "watch" entities ideally have a hierarchical relationship +// with windows, panels, and views (enabling things like drag/drop +// from watch window -> tab, or tab -> watch window, trivially), it +// would be much better if all entities could collapse into engine +// entities. +// - now, the frontend requires various specialized resources for things +// like windows, so what I am thinking is that the engine just controls +// all of the stateful windows/panel/view/watch mechanisms, and then +// the frontend pure-functionally queries stuff like os/r handles +// on-demand, and then prunes them, immediate-mode cache style. +// [ ] command params -> d_regs +// - currently there are two almost-identical concepts relating to commands: +// the parameters struct, and D_Regs. D_Regs is a registers struct which +// is used to manage a stack of contextual information in various debugger +// codepaths. it is used so that codepaths can register information they +// know about, without passing it down to everyone explicitly - but those +// later codepaths can still pass that information along. e.g. a window +// calls into a watch window, watch window calls into visualizer, visualizer +// pushes command, which needs to pass which window it occurred on along. +// - i think D_Regs needs to expand a bit in order to encompass all of the +// things that the command parameters were being used for, but at that point +// commands can just be a spec * regs, and then the push-command API can +// just have ways of overriding regs values explicitly, when the codepath +// needs to be opinionated about which things are affected by which commands +// +// [ ] transient view timeout releasing +// +// [ ] save view column pcts; generalize to being a first-class thing in +// DF_View, e.g. by just having a string -> f32 store +// [ ] decay arrays to pointers in pointer/value comparison +// [ ] EVAL LOOKUP RULES -> currently going 0 -> rdis_count, but we need +// to prioritize the primary rdi +// +// [ ] file overrides -> always pick most specific one! found with conflicting +// overrides, e.g. C:/devel/ -> D:/devel/, but also C:/devel/foo -> +// C:/devel/bar, etc. +// +// [ ] auto-scroll output window +// [ ] theme lister -> fonts & font sizes +// [ ] "Browse..." buttons should adopt a more relevant starting search path, +// if possible +// [ ] visualize all breakpoints everywhere - source view should show up in +// disasm, disasm should show up in source view, function should show up in +// both, etc. +// [ ] ** Function breakpoints should show up in the source listing. Without +// them being visible, it is confusing when you run and you stop there, +// because you're like "wait why did it stop" and then you later remember +// that's because there was a function breakpoint there. +// +// [ ] font lister +// [ ] per-panel font size overrides +// +// [ ] For the Scheduler window, it would be nice if you could dim or +// folderize threads that are not your threads - eg., if a thread doesn't +// have any resolved stack pointers in your executable code, then you can +// ignore it when you are focusing on your own code. I don't know what the +// best way to detect this is, other than by walking the call stack... one +// way might be to just have a way to separate threads you've named from +// threads you haven't? Or, there could even be a debugger-specific API +// that you use to tag them. Just some way that would make it easier to +// focus on your own threads. +// +// [ ] "concept key stack"; basically, any point in UI builder path has a stack +// of active "concept keys", which can be used to e.g. build context menus +// automatically (could just be a per-box attachment; right-click any +// point, search up the tree and see the concept keys) + +//////////////////////////////// +//~ rjf: Hot, Medium Priority Tasks (Low-Hanging-Fruit Features, UI Jank, Cleanup) +// +// [ ] Setting the code_font/main_font values to a font name doesn't work. +// Should probably make note that you have to set it to a path to a TTF, +// since that's not normally how Windows fonts work. +// +// [ ] "root" concept in hash store, which buckets keys & allows usage code to +// jettison a collection of keys in retained mode fashion +// +// [ ] Jeff Notes +// [ ] sort locals by appearance in source code (or maybe just debug info) +// [ ] sum view rule +// [ ] plot view rule +// [ ] histogram view rule +// [ ] max view rule +// [ ] min view rule +// +// [ ] filesystem drag/drop support +// [ ] double-click vs. single-click for folder navigation, see if we can infer +// [ ] use backslashes on windows by default, forward slashes elsewhere +// +// [ ] investigate /DEBUG:FASTLINK - can we somehow alert that we do not +// support it? +// +// [ ] ** Converter performance & heuristics for asynchronously doing it early +// +// [ ] visualize conversion failures +// +// [ ] I was a little confused about what a profile file was. I understood +// what the user file was, but the profile file sounded like it should +// perhaps be per-project, yet it sounded like it was meant to be somewhat +// global? I don't have any feedback here because it probably will make +// sense once I use the debugger more, but I just thought I'd make a note +// to say that I was confused about it after reading the manual, so +// perhaps you could elaborate a little more on it in there. +// [ ] It wasn't clear to me how you save a user or project file. I can see +// how to load them, but not how you save them. Obviously I can just copy +// the files myself in the shell, but it seemed weird that there was no +// "save" option in the menus. +// +// [ ] Right-clicking on a thread in the Scheduler window pops up a context +// menu, but you can't actually see it because the tooltip for the thread +// draws on top of it, so you can't see the menu. +// +// [ ] In a "hover watch" (where you hover over a variable and it shows a pop- +// up watch window), if you expand an item near the bottom of the listing, +// it will be clipped to the bottom of the listing instead of showing the +// actual items (ie., it doesn't resize the listing based on what's +// actually visible) +// +// [ ] ** One very nice feature of RemedyBG that I use all the time is the +// ability to put "$err, hr" into the watch window, which will just show +// the value of GetLastError() as a string. This is super useful for +// debugging, so you don't have to litter your own code with it. +// +// [ ] Tooltip Coverage: +// [ ] lock icon +// [ ] "rotation arrow" icon next to executables +// +// [ ] For theme editing, when you hove the mouse over a theme color entry and +// it highlights that entry, it might help to temporarily change that +// color to white (or the inverse of the background color, or whatever) so +// that the user can see what things on the screen use that theme color. +// +// [ ] I had to go into the user file to change the font. That should probably +// be in the theme window? +// +// [ ] It'd be nice to have a "goto byte" option for source views, for jumping +// to error messages that are byte-based instead of line-based. +// +// [ ] @feature debug info overrides (both path-based AND module-based) +// +// [ ] C++ virtual inheritance member visualization in watch window + +//////////////////////////////// +//~ rjf: Hot, Low Priority Tasks (UI Opinions, Less-Serious Jank, Preferences, Cleanup) +// +// [ ] The hex format for color values in the config file was a real +// mindbender. It's prefixed with "0x", so I was assuming it was either +// Windows Big Endian (0xAARRGGBB) or Mac Little Endian (0xAABBGGRR). To +// my surprise, it was neither - it was actually web format (RRGGBBAA), +// which I was not expecting because that is normally written with a +// number sign (#AARRGGBB) not an 0x. +// +// [ ] Clicking on either side of a scroll bar is idiosyncratic. Normally, +// that is "page up" / "page down", but here it is "smooth scroll upward" +// / "smooth scroll downward" for some reason? +// +// [ ] can it ignore stepping into _RTC_CheckStackVars generated functions? +// [ ] mouse back button should make view to go back after I double clicked +// on function to open it +// [ ] Alt+8 to switch to disassembly would be nice (regardless on which +// panel was previous, don't want to use ctrl+, multiple times) +// Alt+8 for disasm and Alt+6 for memory view are shortcuts I often use +// in VS +// [ ] default font size is too small for me - not only source code, but +// menus/tab/watch names (which don't resize). Maybe you could query +// Windows for initial font size? +// [ ] icon fonts glyphs sometimes disappear for specific font size, but they +// reappear if you go +1 higher or -1 lower. Mostly red triangle in watch +// values for "unknown identifier". But also yellow arrow in call stack +// disappears if font size gets too large. +// [ ] undo close tab would be nice. If not for everything, then at least +// just for source files +// [ ] Jump table thunks, on code w/o /INCREMENTAL:NO + +//////////////////////////////// +//~ rjf: Hot, Feature Tasks (Not really "low priority" but less urgent than fixes) +// +// [ ] @eval_upgrade +// [ ] new eval system; support strings, many address spaces, many debug +// infos, wide/async transforms (e.g. diff(blob1, blob2)) +// +// [ ] Fancy View Rules +// [ ] table column boundaries should be checked against *AFTER* table +// contents, not before +// [ ] `array:(x, y)` - multidimensional array +// +// [ ] search-in-all-files +// +// [ ] Memory View +// [ ] memory view mutation controls +// [ ] memory view user-made annotations +// +// [ ] undo/redo +// [ ] proper "go back" + "go forward" history navigations +// +// [ ] globally disable/configure default view rule-like things (string +// viz for u8s in particular) +// +// [ ] @feature processor/data breakpoints +// [ ] @feature automatically snap to search matches when searching source files +// [ ] automatically start search query with selected text +// [ ] @feature entity views: filtering & reordering + +//////////////////////////////// +//~ rjf: Cold, Clean-up Tasks That Probably Only Ryan Notices +// (E.G. Because They Are Code-Related Or Because Nobody Cares) +// +// [ ] @bug view-snapping in scroll-lists, accounting for mapping between +// visual positions & logical positions (variably sized rows in watch, +// table headers, etc.) +// [ ] @cleanup straighten out index/number space & types & terminology for +// scroll lists +// [ ] @cleanup central worker thread pool - eliminate per-layer thread pools +// [ ] @cleanup eliminate explicit font parameters in the various ui paths (e.g. +// code slice params) + +//////////////////////////////// +//~ rjf: Cold, Unsorted Notes (Deferred Until Existing Lists Mostly Exhausted) +// +// [ ] @feature types -> auto view rules (don't statefully fill view rules +// given types, just query if no other view rule is present, & autofill +// when editing) +// [ ] @feature eval system -> somehow evaluate breakpoint hit counts? "meta" +// variables? +// +// [ ] @feature disasm view improvement features +// [ ] visualize jump destinations in disasm +// +// [ ] @feature eval ui improvement features +// [ ] serializing eval view maps +// [ ] view rule editors in hover-eval +// [ ] view rule hook coverage +// [ ] `each:(expr addition)` - apply some additional expression to all +// elements in an array/linked list would be useful to look at only a +// subset of an array of complex structs +// [ ] `slider:(min max)` view rule +// [ ] `v2f32` view rule +// [ ] `v3` view rule +// [ ] `quat` view rule +// [ ] `matrix` view rule +// [ ] `audio` waveform view rule +// [ ] smart scopes - expression operators for "grab me the first type X" +// [ ] "pinning" watch expressions, to attach it to a particular ctrl_ctx +// +// [ ] @feature header file for target -> debugger communication; printf, log, +// etc. +// [ ] @feature just-in-time debugging +// [ ] @feature step-out-of-loop +// +//-[ ] long-term future notes from martins +// [ ] core dump saving/loading +// [ ] parallel call stacks view +// [ ] parallel watch view +// [ ] mixed native/interpreted/jit debugging +// - it seems python has a top-level linked list of interpreter states, +// which should allow the debugger to map native callstacks to python +// code +// +// [ ] fancy string runs can include "weakness" information for text truncation +// ... can prioritize certain parts of strings to be truncated before +// others. would be good for e.g. the middle of a path +// [ ] font cache eviction (both for font tags, closing fp handles, and +// rasterizations) +// [ ] frontend speedup opportunities +// [ ] tables in UI -> currently building per-row, could probably cut down on +// # of boxes and # of draws by doing per-column in some cases? +// [ ] font cache layer -> can probably cache (string*font*size) -> (run) too +// (not just rasterization)... would save a *lot*, there is a ton of work +// just in looking up & stitching stuff repeatedly +// [ ] convert UI layout pass to not be naive recursive version +// [ ] (big change) parallelize window ui build codepaths per-panel + +//////////////////////////////// +//~ rjf: Recently Completed Task Log +// +// [x] UI_NavActions, OS_Event -> UI_Event (single event stream) +// [x] better discoverability for view rules - have better help hover tooltip, +// info on arguments, and better autocomplete lister +// [x] source view -> floating margin/line-nums +// [x] watch window reordering +// [x] standard way to filter +// [x] autocomplete lister should respect position in edited expression, +// tabbing through should autocomplete but not exit, etc. +// [x] pipe failure-to-launch errors back to frontend +// [x] bit more padding on the tabs +// [x] unified top-level cursor/typing/lister helper +// [x] collapse text cells & command lister & etc. into same codepath (?) +// [x] page-up & page-down correct handling in keyboard nav +// [x] interleaved src/dasm view +// [x] in watch window when I enter some new expression and then click mouse +// away from cell, then it should behave the same as if I pressed enter. +// Currently it does the same as if I have pressed esc and I have lost my +// expression +// [x] pressing random keyboard keys in source code advances text cursor like +// you were inputting text, very strange. +// [x] It's confusing that ENTER is the way you expand and collapse things in +// the watch window, but then also how you edit them if they are not +// expandable? It seems like this should be consistent (one way to edit, +// one way to expand/collapse, that are distinct) +// [x] Dragging a window tab (like Locals or Registers or whatnot) and +// canceling with ESC should revert the window tab to where it was. +// Currently, it leaves the window tab reordered if you dragged over its +// window and shuffled its position. +// [x] ** I couldn't figure out how to really view threads in the debugger. +// The only place I found a thread list was in "The Scheduler", but it +// only lists threads by ID, which is hard to use. I can hover over them +// to get the stack, which helps, but it would be much nicer if the top +// function was displayed in the window by default next to the thread. +// [x] ** It would be nice if thread listings displayed the name of the +// thread, instead of just the ID. +// [x] TLS eval -> in-process-memory EXE info +// [x] unwinding -> in-process-memory EXE info +// [x] new fuzzy searching layer +// [x] robustify dbgi layer to renames (cache should not be based only on +// path - must invalidate naturally when new filetime occurs) +// [x] rdi file regeneration too strict +// [x] raddbg jai.exe my_file.jai -- foobar -> raddbg consumes `--` incorrectly +// [x] mouse-driven way to complete file/folder selection, or more generally +// query completion +// [x] it would be nice to have "show in explorer" for right click on source +// file tab (opens explorer & selects the file) +// [x] asan stepping breakage +// [x] what's up with decimal number coloring where every group of 3 are in +// different color? can I turn it off? And why sometimes digits in number +// start with brighter color, but sometimes with darker - shouldn't it +// always have the same color ordering? +// [x] fix tabs-on-bottom positioning +// [x] colors: consistent tooltip styles (colors, font flags, etc.) +// [x] colors: scroll bars +// [x] colors: watch window navigation visuals +// [x] floating source view margin background/placement +// [x] "interaction root", or "group" ui_key, or something; used for menu bar interactions +// [x] theme colors -> more explicit about e.g. opaque backgrounds vs. floating +// & scrollbars etc. +// [x] Pressing the left mouse button on the menu bar and dragging does not +// move through the menus as expected - instead, it opens the one you +// clicked down on, then does nothing until you release, at which point it +// opens the menu you released on. +// [x] Similarly, pressing the left mouse button on a menu and dragging to an +// item, then releasing, does not trigger that item as expected. Instead, +// it is a nop, and it waits for you to click again on the item. +// [x] Using the word "symbol" in "Code (Symbol)" seems like a bad idea, since +// you're referring to non-identifier characters, but in a debugger +// "symbol" usually means something defined in the debug information. +// [x] I couldn't figure out how to affect the "dim" color in constants that +// have alternating bright/dim letters to show sections of a number. Is +// this in the theme colors somewhere? +// +// [x] ** Scrollbars are barely visible for me, for some reason. I could not +// find anything in the theme that would fill them with a solid, bright +// color. Instead they are just a thin outline and the same color as the +// scroll bar background. +// +// [x] Many of the UI elements, like the menus, would like better if they had +// a little bit of margin. Having the text right next to the edges, and +// with no line spacing, makes it harder to read things quickly. +// [x] colors: memory view +// [x] Hitting ESC during a color picker drag should abort the color picking +// and revert to the previous color. Currently, it just accepts the last +// drag result as the new color. +// [x] It was not clear to me why a small "tab picker" appeared when I got to +// a certain number of tabs. It seemed to appear even if the tabs were +// quite large, and there was no need to a drop-down menu to pick them. It +// feels like either it should always be there, or it should only show up +// if at least one tab gets small enough to have its name cut off? +// [x] I found the "context menu" convention to be confusing. For example, if +// I left-click on a tab, it selects the tab. If I right-click on a tab, +// it opens the context menu. However, if I left-click on a module, it +// opens the context window. It seems like maybe menus should be right, +// and left should do the default action, more consistently? +// +// [x] double click on procedure in procedures tab to jump to source +// [x] highlighted text & ctrl+f -> auto-fill search query +// [x] double-click any part of frame in callstack view -> snap to function +// [x] Menus take too long to show up. I would prefer it if they were instant. +// The animation doesn't really provide any useful cues, since I know +// where the menu came from. +// [x] user settings (ui & functionality - generally need a story for it) +// [x] hover animations +// [x] press animations +// [x] focus animations +// [x] tooltip animations +// [x] context menu animations +// [x] scrolling animations +// [x] background blur +// [x] tab width +// [x] ** In the call stack, I would like to be able to click quickly and move +// around the stack. Right now, you can do that with the first and third +// column, but the second column drops down a context menu. Since right +// click is already for context menus, can it not just be that double- +// clicking any column jumps to that stack frame? +// +// [x] ** I find it really hard to read the code with the heavyweight lines +// running through it for breakpoints and stepping and things. Is there a +// way to turn the lines off? AFAICT they are based on thread and +// breakpoint color, so you can't really control the line drawing? I might +// be fine with them, but they would have to be much more light (like +// alpha 0.1 or something) +// [x] zooming behaves very strangely - sometimes it zooms source code, +// sometimes both source code and menu/tab/watch font size, sometimes +// just menu/tab/watch font size not source size. +// [x] colors: fill out rest of theme presets for new theme setup +// [x] I LOVE ALT-W to add watch under cursor, but I would prefer to have it +// add what's under the MOUSE cursor instead of the keyboard cursor. Can +// we get a command for that so I can bind ALT-W to that instead? +// [x] editing multiple bindings for commands +// [x] inline breakpoint hit_count +// [x] to count hit counts, resolve all bps to addresses, check addresses +// against stopper thread's +// +// [x] PDB files distributed with the build are not found by DbgHelp!!! +// [x] Jai compiler debugging crash +// +//- 2024/8/29 +// +// [x] fix HRESULTs +// [x] fix escape char literals +// [x] eval: indexing into string literals +// [x] fix incorrectly consuming keyboard inputs, preventing fallback-to-filtering, when +// selecting null selection in watch views +// [x] ui_next_event(...), built-in focus filtering, no need to manually check +// if(ui_is_focus_active()) +// [x] Theme window should include font scaling. I was able to find the +// command for increasing the font scale, but I imagine most people +// wouldn't think to look there. +// [x] n-row table selection, in watch window & other UIs, multi-selection +// ctrl+C +// [x] target/breakpoint/watch-pin reordering +// [x] move breakpoints to being a global thing, not nested to particular files +// [x] EVAL SPACES - each rdi gets an rdi space, rdi space is passed to +// memory reads & so on, used to resolve to value space; REPLACES "mode" +// [x] fix selecting hover eval, then hover eval disappearing, causing +// busted focus, until a new hover eval is opened +// [x] `text[:lang]` - interpret memory as text, in lang `lang` +// [x] `disasm:arch` - interpret memory as machine code for isa `arch` +// [x] `memory` - view memory in usual memory hex-editor view +// NOTE(rjf): When the visualization system is solid, layers like dasm, txti, +// and so on can be dispensed with, as things like the source view, disasm +// view, or memory view will simply be specializations of the general purpose +// viz system. +// [x] view rule hook for standalone visualization ui, granted its own +// tab +// [x] collapse frontend visualization systems - source view, disasm view, +// callstack, modules, scheduler, should *all* be flavors of watch view +// [x] globally disable/configure bp/ip lines in source view +// [x] @cleanup naming pass over eval visualization part of the frontend, +// "blocks" vs. "canvas" vs. "expansion" - etc. +// [x] @cleanup collapse DF_CfgNodes into just being MD trees, find another way +// to encode config source - don't need it at every node +// [x] @cleanup in the frontend, we are starting to have to pass down "DF_Window" +// everywhere, because of per-window parameters (e.g. font rendering settings). +// this is really better solved by implicit thread-local parameters, similar to +// interaction registers, so that one window can "pick" all of the implicit +// parameters, and then 99% of the UI code does not have to care. +// [x] @cleanup simplification pass over eval visualization pipeline & types, +// including view rule hooks + //////////////////////////////// //~ rjf: Build Options @@ -65,7 +543,6 @@ #include "ui/ui_inc.h" #include "dbg_engine/dbg_engine_inc.h" #include "dbg_frontend/dbg_frontend_inc.h" -#include "raddbg.h" //- rjf: [c] #include "base/base_inc.c" @@ -105,7 +582,43 @@ #include "ui/ui_inc.c" #include "dbg_engine/dbg_engine_inc.c" #include "dbg_frontend/dbg_frontend_inc.c" -#include "raddbg.c" + +//////////////////////////////// +//~ rjf: Top-Level Execution Types + +typedef enum ExecMode +{ + ExecMode_Normal, + ExecMode_IPCSender, + ExecMode_Converter, + ExecMode_Help, +} +ExecMode; + +typedef struct IPCInfo IPCInfo; +struct IPCInfo +{ + U64 msg_size; +}; + +//////////////////////////////// +//~ rjf: Globals + +//- rjf: IPC resources +#define IPC_SHARED_MEMORY_BUFFER_SIZE MB(4) +StaticAssert(IPC_SHARED_MEMORY_BUFFER_SIZE > sizeof(IPCInfo), ipc_buffer_size_requirement); +global OS_Handle ipc_signal_semaphore = {0}; +global OS_Handle ipc_lock_semaphore = {0}; +global U8 *ipc_shared_memory_base = 0; +global U8 ipc_s2m_ring_buffer[MB(4)] = {0}; +global U64 ipc_s2m_ring_write_pos = 0; +global U64 ipc_s2m_ring_read_pos = 0; +global OS_Handle ipc_s2m_ring_mutex = {0}; +global OS_Handle ipc_s2m_ring_cv = {0}; + +//- rjf: main thread log +global Log *main_thread_log = 0; +global String8 main_thread_log_path = {0}; //////////////////////////////// //~ rjf: IPC Signaler Thread @@ -143,6 +656,53 @@ ipc_signaler_thread__entry_point(void *p) } } +//////////////////////////////// +//~ rjf: Per-Frame Entry Point + +internal void +update_and_render(OS_Handle repaint_window_handle, void *user_data) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + + //- rjf: begin logging + if(main_thread_log == 0) + { + main_thread_log = log_alloc(); + String8 user_program_data_path = os_get_process_info()->user_program_data_path; + String8 user_data_folder = push_str8f(scratch.arena, "%S/raddbg/logs", user_program_data_path); + main_thread_log_path = push_str8f(d_state->arena, "%S/ui_thread.raddbg_log", user_data_folder); + os_make_directory(user_data_folder); + os_write_data_to_file_path(main_thread_log_path, str8_zero()); + } + log_select(main_thread_log); + log_scope_begin(); + + //- rjf: do frontend frame + df_frame(repaint_window_handle); + + //- rjf: end logging + { + LogScopeResult log = log_scope_end(scratch.arena); + os_append_data_to_file_path(main_thread_log_path, log.strings[LogMsgKind_Info]); + if(log.strings[LogMsgKind_UserError].size != 0) + { + d_error(log.strings[LogMsgKind_UserError]); + } + } + + scratch_end(scratch); + ProfEnd(); +} + +//////////////////////////////// +//~ rjf: Ctrl -> Main Thread Wakeup Hook + +internal CTRL_WAKEUP_FUNCTION_DEF(wakeup_hook_ctrl) +{ + os_send_wakeup_event(); +} + //////////////////////////////// //~ rjf: Entry Point