implement 'priority thread' in demon, to prefer debug events from selected thread - greatly improves multithreaded stepping

This commit is contained in:
Ryan Fleury
2025-07-16 15:38:27 -07:00
parent db52ba32e6
commit 6e6a2df0cb
4 changed files with 73 additions and 39 deletions
+1
View File
@@ -5870,6 +5870,7 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg)
//- rjf: setup run controls
//
DMN_RunCtrls run_ctrls = {0};
run_ctrls.priority_thread = target_thread.dmn_handle;
run_ctrls.ignore_previous_exception = 1;
run_ctrls.run_entity_count = frozen_threads.count;
run_ctrls.run_entities = push_array(scratch.arena, DMN_Handle, run_ctrls.run_entity_count);
+1
View File
@@ -139,6 +139,7 @@ struct DMN_TrapChunkList
typedef struct DMN_RunCtrls DMN_RunCtrls;
struct DMN_RunCtrls
{
DMN_Handle priority_thread;
DMN_Handle single_step_thread;
B8 ignore_previous_exception;
B8 run_entities_are_unfrozen;
+65 -33
View File
@@ -1801,40 +1801,14 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
}
}
//////////////////////////
//- rjf: resume threads which will run
//
ProfScope("resume threads which will run")
{
for(DMN_W32_EntityNode *n = first_run_thread; n != 0; n = n->next)
{
DMN_W32_Entity *thread = n->v;
DWORD resume_result = ResumeThread(thread->handle);
switch(resume_result)
{
case 0xffffffffu:
{
// TODO(rjf): error - unknown cause. need to do GetLastError, FormatMessage
}break;
default:
{
DWORD desired_counter = 0;
DWORD current_counter = resume_result - 1;
if(current_counter != desired_counter)
{
// NOTE(rjf): Warning. The user has manually suspended this thread,
// so even though from Demon's perspective it thinks this thread
// should run, it will not, because the user has manually called
// SuspendThread or used CREATE_SUSPENDED or whatever.
}
}break;
}
}
}
//////////////////////////
//- rjf: loop, consume win32 debug events until we produce the relevant demon events
//
B32 priority_mode = !dmn_handle_match(dmn_handle_zero(), ctrls->priority_thread);
B32 did_priority_mode = priority_mode;
B32 do_threads_resume = 1;
DMN_W32_EntityNode *first_ran_thread = 0;
DMN_W32_EntityNode *last_ran_thread = 0;
U64 begin_time = os_now_microseconds();
String8List debug_strings = {0};
DMN_Event *debug_strings_event = 0;
@@ -1842,6 +1816,54 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
{
keep_going = 0;
////////////////////////
//- rjf: resume threads that we want to run
//
// if priority mode? => first, just resume priority thread
// if not? => resume all non-priority threads
//
if(do_threads_resume)
{
do_threads_resume = 0;
for(DMN_W32_EntityNode *n = first_run_thread; n != 0; n = n->next)
{
DMN_W32_Entity *thread = n->v;
B32 thread_is_priority = dmn_handle_match(dmn_w32_handle_from_entity(thread), ctrls->priority_thread);
if((priority_mode && !thread_is_priority) ||
(!priority_mode && did_priority_mode && thread_is_priority))
{
continue;
}
DWORD resume_result = ResumeThread(thread->handle);
DMN_W32_EntityNode *n = push_array(scratch.arena, DMN_W32_EntityNode, 1);
SLLQueuePush(first_ran_thread, last_ran_thread, n);
n->v = thread;
switch(resume_result)
{
case 0xffffffffu:
{
// TODO(rjf): error - unknown cause. need to do GetLastError, FormatMessage
}break;
default:
{
DWORD desired_counter = 0;
DWORD current_counter = resume_result - 1;
if(current_counter != desired_counter)
{
// NOTE(rjf): Warning. The user has manually suspended this thread,
// so even though from Demon's perspective it thinks this thread
// should run, it will not, because the user has manually called
// SuspendThread or used CREATE_SUSPENDED or whatever.
}
}break;
}
if(priority_mode && thread_is_priority)
{
break;
}
}
}
////////////////////////
//- rjf: choose win32 resume code
//
@@ -1878,7 +1900,12 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
}
if(resume_good)
{
evt_good = !!WaitForDebugEvent(&evt, 100);
DWORD wait_ms = 100;
if(priority_mode)
{
wait_ms = 30;
}
evt_good = !!WaitForDebugEvent(&evt, wait_ms);
if(evt_good)
{
dmn_w32_shared->resume_needed = 1;
@@ -1891,6 +1918,11 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
(void)err;
keep_going = 1;
}
if(priority_mode)
{
priority_mode = 0;
do_threads_resume = 1;
}
}
}
@@ -2664,7 +2696,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls)
//
ProfScope("suspend threads which ran")
{
for(DMN_W32_EntityNode *n = first_run_thread; n != 0; n = n->next)
for(DMN_W32_EntityNode *n = first_ran_thread; n != 0; n = n->next)
{
DMN_W32_Entity *thread = n->v;
if(thread->kind != DMN_W32_EntityKind_Thread)
+6 -6
View File
@@ -6,7 +6,6 @@
//
//- urgent fixes
// [ ] hardware breakpoints regression (global eval in ctrl)
// [ ] []string being sized by [0], due to `.` applying to first ^string
//
//- memory view
// [ ] have smaller visible range than entire memory
@@ -21,9 +20,6 @@
// [ ] disassembly sometimes has a problem where source line annotations are
// periodically removed/inserted... maybe updating on fs change when we
// shouldn't, non-deterministic line annotation path?
// [ ] process memory cache sometimes is not correctly updating - best repro
// case so far is (for some reason?) only hover evaluation - only spotted
// on laptop in debug builds. g0 ctrl_bindings.bindings initialization.
//
//- watch improvements
// [ ] *ALL* expressions in watch windows need to be editable.
@@ -115,7 +111,6 @@
//- eval improvements
// [ ] maybe add extra caching layer to process memory querying? we pay a pretty
// heavy cost even to just read 8 bytes...
// [ ] evaluate `foo.bar` symbol names without escape hatch?
// [ ] serializing eval view maps (?)
// [ ] EVAL LOOKUP RULES -> currently going 0 -> rdis_count, but we need
// to prioritize the primary rdi
@@ -149,7 +144,6 @@
// [ ] investigate wide-conversion performance
// [ ] oversubscribing cores?
// [ ] conversion crashes?
// [ ] fastpath lookup to determine debug info relevance?
// [ ] live++ investigations - ctrl+alt+f11 in UE?
//
//- memory usage improvements
@@ -189,6 +183,12 @@
// is not correctly incremented.
// [x] output: add option for scroll-to-bottom - ensure this shows up in universal ctx menu
// [x] auto-annotations for non-locals
// [x] []string being sized by [0], due to `.` applying to first ^string
// [x] process memory cache sometimes is not correctly updating - best repro
// case so far is (for some reason?) only hover evaluation - only spotted
// on laptop in debug builds. g0 ctrl_bindings.bindings initialization.
// [x] evaluate `foo.bar` symbol names without escape hatch?
// [x] fastpath lookup to determine debug info relevance?
////////////////////////////////
//~ rjf: Build Options