@@ -1838,7 +1838,7 @@ DW_MEM_READ(ctrl_unwind_mem_read_dwarf_x64)
18381838 B32 is_read = ctrl_process_memory_read (ctx -> process_handle , r1u64 (addr , addr + size ), & is_stale , buffer , ctx -> endt_us );
18391839
18401840 DW_UnwindStatus status = DW_UnwindStatus_Fail ;
1841- if (is_stale && is_read )
1841+ if (is_stale )
18421842 {
18431843 status = DW_UnwindStatus_Maybe ;
18441844 }
@@ -1903,12 +1903,35 @@ DW_REG_WRITE(ctrl_unwind_reg_write_dwarf_x64)
19031903}
19041904
19051905internal CTRL_UnwindStepResult
1906- ctrl_unwind_step__dwarf ( CTRL_Handle process_handle , CTRL_Handle module_handle , Arch arch , void * regs , U64 endt_us )
1906+ ctrl_unwind_step_result_from_dwarf_status ( DW_UnwindStatus s )
19071907{
1908- Temp scratch = scratch_begin (0 , 0 );
1909-
1908+ CTRL_UnwindStepResult result = {0 };
1909+ switch (s )
1910+ {
1911+ case DW_UnwindStatus_Ok :
1912+ {
1913+ result .flags &= ~(CTRL_UnwindFlag_Error |CTRL_UnwindFlag_Stale );
1914+ }break ;
1915+ case DW_UnwindStatus_Fail :
1916+ {
1917+ result .flags |= CTRL_UnwindFlag_Error ;
1918+ }break ;
1919+ case DW_UnwindStatus_Maybe :
1920+ {
1921+ result .flags &= ~CTRL_UnwindFlag_Error ;
1922+ result .flags |= CTRL_UnwindFlag_Stale ;
1923+ }break ;
1924+ default : { InvalidPath ; } break ;
1925+ }
1926+ return result ;
1927+ }
1928+
1929+ internal CTRL_UnwindStepResult
1930+ ctrl_establish_frame_unwind_context__dwarf (Arena * arena , CTRL_Handle process_handle , CTRL_Handle module_handle , Arch arch , void * regs , U64 endt_us , CTRL_FrameUnwindContext * ctx_out )
1931+ {
1932+ Temp scratch = scratch_begin (& arena , 1 );
19101933 CTRL_UnwindStepResult result = { .flags = CTRL_UnwindFlag_Error };
1911-
1934+
19121935 // gather context for virtual stack unwinder
19131936 U64 cfi_rebase = 0 ;
19141937 B32 is_unwind_eh = 0 ;
@@ -1944,9 +1967,9 @@ ctrl_unwind_step__dwarf(CTRL_Handle process_handle, CTRL_Handle module_handle, A
19441967 if (fde_addr != max_U64 )
19451968 {
19461969 // parse call frame info
1947- DW_CIE cie = {0 };
1948- DW_FDE fde = {0 };
1949- B32 is_cfi_parsed = 0 ;
1970+ DW_CIE cie = {0 };
1971+ DW_FDE fde = {0 };
1972+ B32 is_cfi_parsed = 0 ;
19501973 if (is_unwind_eh )
19511974 {
19521975 B32 is_stale = 0 ;
@@ -2051,16 +2074,14 @@ ctrl_unwind_step__dwarf(CTRL_Handle process_handle, CTRL_Handle module_handle, A
20512074 }
20522075
20532076 // find register rules for IP
2054- DW_CFI_Row * cfi_row = dw_cfi_row_from_pc (scratch . arena , arch , & cie , & fde , decode_ptr_func , decode_ptr_ctx , ip );
2077+ DW_CFI_Row * cfi_row = dw_cfi_row_from_pc (arena , arch , & cie , & fde , decode_ptr_func , decode_ptr_ctx , ip );
20552078 if (cfi_row )
20562079 {
20572080 // setup machine ops
20582081 void * mem_read_ctx = 0 ;
20592082 void * reg_read_ctx = 0 ;
2060- void * reg_write_ctx = 0 ;
20612083 DW_MemRead * mem_read_func = 0 ;
20622084 DW_RegRead * reg_read_func = 0 ;
2063- DW_RegWrite * reg_write_func = 0 ;
20642085 switch (arch )
20652086 {
20662087 case Arch_Null : break ;
@@ -2072,64 +2093,101 @@ ctrl_unwind_step__dwarf(CTRL_Handle process_handle, CTRL_Handle module_handle, A
20722093
20732094 mem_read_ctx = mem_read_ctx_x64 ;
20742095 reg_read_ctx = regs ;
2075- reg_write_ctx = regs ;
20762096
20772097 mem_read_func = ctrl_unwind_mem_read_dwarf_x64 ;
20782098 reg_read_func = ctrl_unwind_reg_read_dwarf_x64 ;
2079- reg_write_func = ctrl_unwind_reg_write_dwarf_x64 ;
20802099 }break ;
20812100 case Arch_x86 :
20822101 case Arch_arm64 :
20832102 case Arch_arm32 :
20842103 {
20852104 NotImplemented ;
20862105 }break ;
2087- default : { InvalidPath ; } break ;
2106+ default : { InvalidPath ; }break ;
20882107 }
2089-
2090- // apply register rules to the context
2091- DW_UnwindStatus cfi_uw_status = dw_cfi_apply_register_rules (arch ,
2092- & cie ,
2093- cfi_row ,
2094- mem_read_func ,
2095- mem_read_ctx ,
2096- reg_read_func ,
2097- reg_read_ctx ,
2098- reg_write_func ,
2099- reg_write_ctx );
2100-
2101- // last frame typically has undefined rule for IP
2102- if (cfi_row -> regs [cie .ret_addr_reg ].rule == DW_CFI_RegisterRule_Undefined )
2103- {
2104- regs_arch_block_write_rip (arch , regs , 0 );
2105- }
2106-
2107- // translate unwind status code to control layer's result flags
2108- switch (cfi_uw_status )
2108+
2109+ // compute CFA for the row
2110+ U64 cfa = 0 ;
2111+ DW_UnwindStatus unwind_status = dw_compute_cfa (arch , cfi_row , mem_read_func , mem_read_ctx , reg_read_func , reg_read_ctx , & cfa );
2112+
2113+ // on success fill out output
2114+ if (unwind_status == DW_UnwindStatus_Ok )
21092115 {
2110- case DW_UnwindStatus_Ok :
2111- {
2112- result .flags &= ~(CTRL_UnwindFlag_Error |CTRL_UnwindFlag_Stale );
2113- }break ;
2114- case DW_UnwindStatus_Fail :
2115- {
2116- result .flags |= CTRL_UnwindFlag_Error ;
2117- }break ;
2118- case DW_UnwindStatus_Maybe :
2119- {
2120- result .flags &= ~CTRL_UnwindFlag_Error ;
2121- result .flags |= CTRL_UnwindFlag_Stale ;
2122- }break ;
2123- default : { InvalidPath ; } break ;
2116+ ctx_out -> cfa = cfa ;
2117+ ctx_out -> cfi_row = cfi_row ;
2118+ ctx_out -> ret_addr_reg = cie .ret_addr_reg ;
21242119 }
2120+
2121+ // translate unwind status code
2122+ result = ctrl_unwind_step_result_from_dwarf_status (unwind_status );
21252123 }
21262124 }
21272125 }
2128- else
2126+
2127+ scratch_end (scratch );
2128+ return result ;
2129+ }
2130+
2131+ internal CTRL_UnwindStepResult
2132+ ctrl_unwind_step__dwarf (CTRL_Handle process_handle , CTRL_Handle module_handle , Arch arch , void * regs , CTRL_FrameUnwindContext * frame_ctx , U64 endt_us )
2133+ {
2134+ Temp scratch = scratch_begin (0 , 0 );
2135+
2136+ CTRL_UnwindStepResult result = { .flags = CTRL_UnwindFlag_Error };
2137+
2138+ // setup machine ops
2139+ void * mem_read_ctx = 0 ;
2140+ void * reg_read_ctx = 0 ;
2141+ void * reg_write_ctx = 0 ;
2142+ DW_MemRead * mem_read_func = 0 ;
2143+ DW_RegRead * reg_read_func = 0 ;
2144+ DW_RegWrite * reg_write_func = 0 ;
2145+ switch (arch )
2146+ {
2147+ case Arch_Null : break ;
2148+ case Arch_x64 :
2149+ {
2150+ CTRL_MemoryReadContextDwarfX64 * mem_read_ctx_x64 = push_array (scratch .arena , CTRL_MemoryReadContextDwarfX64 , 1 );
2151+ mem_read_ctx_x64 -> process_handle = process_handle ;
2152+ mem_read_ctx_x64 -> endt_us = endt_us ;
2153+
2154+ mem_read_ctx = mem_read_ctx_x64 ;
2155+ reg_read_ctx = regs ;
2156+ reg_write_ctx = regs ;
2157+
2158+ mem_read_func = ctrl_unwind_mem_read_dwarf_x64 ;
2159+ reg_read_func = ctrl_unwind_reg_read_dwarf_x64 ;
2160+ reg_write_func = ctrl_unwind_reg_write_dwarf_x64 ;
2161+ }break ;
2162+ case Arch_x86 :
2163+ case Arch_arm64 :
2164+ case Arch_arm32 :
2165+ {
2166+ NotImplemented ;
2167+ }break ;
2168+ default : { InvalidPath ; }break ;
2169+ }
2170+
2171+ // apply register rules to the context
2172+ DW_UnwindStatus unwind_status = dw_cfi_apply_register_rules (arch ,
2173+ frame_ctx -> cfa ,
2174+ frame_ctx -> cfi_row ,
2175+ mem_read_func ,
2176+ mem_read_ctx ,
2177+ reg_read_func ,
2178+ reg_read_ctx ,
2179+ reg_write_func ,
2180+ reg_write_ctx );
2181+
2182+ // last frame typically has undefined rule for IP
2183+ if (frame_ctx -> cfi_row -> regs [frame_ctx -> ret_addr_reg ].rule == DW_CFI_RegisterRule_Undefined )
21292184 {
2130- // TODO: if IP does not have FDE, does this mean function is a leaf?
2185+ regs_arch_block_write_rip ( arch , regs , 0 );
21312186 }
21322187
2188+ // translate unwind status code
2189+ result = ctrl_unwind_step_result_from_dwarf_status (unwind_status );
2190+
21332191 scratch_end (scratch );
21342192 return result ;
21352193}
@@ -2977,23 +3035,41 @@ ctrl_unwind_step__pe_x64(CTRL_Handle process_handle, CTRL_Handle module_handle,
29773035//- rjf: abstracted unwind step
29783036
29793037internal CTRL_UnwindStepResult
2980- ctrl_unwind_step ( CTRL_Handle process , CTRL_Handle module , U64 module_base_vaddr , Arch arch , void * reg_block , U64 endt_us )
3038+ ctrl_establish_frame_unwind_context ( Arena * arena , CTRL_Handle process_handle , CTRL_Handle module_handle , Arch arch , void * regs , U64 endt_us , CTRL_FrameUnwindContext * ctx_out )
29813039{
29823040 CTRL_UnwindStepResult result = {0 };
2983-
2984- result = ctrl_unwind_step__dwarf (process , module , arch , reg_block , endt_us );
2985-
2986- if (result .flags == CTRL_UnwindFlag_Error && ~result .flags & CTRL_UnwindFlag_Stale )
2987- {
2988- switch (arch )
2989- {
2990- default :{}break ;
2991- case Arch_x64 :
2992- {
2993- result = ctrl_unwind_step__pe_x64 (process , module , module_base_vaddr , (REGS_RegBlockX64 * )reg_block , endt_us );
2994- }break ;
2995- }
3041+ #if OS_LINUX
3042+ result = ctrl_establish_frame_unwind_context__dwarf (arena , process_handle , module_handle , arch , regs , endt_us , ctx_out );
3043+ #elif OS_WINDOWS
3044+ // windows does not have a concept of frame context
3045+ #else
3046+ # error "unwinder is not defined for current OS"
3047+ #endif
3048+ return result ;
3049+ }
3050+
3051+ internal CTRL_UnwindStepResult
3052+ ctrl_unwind_step (CTRL_Handle process , CTRL_Handle module , U64 module_base_vaddr , Arch arch , void * reg_block , CTRL_FrameUnwindContext * frame_ctx , U64 endt_us )
3053+ {
3054+ CTRL_UnwindStepResult result = {0 };
3055+ #if OS_LINUX
3056+ result = ctrl_unwind_step__dwarf (process , module , arch , reg_block , frame_ctx , endt_us );
3057+ #elif OS_WINDOWS
3058+ switch (arch )
3059+ {
3060+ case Arch_Null :{}break ;
3061+ case Arch_x64 :{result = ctrl_unwind_step__pe_x64 (process , module , module_base_vaddr , reg_block , endt_us );}break ;
3062+ case Arch_x86 :
3063+ case Arch_arm32 :
3064+ case Arch_arm64 :
3065+ {
3066+ NotImplemented ;
3067+ }break ;
3068+ default :{InvalidPath ;}break ;
29963069 }
3070+ #else
3071+ # error "unwinder is not defined for current OS"
3072+ #endif
29973073 return result ;
29983074}
29993075
@@ -3004,8 +3080,7 @@ ctrl_unwind_from_thread(Arena *arena, CTRL_EntityCtx *ctx, CTRL_Handle thread, U
30043080{
30053081 ProfBeginFunction ();
30063082 Temp scratch = scratch_begin (& arena , 1 );
3007- CTRL_Unwind unwind = {0 };
3008- unwind .flags |= CTRL_UnwindFlag_Error ;
3083+ CTRL_Unwind unwind = { .flags = CTRL_UnwindFlag_Error };
30093084
30103085 //- rjf: unpack args
30113086 CTRL_Entity * thread_entity = ctrl_entity_from_handle (ctx , thread );
@@ -3023,44 +3098,45 @@ ctrl_unwind_from_thread(Arena *arena, CTRL_EntityCtx *ctx, CTRL_Handle thread, U
30233098 U64 frame_node_count = 0 ;
30243099 if (regs_block_good )
30253100 {
3026- unwind .flags = 0 ;
3027- for (;;)
3101+ for (unwind .flags = 0 ;;)
30283102 {
3029- // rjf: regs -> rip*module
30303103 U64 rip = regs_rip_from_arch_block (arch , regs_block );
30313104 U64 rsp = regs_rsp_from_arch_block (arch , regs_block );
3032- CTRL_Entity * module = & ctrl_entity_nil ;
3033- for (CTRL_Entity * m = process_entity -> first ; m != & ctrl_entity_nil ; m = m -> next )
3105+
3106+ // rjf: cancel on 0 rip
3107+ if (rip == 0 )
30343108 {
3035- if (m -> kind == CTRL_EntityKind_Module && contains_1u64 (m -> vaddr_range , rip ))
3036- {
3037- module = m ;
3038- break ;
3039- }
3109+ break ;
30403110 }
3041-
3042- // rjf: cancel on 0 rip/rsp
3043- if (rsp == 0 && rip == 0 )
3111+
3112+ // rip -> module
3113+ CTRL_Entity * module = ctrl_module_from_process_vaddr (process_entity , rip );
3114+
3115+ // establish frame context
3116+ CTRL_FrameUnwindContext frame_ctx = {0 };
3117+ CTRL_UnwindStepResult frame_ctx_result = ctrl_establish_frame_unwind_context (scratch .arena , process_entity -> handle , module -> handle , arch , regs_block , endt_us , & frame_ctx );
3118+ unwind .flags |= frame_ctx_result .flags ;
3119+ if (unwind .flags & (CTRL_UnwindFlag_Stale |CTRL_UnwindFlag_Error ))
30443120 {
30453121 break ;
30463122 }
30473123
30483124 // rjf: valid step -> push frame
30493125 CTRL_UnwindFrameNode * frame_node = push_array (scratch .arena , CTRL_UnwindFrameNode , 1 );
30503126 CTRL_UnwindFrame * frame = & frame_node -> v ;
3127+ frame -> cfa = frame_ctx .cfa ;
30513128 frame -> regs = push_array_no_zero (arena , U8 , arch_reg_block_size );
30523129 MemoryCopy (frame -> regs , regs_block , arch_reg_block_size );
30533130 DLLPushBack (first_frame_node , last_frame_node , frame_node );
30543131 frame_node_count += 1 ;
30553132
30563133 // rjf: unwind one step
3057- CTRL_UnwindStepResult step = ctrl_unwind_step (process_entity -> handle , module -> handle , module -> vaddr_range .min , arch , regs_block , endt_us );
3134+ CTRL_UnwindStepResult step = ctrl_unwind_step (process_entity -> handle , module -> handle , module -> vaddr_range .min , arch , regs_block , & frame_ctx , endt_us );
30583135 unwind .flags |= step .flags ;
3059- if (step .flags & CTRL_UnwindFlag_Error ||
3060- regs_rsp_from_arch_block (arch , regs_block ) == 0 ||
3061- regs_rip_from_arch_block (arch , regs_block ) == 0 ||
3062- (regs_rsp_from_arch_block (arch , regs_block ) == rsp &&
3063- regs_rip_from_arch_block (arch , regs_block ) == rip ))
3136+
3137+ // stop unwinding on errors or stale data
3138+ if (unwind .flags & (CTRL_UnwindFlag_Stale |CTRL_UnwindFlag_Error ) ||
3139+ (regs_rsp_from_arch_block (arch , regs_block ) == rsp && regs_rip_from_arch_block (arch , regs_block ) == rip ))
30643140 {
30653141 break ;
30663142 }
@@ -3133,6 +3209,7 @@ ctrl_call_stack_from_unwind(Arena *arena, CTRL_Entity *process, CTRL_Unwind *bas
31333209 SLLQueuePush (first_frame , last_frame , dst_inline );
31343210 dst_inline -> v .unwind_count = base_frame_idx ;
31353211 dst_inline -> v .regs = src -> regs ;
3212+ dst_inline -> v .cfa = src -> cfa ;
31363213 frame_count += 1 ;
31373214 inline_frame_count += 1 ;
31383215 }
@@ -3142,6 +3219,7 @@ ctrl_call_stack_from_unwind(Arena *arena, CTRL_Entity *process, CTRL_Unwind *bas
31423219 SLLQueuePush (first_frame , last_frame , dst_base );
31433220 dst_base -> v .unwind_count = base_frame_idx ;
31443221 dst_base -> v .regs = src -> regs ;
3222+ dst_base -> v .cfa = src -> cfa ;
31453223 frame_count += 1 ;
31463224
31473225 // rjf: hook up inline frames to point to concrete frame, and to account for inline depth
@@ -4898,6 +4976,8 @@ ctrl_thread__eval_scope_begin(Arena *arena, CTRL_UserBreakpointList *user_bps, C
48984976 ctx -> frame_base = push_array (arena , U64 , 1 );
48994977 // TODO(rjf): need to compute this out here somehow... ctx->frame_base[0] = ;
49004978 ctx -> tls_base = push_array (arena , U64 , 1 );
4979+ // TODO: compute CFA
4980+ ctx -> cfa = 0 ;
49014981 }
49024982 e_select_interpret_ctx (& scope -> interpret_ctx , eval_dbg_infos_primary -> rdi , thread_rip_voff );
49034983
0 commit comments