From 6cf0c7ee928d87c846cac9f3ae156d1384149bcf Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Sun, 8 Sep 2024 13:09:52 -0700 Subject: [PATCH] promote thread freeze state to ctrl entity tree; communicate via msgs for user -> ctrl, and communicate entity state changes back via events for ctrl -> user --- src/ctrl/ctrl_core.c | 121 ++++++++++++++++-------- src/ctrl/ctrl_core.h | 13 ++- src/dbg_engine/dbg_engine_core.c | 100 ++++++-------------- src/dbg_engine/dbg_engine_core.h | 8 -- src/dbg_frontend/dbg_frontend_core.c | 3 +- src/dbg_frontend/dbg_frontend_views.c | 3 +- src/dbg_frontend/dbg_frontend_widgets.c | 6 +- 7 files changed, 129 insertions(+), 125 deletions(-) diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index 93aea4b1..85a6dc10 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -178,7 +178,6 @@ ctrl_msg_deep_copy(Arena *arena, CTRL_Msg *dst, CTRL_Msg *src) dst->env_string_list = str8_list_copy(arena, &src->env_string_list); dst->traps = ctrl_trap_list_copy(arena, &src->traps); dst->user_bps = ctrl_user_breakpoint_list_copy(arena, &src->user_bps); - dst->freeze_state_threads = ctrl_machine_id_handle_pair_list_copy(arena, &src->freeze_state_threads); } //- rjf: list building @@ -272,16 +271,6 @@ ctrl_serialized_string_from_msg_list(Arena *arena, CTRL_MsgList *msgs) str8_serial_push_struct(scratch.arena, &msgs_srlzed, &bp->condition.size); str8_serial_push_data(scratch.arena, &msgs_srlzed, bp->condition.str, bp->condition.size); } - - // rjf: write freeze state thread list - str8_serial_push_struct(scratch.arena, &msgs_srlzed, &msg->freeze_state_threads.count); - for(CTRL_MachineIDHandlePairNode *n = msg->freeze_state_threads.first; n != 0; n = n->next) - { - str8_serial_push_struct(scratch.arena, &msgs_srlzed, &n->v); - } - - // rjf: write freeze state - str8_serial_push_struct(scratch.arena, &msgs_srlzed, &msg->freeze_state_is_frozen); } } String8 string = str8_serial_end(arena, &msgs_srlzed); @@ -394,19 +383,6 @@ ctrl_msg_list_from_serialized_string(Arena *arena, String8 string) bp->condition.str = push_array_no_zero(arena, U8, bp->condition.size); read_off += str8_deserial_read(string, read_off, bp->condition.str, bp->condition.size, 1); } - - // rjf: read freeze state thread list - U64 frozen_thread_count = 0; - read_off += str8_deserial_read_struct(string, read_off, &frozen_thread_count); - for(U64 idx = 0; idx < frozen_thread_count; idx += 1) - { - CTRL_MachineIDHandlePair pair = {0}; - read_off += str8_deserial_read_struct(string, read_off, &pair); - ctrl_machine_id_handle_pair_list_push(arena, &msg->freeze_state_threads, &pair); - } - - // rjf: read freeze state - read_off += str8_deserial_read_struct(string, read_off, &msg->freeze_state_is_frozen); } } return msgs; @@ -510,6 +486,17 @@ ctrl_event_from_serialized_string(Arena *arena, String8 string) //////////////////////////////// //~ rjf: Entity Type Functions +//- rjf: entity list data structures + +internal void +ctrl_entity_list_push(Arena *arena, CTRL_EntityList *list, CTRL_Entity *entity) +{ + CTRL_EntityNode *n = push_array(arena, CTRL_EntityNode, 1); + n->v = entity; + SLLQueuePush(list->first, list->last, n); + list->count += 1; +} + //- rjf: cache creation/destruction internal CTRL_EntityStore * @@ -944,12 +931,28 @@ ctrl_voff_range_from_vaddr_range(CTRL_Entity *module, Rng1U64 vaddr_range) return result; } +internal B32 +ctrl_entity_tree_is_frozen(CTRL_Entity *root) +{ + B32 is_frozen = 1; + for(CTRL_Entity *e = root; e != &ctrl_entity_nil; e = ctrl_entity_rec_depth_first_pre(e, root).next) + { + if(e->kind == CTRL_EntityKind_Thread && !e->is_frozen) + { + is_frozen = 0; + break; + } + } + return is_frozen; +} + //- rjf: entity tree iteration internal CTRL_EntityRec ctrl_entity_rec_depth_first(CTRL_Entity *entity, CTRL_Entity *subtree_root, U64 sib_off, U64 child_off) { CTRL_EntityRec result = {0}; + result.next = &ctrl_entity_nil; if((*MemberFromOffset(CTRL_Entity **, entity, child_off)) != &ctrl_entity_nil) { result.next = *MemberFromOffset(CTRL_Entity **, entity, child_off); @@ -1019,6 +1022,16 @@ ctrl_entity_store_apply_events(CTRL_EntityStore *store, CTRL_EventList *list) CTRL_Entity *thread = ctrl_entity_from_machine_id_handle(store, event->machine_id, event->entity); ctrl_entity_equip_string(store, thread, event->string); }break; + case CTRL_EventKind_ThreadFrozen: + { + CTRL_Entity *thread = ctrl_entity_from_machine_id_handle(store, event->machine_id, event->entity); + thread->is_frozen = 1; + }break; + case CTRL_EventKind_ThreadThawed: + { + CTRL_Entity *thread = ctrl_entity_from_machine_id_handle(store, event->machine_id, event->entity); + thread->is_frozen = 0; + }break; //- rjf: modules case CTRL_EventKind_NewModule: @@ -3100,6 +3113,24 @@ ctrl_thread__entry_point(void *p) evt->timestamp = new_dbgi_timestamp; ctrl_c2u_push_events(&evts); }break; + case CTRL_MsgKind_FreezeThread: + { + CTRL_EventList evts = {0}; + CTRL_Event *evt = ctrl_event_list_push(scratch.arena, &evts); + evt->kind = CTRL_EventKind_ThreadFrozen; + evt->machine_id = msg->machine_id; + evt->entity = msg->entity; + ctrl_c2u_push_events(&evts); + }break; + case CTRL_MsgKind_ThawThread: + { + CTRL_EventList evts = {0}; + CTRL_Event *evt = ctrl_event_list_push(scratch.arena, &evts); + evt->kind = CTRL_EventKind_ThreadThawed; + evt->machine_id = msg->machine_id; + evt->entity = msg->entity; + ctrl_c2u_push_events(&evts); + }break; } } } @@ -4262,18 +4293,10 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) U64 rip = dmn_rip_from_thread(thread->handle); // rjf: determine if thread is frozen - B32 thread_is_frozen = !msg->freeze_state_is_frozen; - for(CTRL_MachineIDHandlePairNode *n = msg->freeze_state_threads.first; n != 0; n = n->next) - { - if(dmn_handle_match(n->v.handle, thread->handle)) - { - thread_is_frozen ^= 1; - break; - } - } + B32 thread_is_frozen = thread->is_frozen; // rjf: not frozen? -> check if stuck & gather if so - if(thread_is_frozen == 0) + if(!thread_is_frozen) { for(DMN_TrapChunkNode *n = user_traps.first; n != 0; n = n->next) { @@ -4352,6 +4375,28 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) } } + ////////////////////////////// + //- rjf: gather frozen threads + // + CTRL_EntityList frozen_threads = {0}; + for(CTRL_Entity *machine = ctrl_state->ctrl_thread_entity_store->root->first; + machine != &ctrl_entity_nil; + machine = machine->next) + { + if(machine->kind != CTRL_EntityKind_Machine) { continue; } + for(CTRL_Entity *process = machine->first; process != &ctrl_entity_nil; process = process->next) + { + if(process->kind != CTRL_EntityKind_Process) { continue; } + for(CTRL_Entity *thread = process->first; thread != &ctrl_entity_nil; thread = thread->next) + { + if(thread->is_frozen) + { + ctrl_entity_list_push(scratch.arena, &frozen_threads, thread); + } + } + } + } + ////////////////////////////// //- rjf: resolve trap net // @@ -4417,14 +4462,14 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) // DMN_RunCtrls run_ctrls = {0}; run_ctrls.ignore_previous_exception = 1; - run_ctrls.run_entity_count = msg->freeze_state_threads.count; + run_ctrls.run_entity_count = frozen_threads.count; run_ctrls.run_entities = push_array(scratch.arena, DMN_Handle, run_ctrls.run_entity_count); - run_ctrls.run_entities_are_unfrozen = !msg->freeze_state_is_frozen; + run_ctrls.run_entities_are_unfrozen = 0; { U64 idx = 0; - for(CTRL_MachineIDHandlePairNode *n = msg->freeze_state_threads.first; n != 0; n = n->next) + for(CTRL_EntityNode *n = frozen_threads.first; n != 0; n = n->next) { - run_ctrls.run_entities[idx] = n->v.handle; + run_ctrls.run_entities[idx] = n->v->handle; idx += 1; } } diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index 346f257d..91d67d2f 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -64,6 +64,7 @@ struct CTRL_Entity CTRL_Entity *parent; CTRL_EntityKind kind; Arch arch; + B32 is_frozen; CTRL_MachineID machine_id; DMN_Handle handle; U64 id; @@ -278,6 +279,8 @@ typedef enum CTRL_MsgKind CTRL_MsgKind_SingleStep, CTRL_MsgKind_SetUserEntryPoints, CTRL_MsgKind_SetModuleDebugInfoPath, + CTRL_MsgKind_FreezeThread, + CTRL_MsgKind_ThawThread, CTRL_MsgKind_COUNT, } CTRL_MsgKind; @@ -307,8 +310,6 @@ struct CTRL_Msg String8List env_string_list; CTRL_TrapList traps; CTRL_UserBreakpointList user_bps; - CTRL_MachineIDHandlePairList freeze_state_threads; // NOTE(rjf): can be frozen or unfrozen, depending on `freeze_state_is_frozen` - B32 freeze_state_is_frozen; }; typedef struct CTRL_MsgNode CTRL_MsgNode; @@ -346,6 +347,10 @@ typedef enum CTRL_EventKind CTRL_EventKind_EndThread, CTRL_EventKind_EndModule, + //- rjf: thread freeze state changes + CTRL_EventKind_ThreadFrozen, + CTRL_EventKind_ThreadThawed, + //- rjf: debug info changes CTRL_EventKind_ModuleDebugInfoPathChange, @@ -713,6 +718,9 @@ internal CTRL_Event ctrl_event_from_serialized_string(Arena *arena, String8 stri //////////////////////////////// //~ rjf: Entity Type Functions +//- rjf: entity list data structures +internal void ctrl_entity_list_push(Arena *arena, CTRL_EntityList *list, CTRL_Entity *entity); + //- rjf: cache creation/destruction internal CTRL_EntityStore *ctrl_entity_store_alloc(void); internal void ctrl_entity_store_release(CTRL_EntityStore *store); @@ -742,6 +750,7 @@ internal U64 ctrl_vaddr_from_voff(CTRL_Entity *module, U64 voff); internal U64 ctrl_voff_from_vaddr(CTRL_Entity *module, U64 vaddr); internal Rng1U64 ctrl_vaddr_range_from_voff_range(CTRL_Entity *module, Rng1U64 voff_range); internal Rng1U64 ctrl_voff_range_from_vaddr_range(CTRL_Entity *module, Rng1U64 vaddr_range); +internal B32 ctrl_entity_tree_is_frozen(CTRL_Entity *root); //- rjf: entity tree iteration internal CTRL_EntityRec ctrl_entity_rec_depth_first(CTRL_Entity *entity, CTRL_Entity *subtree_root, U64 sib_off, U64 child_off); diff --git a/src/dbg_engine/dbg_engine_core.c b/src/dbg_engine/dbg_engine_core.c index bff2c66f..9020a616 100644 --- a/src/dbg_engine/dbg_engine_core.c +++ b/src/dbg_engine/dbg_engine_core.c @@ -1371,7 +1371,6 @@ d_entity_release(D_Entity *entity) log_infof("id: $0x%I64x\n", task->e->id); log_infof("display_string: \"%S\"\n", name); } - d_set_thread_freeze_state(task->e, 0); SLLStackPush(d_state->entities_free[free_list_idx], task->e); d_state->entities_free_count += 1; d_state->entities_active_count -= 1; @@ -1991,59 +1990,6 @@ d_entity_from_name_and_kind(String8 string, D_EntityKind kind) return result; } -//- rjf: entity freezing state - -internal void -d_set_thread_freeze_state(D_Entity *thread, B32 frozen) -{ - D_Handle thread_handle = d_handle_from_entity(thread); - D_HandleNode *already_frozen_node = d_handle_list_find(&d_state->frozen_threads, thread_handle); - B32 is_frozen = !!already_frozen_node; - B32 should_be_frozen = frozen; - - // rjf: not frozen => frozen - if(!is_frozen && should_be_frozen) - { - D_HandleNode *node = d_state->free_handle_node; - if(node) - { - SLLStackPop(d_state->free_handle_node); - } - else - { - node = push_array(d_state->arena, D_HandleNode, 1); - } - node->handle = thread_handle; - d_handle_list_push_node(&d_state->frozen_threads, node); - } - - // rjf: frozen => not frozen - if(is_frozen && !should_be_frozen) - { - d_handle_list_remove(&d_state->frozen_threads, already_frozen_node); - SLLStackPush(d_state->free_handle_node, already_frozen_node); - } -} - -internal B32 -d_entity_is_frozen(D_Entity *entity) -{ - B32 is_frozen = !d_entity_is_nil(entity); - for(D_Entity *e = entity; !d_entity_is_nil(e); e = d_entity_rec_depth_first_pre(e, entity).next) - { - if(e->kind == D_EntityKind_Thread) - { - B32 thread_is_frozen = !!d_handle_list_find(&d_state->frozen_threads, d_handle_from_entity(e)); - if(!thread_is_frozen) - { - is_frozen = 0; - break; - } - } - } - return is_frozen; -} - //////////////////////////////// //~ rjf: Command Stateful Functions @@ -3313,9 +3259,24 @@ d_hash_from_ctrl_param_state(D_BreakpointArray *breakpoints) // rjf: build data strings of all param data String8List strings = {0}; { - for(D_HandleNode *n = d_state->frozen_threads.first; n != 0; n = n->next) + for(CTRL_Entity *machine = d_state->ctrl_entity_store->root->first; + machine != &ctrl_entity_nil; + machine = machine->next) { - str8_list_push(scratch.arena, &strings, str8_struct(&n->handle)); + if(machine->kind != CTRL_EntityKind_Machine) { continue; } + for(CTRL_Entity *process = machine->first; + process != &ctrl_entity_nil; + process = process->next) + { + if(process->kind != CTRL_EntityKind_Process) { continue; } + for(CTRL_Entity *thread = process->first; + thread != &ctrl_entity_nil; + thread = thread->next) + { + if(thread->kind != CTRL_EntityKind_Thread) { continue; } + str8_list_push(scratch.arena, &strings, str8_struct(&thread->is_frozen)); + } + } } for(U64 idx = 0; idx < breakpoints->count; idx += 1) { @@ -6515,7 +6476,6 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, DI_ case CTRL_EventKind_EndThread: { D_Entity *thread = d_entity_from_ctrl_handle(event->machine_id, event->entity); - d_set_thread_freeze_state(thread, 0); d_entity_mark_for_deletion(thread); }break; @@ -6961,11 +6921,10 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, DI_ case D_CmdKind_Continue: { B32 good_to_run = 0; - D_EntityList machines = d_query_cached_entity_list_with_kind(D_EntityKind_Machine); - for(D_EntityNode *n = machines.first; n != 0; n = n->next) + CTRL_EntityList threads = ctrl_entity_list_from_kind(d_state->ctrl_entity_store, CTRL_EntityKind_Thread); + for(CTRL_EntityNode *n = threads.first; n != 0; n = n->next) { - D_Entity *machine = n->entity; - if(!d_entity_is_frozen(machine)) + if(!n->v->is_frozen) { good_to_run = 1; break; @@ -7001,7 +6960,7 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, DI_ d_cmd_list_push(arena, cmds, &p, d_cmd_spec_from_kind(D_CmdKind_Error)); } } - else if(d_entity_is_frozen(d_thread)) + else if(thread->is_frozen) { D_CmdParams p = params; p.string = str8_lit("Must thaw selected thread before stepping."); @@ -7928,7 +7887,12 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, DI_ { if(e->kind == D_EntityKind_Thread) { - d_set_thread_freeze_state(e, should_freeze); + CTRL_Entity *thread = ctrl_entity_from_machine_id_handle(d_state->ctrl_entity_store, e->ctrl_machine_id, e->ctrl_handle); + thread->is_frozen = should_freeze; + CTRL_Msg msg = {should_freeze ? CTRL_MsgKind_FreezeThread : CTRL_MsgKind_ThawThread}; + msg.machine_id = thread->machine_id; + msg.entity = thread->handle; + d_push_ctrl_msg(&msg); } } }break; @@ -8403,16 +8367,6 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, DI_ ctrl_user_breakpoint_list_push(scratch.arena, &msg.user_bps, &ctrl_user_bp); } } - for(D_HandleNode *n = d_state->frozen_threads.first; n != 0; n = n->next) - { - D_Entity *thread = d_entity_from_handle(n->handle); - if(!d_entity_is_nil(thread)) - { - CTRL_MachineIDHandlePair pair = {thread->ctrl_machine_id, thread->ctrl_handle}; - ctrl_machine_id_handle_pair_list_push(scratch.arena, &msg.freeze_state_threads, &pair); - } - } - msg.freeze_state_is_frozen = 1; } // rjf: push msg diff --git a/src/dbg_engine/dbg_engine_core.h b/src/dbg_engine/dbg_engine_core.h index 1f3da902..82b68022 100644 --- a/src/dbg_engine/dbg_engine_core.h +++ b/src/dbg_engine/dbg_engine_core.h @@ -1047,10 +1047,6 @@ struct D_State U64 view_rule_spec_table_size; D_ViewRuleSpec **view_rule_spec_table; - // rjf: freeze state - D_HandleList frozen_threads; - D_HandleNode *free_handle_node; - // rjf: control thread user -> ctrl driving state Arena *ctrl_last_run_arena; D_RunKind ctrl_last_run_kind; @@ -1305,10 +1301,6 @@ internal D_Entity *d_entity_from_ctrl_handle(CTRL_MachineID machine_id, DMN_Hand internal D_Entity *d_entity_from_ctrl_id(CTRL_MachineID machine_id, U32 id); internal D_Entity *d_entity_from_name_and_kind(String8 string, D_EntityKind kind); -//- rjf: entity freezing state -internal void d_set_thread_freeze_state(D_Entity *thread, B32 frozen); -internal B32 d_entity_is_frozen(D_Entity *entity); - //////////////////////////////// //~ rjf: Command Stateful Functions diff --git a/src/dbg_frontend/dbg_frontend_core.c b/src/dbg_frontend/dbg_frontend_core.c index 9e71f979..d87906d1 100644 --- a/src/dbg_frontend/dbg_frontend_core.c +++ b/src/dbg_frontend/dbg_frontend_core.c @@ -2166,6 +2166,7 @@ df_window_update_and_render(Arena *arena, DF_Window *ws, D_CmdList *cmds) DF_Palette(DF_PaletteCode_ImplicitButton) { D_Entity *entity = d_entity_from_handle(ws->entity_ctx_menu_entity); + CTRL_Entity *entity_ctrl = ctrl_entity_from_machine_id_handle(d_state->ctrl_entity_store, entity->ctrl_machine_id, entity->ctrl_handle); DF_IconKind entity_icon = df_entity_kind_icon_kind_table[entity->kind]; D_EntityKindFlags kind_flags = d_entity_kind_flags_table[entity->kind]; String8 display_name = d_display_string_from_entity(scratch.arena, entity); @@ -2333,7 +2334,7 @@ df_window_update_and_render(Arena *arena, DF_Window *ws, D_CmdList *cmds) // rjf: freezing if(kind_flags & D_EntityKindFlag_CanFreeze) { - B32 is_frozen = d_entity_is_frozen(entity); + B32 is_frozen = ctrl_entity_tree_is_frozen(entity_ctrl); ui_set_next_palette(df_palette_from_code(is_frozen ? DF_PaletteCode_NegativePopButton : DF_PaletteCode_PositivePopButton)); if(is_frozen && ui_clicked(df_icon_buttonf(DF_IconKind_Locked, 0, "Thaw###freeze_thaw"))) { diff --git a/src/dbg_frontend/dbg_frontend_views.c b/src/dbg_frontend/dbg_frontend_views.c index 689ce13f..85bc80d5 100644 --- a/src/dbg_frontend/dbg_frontend_views.c +++ b/src/dbg_frontend/dbg_frontend_views.c @@ -5606,6 +5606,7 @@ DF_VIEW_UI_FUNCTION_DEF(scheduler) idx += 1) { D_Entity *entity = items.v[idx].entity; + CTRL_Entity *entity_ctrl = ctrl_entity_from_machine_id_handle(d_state->ctrl_entity_store, entity->ctrl_machine_id, entity->ctrl_handle); B32 row_is_selected = (cursor.y == (S64)(idx+1)); F32 depth = 0.f; if(query.size == 0) switch(entity->kind) @@ -5628,7 +5629,7 @@ DF_VIEW_UI_FUNCTION_DEF(scheduler) UI_TableCellSized(ui_em(1.5f*depth, 1.f)) {} UI_TableCellSized(ui_em(2.25f, 1.f)) UI_FocusHot((row_is_selected && cursor.x == 0) ? UI_FocusKind_On : UI_FocusKind_Off) { - B32 frozen = d_entity_is_frozen(entity); + B32 frozen = ctrl_entity_tree_is_frozen(entity_ctrl); UI_Palette *palette = ui_top_palette(); if(frozen) { diff --git a/src/dbg_frontend/dbg_frontend_widgets.c b/src/dbg_frontend/dbg_frontend_widgets.c index 5e97b9a8..6773f52b 100644 --- a/src/dbg_frontend/dbg_frontend_widgets.c +++ b/src/dbg_frontend/dbg_frontend_widgets.c @@ -1047,6 +1047,7 @@ df_code_slice(DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *prefe { continue; } + CTRL_Entity *thread_ctrl = ctrl_entity_from_machine_id_handle(d_state->ctrl_entity_store, thread->ctrl_machine_id, thread->ctrl_handle); U64 unwind_count = (thread == selected_thread) ? d_regs()->unwind_count : 0; U64 thread_rip_vaddr = d_query_cached_rip_from_thread_unwind(thread, unwind_count); D_Entity *process = d_entity_ancestor_from_kind(thread, D_EntityKind_Process); @@ -1105,7 +1106,7 @@ df_code_slice(DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *prefe u->thread_color = color; u->alive_t = thread->alive_t; u->is_selected = (thread == selected_thread); - u->is_frozen = d_entity_is_frozen(thread); + u->is_frozen = !!thread_ctrl->is_frozen; u->do_lines = df_setting_val_from_code(DF_SettingCode_ThreadLines).s32; u->do_glow = df_setting_val_from_code(DF_SettingCode_ThreadGlow).s32; ui_box_equip_custom_draw(thread_box, df_thread_box_draw_extensions, u); @@ -1205,6 +1206,7 @@ df_code_slice(DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *prefe { continue; } + CTRL_Entity *thread_ctrl = ctrl_entity_from_machine_id_handle(d_state->ctrl_entity_store, thread->ctrl_machine_id, thread->ctrl_handle); U64 unwind_count = (thread == selected_thread) ? d_regs()->unwind_count : 0; U64 thread_rip_vaddr = d_query_cached_rip_from_thread_unwind(thread, unwind_count); D_Entity *process = d_entity_ancestor_from_kind(thread, D_EntityKind_Process); @@ -1263,7 +1265,7 @@ df_code_slice(DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *prefe u->thread_color = color; u->alive_t = thread->alive_t; u->is_selected = (thread == selected_thread); - u->is_frozen = d_entity_is_frozen(thread); + u->is_frozen = !!thread_ctrl->is_frozen; ui_box_equip_custom_draw(thread_box, df_thread_box_draw_extensions, u); // rjf: fill out progress t (progress into range of current line's