sketch out tline -> vline text wrapping cache types; first pass of async call stack tree builder

This commit is contained in:
Ryan Fleury
2025-08-11 13:46:06 -07:00
parent e1d6b9f319
commit 056604fb94
11 changed files with 315 additions and 20 deletions
+177 -7
View File
@@ -1451,6 +1451,14 @@ ctrl_scope_close(CTRL_Scope *scope)
os_condition_variable_broadcast(t->stripe->cv);
SLLStackPush(ctrl_tctx->free_call_stack_touch, t);
}
for(U64 idx = 0; idx < scope->call_stack_tree_touch_count; idx += 1)
{
ins_atomic_u64_dec_eval(&ctrl_state->call_stack_tree_cache.scope_touch_count);
}
if(scope->call_stack_tree_touch_count != 0)
{
os_condition_variable_broadcast(ctrl_state->call_stack_tree_cache.cv);
}
SLLStackPush(ctrl_tctx->free_scope, scope);
}
@@ -3407,7 +3415,7 @@ ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U
//~ rjf: Call Stack Cache Functions
internal CTRL_CallStack
ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_Entity *thread, B32 high_priority, U64 endt_us)
ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_Handle thread_handle, B32 high_priority, U64 endt_us)
{
CTRL_CallStack call_stack = {0};
CTRL_CallStackCache *cache = &ctrl_state->call_stack_cache;
@@ -3415,8 +3423,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_
//////////////////////////////
//- rjf: unpack thread
//
CTRL_Handle handle = thread->handle;
U64 hash = ctrl_hash_from_handle(handle);
U64 hash = ctrl_hash_from_handle(thread_handle);
U64 slot_idx = hash%cache->slots_count;
U64 stripe_idx = slot_idx%cache->stripes_count;
CTRL_CallStackCacheSlot *slot = &cache->slots[slot_idx];
@@ -3439,7 +3446,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_
CTRL_CallStackCacheNode *node = 0;
for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next)
{
if(ctrl_handle_match(n->thread, handle))
if(ctrl_handle_match(n->thread, thread_handle))
{
node = n;
node_exists = 1;
@@ -3478,7 +3485,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_
CTRL_CallStackCacheNode *node = 0;
for(CTRL_CallStackCacheNode *n = slot->first; n != 0; n = n->next)
{
if(ctrl_handle_match(n->thread, handle))
if(ctrl_handle_match(n->thread, thread_handle))
{
node = n;
break;
@@ -3488,7 +3495,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_
{
node = push_array(stripe->arena, CTRL_CallStackCacheNode, 1);
DLLPushBack(slot->first, slot->last, node);
node->thread = thread->handle;
node->thread = thread_handle;
}
if(node->working_count == 0)
{
@@ -3500,7 +3507,7 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_
//- rjf: request if needed
if(node_to_request != 0)
{
if(ctrl_u2csb_enqueue_req(thread->handle, endt_us))
if(ctrl_u2csb_enqueue_req(thread_handle, endt_us))
{
async_push_work(ctrl_call_stack_build_work, .priority = high_priority ? ASYNC_Priority_High : ASYNC_Priority_Low);
}
@@ -3514,6 +3521,47 @@ ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_
return call_stack;
}
////////////////////////////////
//~ rjf: Call Stack Tree Cache Functions
internal CTRL_CallStackTree
ctrl_call_stack_tree(CTRL_Scope *scope, U64 endt_us)
{
CTRL_CallStackTree result = {&ctrl_call_stack_tree_node_nil};
{
U64 reg_gen = ctrl_reg_gen();
U64 mem_gen = ctrl_mem_gen();
CTRL_CallStackTreeCache *cache = &ctrl_state->call_stack_tree_cache;
OS_MutexScopeR(cache->rw_mutex) for(;;)
{
// rjf: unpack cache/time state
B32 is_stale = (cache->reg_gen != reg_gen || cache->mem_gen != mem_gen);
B32 out_of_time = (os_now_microseconds() >= endt_us);
// rjf: is stale? -> request new calculation
if(is_stale && !ins_atomic_u64_eval_cond_assign(&cache->request_count, 1, 0))
{
os_rw_mutex_drop_r(cache->rw_mutex);
async_push_work(ctrl_call_stack_tree_build_work);
os_rw_mutex_take_r(cache->rw_mutex);
}
// rjf: is not stale, or we're out of time? -> grab cached result & touch, exit
if(!is_stale || out_of_time)
{
result = cache->tree;
ins_atomic_u64_inc_eval(&cache->scope_touch_count);
scope->call_stack_tree_touch_count += 1;
break;
}
// rjf: wait for new results
os_condition_variable_wait_rw_r(cache->cv, cache->rw_mutex, endt_us);
}
}
return result;
}
////////////////////////////////
//~ rjf: Halting All Attached Processes
@@ -7261,3 +7309,125 @@ ASYNC_WORK_DEF(ctrl_call_stack_build_work)
scratch_end(scratch);
return 0;
}
////////////////////////////////
//~ rjf: Asynchronous Call Stack Tree Building Functions
ASYNC_WORK_DEF(ctrl_call_stack_tree_build_work)
{
Temp scratch = scratch_begin(0, 0);
CTRL_Scope *ctrl_scope = ctrl_scope_open();
//- rjf: gather list of all thread handles
U64 threads_count = 0;
CTRL_Handle *threads = 0;
CTRL_Handle *threads_processes = 0;
Arch *threads_arches = 0;
OS_MutexScopeR(ctrl_state->ctrl_thread_entity_ctx_rw_mutex)
{
CTRL_EntityCtx *ctx = &ctrl_state->ctrl_thread_entity_store->ctx;
CTRL_EntityArray thread_entities = ctrl_entity_array_from_kind(ctx, CTRL_EntityKind_Thread);
threads_count = thread_entities.count;
threads = push_array(scratch.arena, CTRL_Handle, threads_count);
threads_processes = push_array(scratch.arena, CTRL_Handle, threads_count);
threads_arches = push_array(scratch.arena, Arch, threads_count);
for EachIndex(idx, threads_count)
{
threads[idx] = thread_entities.v[idx]->handle;
threads_processes[idx] = thread_entities.v[idx]->parent->handle;
threads_arches[idx] = thread_entities.v[idx]->arch;
}
}
//- rjf: gather all callstacks
U64 pre_mem_gen = ctrl_mem_gen();
U64 pre_reg_gen = ctrl_reg_gen();
CTRL_CallStack *call_stacks = push_array(scratch.arena, CTRL_CallStack, threads_count);
{
for EachIndex(idx, threads_count)
{
call_stacks[idx] = ctrl_call_stack_from_thread(ctrl_scope, threads[idx], 0, os_now_microseconds()+1000);
}
}
U64 post_mem_gen = ctrl_mem_gen();
U64 post_reg_gen = ctrl_reg_gen();
//- rjf: build call stack tree
Arena *arena = 0;
CTRL_CallStackTree tree = {&ctrl_call_stack_tree_node_nil};
if(pre_mem_gen == post_mem_gen &&
pre_reg_gen == post_reg_gen)
{
arena = arena_alloc();
tree.root = push_array(arena, CTRL_CallStackTreeNode, 1);
MemoryCopyStruct(tree.root, &ctrl_call_stack_tree_node_nil);
for EachIndex(thread_idx, threads_count)
{
CTRL_Handle thread = threads[thread_idx];
CTRL_Handle process = threads_processes[thread_idx];
Arch arch = threads_arches[thread_idx];
CTRL_CallStack call_stack = call_stacks[thread_idx];
CTRL_CallStackTreeNode *thread_node = tree.root;
for EachIndex(frame_idx, call_stack.frames_count)
{
U64 vaddr = regs_rip_from_arch_block(arch, call_stack.frames[frame_idx].regs);
U64 depth = call_stack.frames[frame_idx].inline_depth;
CTRL_CallStackTreeNode *next_node = &ctrl_call_stack_tree_node_nil;
for(CTRL_CallStackTreeNode *child = thread_node->first; child != &ctrl_call_stack_tree_node_nil; child = child->next)
{
if(ctrl_handle_match(child->process, process) && child->vaddr == vaddr && child->depth == depth)
{
next_node = child;
break;
}
}
if(next_node == &ctrl_call_stack_tree_node_nil)
{
next_node = push_array(arena, CTRL_CallStackTreeNode, 1);
MemoryCopyStruct(next_node, &ctrl_call_stack_tree_node_nil);
SLLQueuePush_NZ(&ctrl_call_stack_tree_node_nil, thread_node->first, thread_node->last, next_node, next);
next_node->parent = thread_node;
thread_node->child_count += 1;
}
thread_node = next_node;
}
ctrl_handle_list_push(arena, &thread_node->threads, &thread);
for(CTRL_CallStackTreeNode *n = thread_node; n != &ctrl_call_stack_tree_node_nil; n = n->parent)
{
n->all_descendant_threads_count += 1;
}
}
}
//- rjf: commit to cache
Arena *old_arena = 0;
CTRL_CallStackTreeCache *cache = &ctrl_state->call_stack_tree_cache;
if(tree.root != &ctrl_call_stack_tree_node_nil)
{
OS_MutexScopeW(cache->rw_mutex) for(;;)
{
if(cache->scope_touch_count == 0)
{
old_arena = cache->arena;
cache->arena = arena;
cache->tree = tree;
cache->reg_gen = post_reg_gen;
cache->mem_gen = post_mem_gen;
cache->request_count -= 1;
break;
}
os_condition_variable_wait_rw_w(cache->cv, cache->rw_mutex, max_U64);
}
}
os_condition_variable_broadcast(cache->cv);
//- rjf: release old arena
if(old_arena)
{
arena_release(old_arena);
}
ctrl_scope_close(ctrl_scope);
scratch_end(scratch);
return 0;
}
+60 -1
View File
@@ -271,6 +271,30 @@ struct CTRL_CallStack
U64 concrete_frames_count;
};
////////////////////////////////
//~ rjf: Call Stack Tree Types
typedef struct CTRL_CallStackTreeNode CTRL_CallStackTreeNode;
struct CTRL_CallStackTreeNode
{
CTRL_CallStackTreeNode *first;
CTRL_CallStackTreeNode *last;
CTRL_CallStackTreeNode *next;
CTRL_CallStackTreeNode *parent;
U64 child_count;
CTRL_Handle process;
U64 vaddr;
U64 depth;
CTRL_HandleList threads;
U64 all_descendant_threads_count;
};
typedef struct CTRL_CallStackTree CTRL_CallStackTree;
struct CTRL_CallStackTree
{
CTRL_CallStackTreeNode *root;
};
////////////////////////////////
//~ rjf: Trap Types
@@ -700,6 +724,22 @@ struct CTRL_ModuleImageInfoCache
CTRL_ModuleImageInfoCacheStripe *stripes;
};
////////////////////////////////
//~ rjf: Call Stack Tree Cache Types
typedef struct CTRL_CallStackTreeCache CTRL_CallStackTreeCache;
struct CTRL_CallStackTreeCache
{
Arena *arena;
CTRL_CallStackTree tree;
OS_Handle cv;
OS_Handle rw_mutex;
U64 reg_gen;
U64 mem_gen;
U64 scope_touch_count;
U64 request_count;
};
////////////////////////////////
//~ rjf: Touched Debug Info Directory Cache
@@ -746,6 +786,7 @@ struct CTRL_Scope
CTRL_Scope *next;
CTRL_ScopeCallStackTouch *first_call_stack_touch;
CTRL_ScopeCallStackTouch *last_call_stack_touch;
U64 call_stack_tree_touch_count;
};
typedef struct CTRL_TCTX CTRL_TCTX;
@@ -791,6 +832,7 @@ struct CTRL_State
CTRL_ThreadRegCache thread_reg_cache;
CTRL_CallStackCache call_stack_cache;
CTRL_ModuleImageInfoCache module_image_info_cache;
CTRL_CallStackTreeCache call_stack_tree_cache;
// rjf: generations
U64 run_gen;
@@ -867,6 +909,13 @@ read_only global CTRL_Entity ctrl_entity_nil =
&ctrl_entity_nil,
&ctrl_entity_nil,
};
read_only global CTRL_CallStackTreeNode ctrl_call_stack_tree_node_nil =
{
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
};
thread_static CTRL_TCTX *ctrl_tctx = 0;
thread_static CTRL_EntityCtxLookupAccel *ctrl_entity_ctx_lookup_accel = 0;
@@ -1071,7 +1120,12 @@ internal CTRL_CallStackFrame *ctrl_call_stack_frame_from_unwind_and_inline_depth
////////////////////////////////
//~ rjf: Call Stack Cache Functions
internal CTRL_CallStack ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_EntityCtx *entity_ctx, CTRL_Entity *thread, B32 high_priority, U64 endt_us);
internal CTRL_CallStack ctrl_call_stack_from_thread(CTRL_Scope *scope, CTRL_Handle thread_handle, B32 high_priority, U64 endt_us);
////////////////////////////////
//~ rjf: Call Stack Tree Cache Functions
internal CTRL_CallStackTree ctrl_call_stack_tree(CTRL_Scope *scope, U64 endt_us);
////////////////////////////////
//~ rjf: Halting All Attached Processes
@@ -1155,4 +1209,9 @@ internal void ctrl_u2csb_dequeue_req(CTRL_Handle *out_thread);
//- rjf: entry point
ASYNC_WORK_DEF(ctrl_call_stack_build_work);
////////////////////////////////
//~ rjf: Asynchronous Call Stack Tree Building Functions
ASYNC_WORK_DEF(ctrl_call_stack_tree_build_work);
#endif // CTRL_CORE_H
+2 -2
View File
@@ -1209,7 +1209,7 @@ d_query_cached_rip_from_thread_unwind(CTRL_Entity *thread, U64 unwind_count)
else
{
CTRL_Scope *ctrl_scope = ctrl_scope_open();
CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, 0);
CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, thread->handle, 1, 0);
if(callstack.concrete_frames_count != 0)
{
result = regs_rip_from_arch_block(thread->arch, callstack.concrete_frames[unwind_count%callstack.concrete_frames_count]->regs);
@@ -1889,7 +1889,7 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P
CTRL_Scope *ctrl_scope = ctrl_scope_open();
// rjf: thread => call stack
CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000);
CTRL_CallStack callstack = ctrl_call_stack_from_thread(ctrl_scope, thread->handle, 1, os_now_microseconds()+10000);
// rjf: use first unwind frame to generate trap
if(callstack.concrete_frames_count > 1)
+1 -1
View File
@@ -424,7 +424,7 @@ RD_NameSchemaInfo rd_name_schema_info_table[24] =
{str8_lit_comp("window"), str8_lit_comp("x:\n{\n //- rjf: text rasterization settings\n @default(1) @display_name('Smooth UI Text') @description(\"Controls whether or not UI text is fully anti-aliased, for a smoother appearance.\")\n 'smooth_ui_text': bool,\n @default(1) @display_name('Hint UI Text') @description(\"Controls whether or not UI text is hinted, for better text readability at small sizes.\")\n 'hint_ui_text': bool,\n @default(0) @display_name('Smooth Code Text') @description(\"Controls whether or not code text is fully anti-aliased, for a smoother appearance.\")\n 'smooth_code_text': bool,\n @default(1) @display_name('Hint Code Text') @description(\"Controls whether or not code text is hinted, for better text readability at small sizes.\")\n 'hint_code_text': bool,\n @default(11) @display_name('Window Font Size') @description(\"Controls the window's default font size. Does not apply to tabs with their own font size set.\")\n 'font_size': @range[6, 72] u64,\n\n //- rjf: size settings\n @default(3.f) @display_name('Window Row Height') @description(\"Controls the window's default row height, in multiples of the font size. Does not apply to tabs with their own row height set.\")\n 'row_height': @range[1.75f, 5.f] f32,\n @default(3.f) @description(\"Controls the height of tabs, in multiples of the font size.\")\n 'tab_height': @range[1.75f, 5.f] f32,\n\n //- rjf: theme settings\n @default(1) @display_name('Use Project Theme') @description(\"Prefer using the project theme for this window, if any. If off, only the user's theme settings will be used.\")\n 'use_project_theme': bool,\n}\n")},
{str8_lit_comp("tab"), str8_lit_comp("@row_commands(@file copy_tab_full_path, @file show_file_in_explorer, duplicate_tab, close_tab)\nx:\n{\n @override @display_name('Tab Font Size') @description(\"Controls the tab's font size.\") @no_callee_helper\n 'font_size': @range[6, 72] u64,\n}\n")},
{str8_lit_comp("watch"), str8_lit_comp("@inherit(tab) x:\n{\n @override @display_name('Tab Row Height') @description(\"Controls the tab's row height, in multiples of the font size.\")\n 'row_height': @range[1.75f, 5.f] f32,\n 'label': code_string,\n @description(\"The root expression which is evaluated to produce the watch window.\")\n 'expression': expr_string,\n @no_expand 'watches': query,\n}\n")},
{str8_lit_comp("text"), str8_lit_comp("@inherit(tab) @expand_commands(@output clear_output) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change':bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")},
{str8_lit_comp("text"), str8_lit_comp("@inherit(tab) @expand_commands(@output clear_output) x:\n{\n @description(\"An expression to describe data which should be viewed as text or code.\")\n 'expression': expr_string,\n @description(\"The language that the text should be interpreted as being within. Used for syntax highlighting and other parsing features.\")\n 'lang': code_string,\n @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers':bool,\n @no_callee_helper @default(1) @display_name('Line Wrapping') @description(\"Splits textual lines into multiple visual lines, so that all text is within the visible area.\")\n 'line_wrapping': bool,\n @no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description(\"Scrolls to the bottom if the text is changed.\")\n 'scroll_to_bottom_on_change': bool,\n @no_callee_helper @no_revert @default(0) @display_name('Transient') @description(\"Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.\")\n 'auto': bool,\n}\n")},
{str8_lit_comp("disasm"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression to describe the base address or offset of the disassembly.\")\n 'expression': expr_string,\n 'arch': code_string,\n 'syntax': code_string,\n 'size': expr_string,\n @no_callee_helper @default(1) @description(\"Controls whether or not addresses are shown in the disassembly text.\")\n 'show_addresses': bool,\n @no_callee_helper @default(0) @description(\"Controls whether or not code bytes are shown in the disassembly text.\")\n 'show_code_bytes': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not source lines, corresponding to disassembly instruction ranges, are shown in the disassembly text.\")\n 'show_source_lines': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not disassembly text is decorated with symbol names.\")\n 'show_symbol_names': bool,\n @no_callee_helper @default(1) @description(\"Controls whether or not line numbers are shown.\")\n 'show_line_numbers': bool,\n\n}\n")},
{str8_lit_comp("memory"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as memory.\")\n 'expression': expr_string,\n @display_name(\"Address Range Size\") @description(\"The number of bytes of the viewed memory range.\")\n 'size': expr_string,\n @display_name(\"Cursor Address\") @description(\"The address of the cursor.\")\n 'cursor': expr_string,\n @display_name(\"Cursor Size\") @description(\"The size, in bytes, of the cursor.\")\n 'cursor_size': @range[1, 16] u64,\n @default(16) @description(\"The number of columns to build before building new rows.\")\n 'num_columns': @range[1, 64] u64,\n @default(1) @display_name(\"Track Mark To Cursor\") @description(\"Ensures that the mark always follows the cursor, if the cursor value is updated.\")\n 'track_mark_to_cursor': bool,\n}\n")},
{str8_lit_comp("bitmap"), str8_lit_comp("@inherit(tab) x:\n{\n @description(\"An expression which refers to the base address of data which should be viewed as a bitmap.\")\n 'expression': expr_string,\n @description(\"An expression describing the width of the bitmap, in pixels.\") @order(0) 'w': u64,\n @description(\"An expression describing the height of the bitmap, in pixels.\") @order(1) 'h': u64,\n @display_name(\"Bitmap Format\") @description(\"The pixel format that the bitmap data should be interpreted as being within.\")\n 'fmt': code_string,\n}\n")},
+3 -1
View File
@@ -481,8 +481,10 @@ RD_VocabTable:
'lang': code_string,
@default(1) @description("Controls whether or not line numbers are shown.")
'show_line_numbers':bool,
@no_callee_helper @default(1) @display_name('Line Wrapping') @description("Splits textual lines into multiple visual lines, so that all text is within the visible area.")
'line_wrapping': bool,
@no_callee_helper @default(0) @display_name('Scroll To Bottom On Change') @description("Scrolls to the bottom if the text is changed.")
'scroll_to_bottom_on_change':bool,
'scroll_to_bottom_on_change': bool,
@no_callee_helper @no_revert @default(0) @display_name('Transient') @description("Controls whether or not this tab will be automatically replaced by the debugger when it snaps to new source code locations.")
'auto': bool,
}
+4 -4
View File
@@ -1817,7 +1817,7 @@ rd_eval_space_read(void *u, E_Space space, void *out, Rng1U64 range)
case CTRL_EntityKind_Thread:
{
CTRL_Scope *ctrl_scope = ctrl_scope_open();
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, 1, rd_state->frame_eval_memread_endt_us);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity->handle, 1, rd_state->frame_eval_memread_endt_us);
U64 concrete_frame_idx = e_interpret_ctx->reg_unwind_count;
if(concrete_frame_idx < call_stack.concrete_frames_count)
{
@@ -6469,7 +6469,7 @@ rd_window_frame(void)
Vec4F32 symbol_color = ui_color_from_name(str8_lit("code_symbol"));
CTRL_Entity *process = ctrl_entity_ancestor_from_kind(ctrl_entity, CTRL_EntityKind_Process);
B32 call_stack_high_priority = ctrl_handle_match(ctrl_entity->handle, rd_base_regs()->thread);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, ctrl_entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, ctrl_entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0);
if(call_stack.frames_count != 0)
{
ui_spacer(ui_em(1.5f, 1.f));
@@ -16231,7 +16231,7 @@ rd_frame(void)
{
CTRL_Scope *ctrl_scope = ctrl_scope_open();
CTRL_Entity *thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_base_regs()->thread);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, thread->handle, 1, os_now_microseconds()+10000);
CTRL_CallStackFrame *frame = ctrl_call_stack_frame_from_unwind_and_inline_depth(&call_stack, rd_regs()->unwind_count, rd_regs()->inline_depth);
if(frame == 0)
{
@@ -16250,7 +16250,7 @@ rd_frame(void)
{
CTRL_Scope *ctrl_scope = ctrl_scope_open();
CTRL_Entity *thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_base_regs()->thread);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, thread, 1, os_now_microseconds()+10000);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, thread->handle, 1, os_now_microseconds()+10000);
CTRL_CallStackFrame *current_frame = ctrl_call_stack_frame_from_unwind_and_inline_depth(&call_stack, rd_regs()->unwind_count, rd_regs()->inline_depth);
CTRL_CallStackFrame *next_frame = current_frame;
if(current_frame != 0) switch(kind)
+14 -1
View File
@@ -968,7 +968,7 @@ E_TYPE_IREXT_FUNCTION_DEF(call_stack)
B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread);
accel->arch = entity->arch;
accel->process = ctrl_process_from_entity(entity)->handle;
accel->call_stack = ctrl_call_stack_from_thread(rd_state->frame_ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0);
accel->call_stack = ctrl_call_stack_from_thread(rd_state->frame_ctrl_scope, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0);
}
scratch_end(scratch);
}
@@ -1538,6 +1538,19 @@ E_TYPE_EXPAND_RANGE_FUNCTION_DEF(ctrl_entities)
}
}
////////////////////////////////
//~ rjf: Call Stack Tree Type Hooks
E_TYPE_EXPAND_INFO_FUNCTION_DEF(call_stack_tree)
{
}
E_TYPE_EXPAND_RANGE_FUNCTION_DEF(call_stack_tree)
{
}
////////////////////////////////
//~ rjf: Debug Info Tables Eval Hooks
+6
View File
@@ -100,6 +100,12 @@ E_TYPE_ACCESS_FUNCTION_DEF(ctrl_entities);
E_TYPE_EXPAND_INFO_FUNCTION_DEF(ctrl_entities);
E_TYPE_EXPAND_RANGE_FUNCTION_DEF(ctrl_entities);
////////////////////////////////
//~ rjf: Call Stack Tree Type Hooks
E_TYPE_EXPAND_INFO_FUNCTION_DEF(call_stack_tree);
E_TYPE_EXPAND_RANGE_FUNCTION_DEF(call_stack_tree);
////////////////////////////////
//~ rjf: Debug Info Tables Eval Hooks
+17 -2
View File
@@ -104,6 +104,21 @@ rd_code_view_build(Arena *arena, RD_CodeViewState *cv, RD_CodeViewBuildFlags fla
}
}
//////////////////////////////
//- rjf: set up wrap cache
//
if(cv->wrap_arena == 0)
{
cv->wrap_arena = rd_push_view_arena();
}
if(cv->wrap_total_vline_count == 0)
{
arena_clear(cv->wrap_arena);
cv->wrap_total_vline_count = text_info->lines_count;
cv->wrap_cache_slots_count = text_info->lines_count/64;
cv->wrap_cache_slots = push_array(cv->wrap_arena, RD_CodeViewTLineWrapCacheSlot, cv->wrap_cache_slots_count);
}
//////////////////////////////
//- rjf: determine visible line range / count
//
@@ -1015,7 +1030,7 @@ rd_watch_row_info_from_row(Arena *arena, EV_Row *row)
info.callstack_thread = entity;
U64 frame_num = ev_block_num_from_id(block, key.child_id);
B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0);
if(1 <= frame_num && frame_num <= call_stack.frames_count)
{
CTRL_CallStackFrame *f = &call_stack.frames[frame_num-1];
@@ -2924,7 +2939,7 @@ RD_VIEW_UI_FUNCTION_DEF(memory)
CTRL_Scope *ctrl_scope = ctrl_scope_open();
CTRL_Entity *selected_thread = ctrl_entity_from_handle(&d_state->ctrl_entity_store->ctx, rd_regs()->thread);
CTRL_Entity *selected_process = ctrl_entity_ancestor_from_kind(selected_thread, CTRL_EntityKind_Process);
CTRL_CallStack selected_call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, selected_thread, 1, 0);
CTRL_CallStack selected_call_stack = ctrl_call_stack_from_thread(ctrl_scope, selected_thread->handle, 1, 0);
CTRL_Entity *eval_process = &ctrl_entity_nil;
if(eval.space.kind == RD_EvalSpaceKind_CtrlEntity)
{
+30
View File
@@ -14,6 +14,30 @@ enum
RD_CodeViewBuildFlag_All = 0xffffffff,
};
typedef struct RD_CodeViewTLineSplitNode RD_CodeViewTLineSplitNode;
struct RD_CodeViewTLineSplitNode
{
RD_CodeViewTLineSplitNode *next;
U64 off;
};
typedef struct RD_CodeViewTLineWrapCacheNode RD_CodeViewTLineWrapCacheNode;
struct RD_CodeViewTLineWrapCacheNode
{
RD_CodeViewTLineWrapCacheNode *hash_next;
RD_CodeViewTLineWrapCacheNode *hash_prev;
S64 line_num;
RD_CodeViewTLineSplitNode *first_split;
RD_CodeViewTLineSplitNode *last_split;
};
typedef struct RD_CodeViewTLineWrapCacheSlot RD_CodeViewTLineWrapCacheSlot;
struct RD_CodeViewTLineWrapCacheSlot
{
RD_CodeViewTLineWrapCacheNode *first;
RD_CodeViewTLineWrapCacheNode *last;
};
typedef struct RD_CodeViewState RD_CodeViewState;
struct RD_CodeViewState
{
@@ -32,6 +56,12 @@ struct RD_CodeViewState
Arena *find_text_arena;
String8 find_text_fwd;
String8 find_text_bwd;
// rjf: line wrapping cache & info
Arena *wrap_arena;
RD_CodeViewTLineWrapCacheSlot *wrap_cache_slots;
U64 wrap_cache_slots_count;
U64 wrap_total_vline_count;
};
typedef struct RD_CodeViewBuildResult RD_CodeViewBuildResult;
+1 -1
View File
@@ -552,7 +552,7 @@ rd_title_fstrs_from_ctrl_entity(Arena *arena, CTRL_Entity *entity, B32 include_e
CTRL_Entity *process = ctrl_entity_ancestor_from_kind(entity, CTRL_EntityKind_Process);
Arch arch = entity->arch;
B32 call_stack_high_priority = ctrl_handle_match(entity->handle, rd_base_regs()->thread);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, &d_state->ctrl_entity_store->ctx, entity, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0);
CTRL_CallStack call_stack = ctrl_call_stack_from_thread(ctrl_scope, entity->handle, call_stack_high_priority, call_stack_high_priority ? rd_state->frame_eval_memread_endt_us : 0);
B32 did_first_known = 0;
for(U64 idx = 0, limit = 10;
idx < call_stack.frames_count && idx < limit;