diff --git a/src/ctrl/ctrl_core.c b/src/ctrl/ctrl_core.c index 027f6429..a2a231bf 100644 --- a/src/ctrl/ctrl_core.c +++ b/src/ctrl/ctrl_core.c @@ -396,6 +396,7 @@ ctrl_serialized_string_from_msg_list(Arena *arena, CTRL_MsgList *msgs) CTRL_UserBreakpoint *bp = &n->v; str8_serial_push_struct(scratch.arena, &msgs_srlzed, &bp->kind); str8_serial_push_struct(scratch.arena, &msgs_srlzed, &bp->flags); + str8_serial_push_struct(scratch.arena, &msgs_srlzed, &bp->id); str8_serial_push_struct(scratch.arena, &msgs_srlzed, &bp->string.size); str8_serial_push_data(scratch.arena, &msgs_srlzed, bp->string.str, bp->string.size); str8_serial_push_struct(scratch.arena, &msgs_srlzed, &bp->pt); @@ -518,6 +519,7 @@ ctrl_msg_list_from_serialized_string(Arena *arena, String8 string) CTRL_UserBreakpoint *bp = &n->v; read_off += str8_deserial_read_struct(string, read_off, &bp->kind); read_off += str8_deserial_read_struct(string, read_off, &bp->flags); + read_off += str8_deserial_read_struct(string, read_off, &bp->id); read_off += str8_deserial_read_struct(string, read_off, &bp->string.size); bp->string.str = push_array_no_zero(arena, U8, bp->string.size); read_off += str8_deserial_read(string, read_off, bp->string.str, bp->string.size, 1); @@ -5731,21 +5733,28 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) } // rjf: user breakpoints - for(DMN_TrapChunkNode *n = user_traps.first; n != 0; n = n->next) { - DMN_Trap *trap = n->v; - DMN_Trap *opl = n->v + n->count; - for(;trap < opl; trap += 1) + if(event->user_data != 0) { - if(dmn_handle_match(trap->process, event->process) && - trap->vaddr == event->instruction_pointer && - (!dmn_handle_match(event->thread, target_thread.dmn_handle) || !target_thread_is_on_user_bp_and_trap_net_trap)) + CTRL_UserBreakpoint *user_bp = (CTRL_UserBreakpoint *)event->user_data; + hit_user_bp = 1; + } + for(DMN_TrapChunkNode *n = user_traps.first; n != 0; n = n->next) + { + DMN_Trap *trap = n->v; + DMN_Trap *opl = n->v + n->count; + for(;trap < opl; trap += 1) { - CTRL_UserBreakpoint *user_bp = (CTRL_UserBreakpoint *)trap->id; - hit_user_bp = 1; - if(user_bp != 0 && user_bp->condition.size != 0) + if(dmn_handle_match(trap->process, event->process) && + trap->vaddr == event->instruction_pointer && + (!dmn_handle_match(event->thread, target_thread.dmn_handle) || !target_thread_is_on_user_bp_and_trap_net_trap)) { - str8_list_push(temp.arena, &conditions, user_bp->condition); + CTRL_UserBreakpoint *user_bp = (CTRL_UserBreakpoint *)trap->id; + hit_user_bp = 1; + if(user_bp != 0 && user_bp->condition.size != 0) + { + str8_list_push(temp.arena, &conditions, user_bp->condition); + } } } } @@ -6090,6 +6099,11 @@ ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg) event->exception_kind = ctrl_exception_kind_from_dmn(stop_event->exception_kind); event->vaddr_rng = r1u64(stop_event->address, stop_event->address); event->rip_vaddr = stop_event->instruction_pointer; + if(stop_cause == CTRL_EventCause_UserBreakpoint && stop_event->user_data != 0) + { + CTRL_UserBreakpoint *user_bp = (CTRL_UserBreakpoint *)stop_event->user_data; + event->u64_code = user_bp->id; + } ctrl_c2u_push_events(&evts); } diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index aae980a7..e8481f95 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -281,6 +281,7 @@ struct CTRL_UserBreakpoint { CTRL_UserBreakpointKind kind; CTRL_UserBreakpointFlags flags; + U64 id; String8 string; TxtPt pt; U64 size; diff --git a/src/dbg_engine/dbg_engine_core.c b/src/dbg_engine/dbg_engine_core.c index 058221da..dea8294e 100644 --- a/src/dbg_engine/dbg_engine_core.c +++ b/src/dbg_engine/dbg_engine_core.c @@ -1795,6 +1795,7 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P evt->cause = cause; evt->thread = thread->kind == CTRL_EntityKind_Thread ? thread->handle : ctrl_handle_zero(); evt->vaddr = event->rip_vaddr; + evt->id = event->u64_code; } }break; @@ -2417,6 +2418,7 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P { CTRL_UserBreakpoint ctrl_user_bp = {CTRL_UserBreakpointKind_FileNameAndLineColNumber}; ctrl_user_bp.flags = ctrl_bp_flags; + ctrl_user_bp.id = bp->id; ctrl_user_bp.string = n->string; ctrl_user_bp.pt = bp->pt; ctrl_user_bp.condition = bp->condition; @@ -2430,6 +2432,7 @@ d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_P { CTRL_UserBreakpoint ctrl_user_bp = {CTRL_UserBreakpointKind_Expression}; ctrl_user_bp.flags = ctrl_bp_flags; + ctrl_user_bp.id = bp->id; ctrl_user_bp.string = bp->vaddr_expr; ctrl_user_bp.condition = bp->condition; ctrl_user_bp.size = bp->size; diff --git a/src/dbg_engine/dbg_engine_core.h b/src/dbg_engine/dbg_engine_core.h index 10d237cd..4efc7234 100644 --- a/src/dbg_engine/dbg_engine_core.h +++ b/src/dbg_engine/dbg_engine_core.h @@ -40,6 +40,7 @@ typedef struct D_Breakpoint D_Breakpoint; struct D_Breakpoint { D_BreakpointFlags flags; + U64 id; String8 file_path; TxtPt pt; String8 vaddr_expr; @@ -98,6 +99,7 @@ struct D_Event CTRL_Handle thread; U64 vaddr; U64 code; + U64 id; }; typedef struct D_EventNode D_EventNode; diff --git a/src/demon/win32/demon_core_win32.c b/src/demon/win32/demon_core_win32.c index d9a7618b..5ec06fe0 100644 --- a/src/demon/win32/demon_core_win32.c +++ b/src/demon/win32/demon_core_win32.c @@ -1568,51 +1568,53 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) } } + ////////////////////////// + //- rjf: gather all flagged traps, bucketed by process + // + typedef struct DMN_FlaggedTrapTask DMN_FlaggedTrapTask; + struct DMN_FlaggedTrapTask + { + DMN_FlaggedTrapTask *next; + DMN_Handle process; + DMN_TrapChunkList traps; + }; + DMN_FlaggedTrapTask *first_flagged_trap_task = 0; + DMN_FlaggedTrapTask *last_flagged_trap_task= 0; + for(DMN_TrapChunkNode *n = ctrls->traps.first; n != 0; n = n->next) + { + for(U64 n_idx = 0; n_idx < n->count; n_idx += 1) + { + DMN_Trap *trap = n->v+n_idx; + if(trap->flags != 0) + { + DMN_FlaggedTrapTask *task = 0; + for(DMN_FlaggedTrapTask *t = first_flagged_trap_task; t != 0; t = t->next) + { + if(dmn_handle_match(t->process, trap->process)) + { + task = t; + break; + } + } + if(task == 0) + { + task = push_array(scratch.arena, DMN_FlaggedTrapTask, 1); + SLLQueuePush(first_flagged_trap_task, last_flagged_trap_task, task); + task->process = trap->process; + } + dmn_trap_chunk_list_push(scratch.arena, &task->traps, 8, trap); + } + } + } + ////////////////////////// //- rjf: write all debug register states, for flagged-traps // ProfScope("write all debug register states, for flagged-traps") { - //- rjf: gather all flagged traps, bucketed by process - typedef struct DMN_FlaggedTrapTask DMN_FlaggedTrapTask; - struct DMN_FlaggedTrapTask - { - DMN_FlaggedTrapTask *next; - DMN_Handle process; - DMN_TrapChunkList traps; - }; - DMN_FlaggedTrapTask *first_task = 0; - DMN_FlaggedTrapTask *last_task = 0; - for(DMN_TrapChunkNode *n = ctrls->traps.first; n != 0; n = n->next) - { - for(U64 n_idx = 0; n_idx < n->count; n_idx += 1) - { - DMN_Trap *trap = n->v+n_idx; - if(trap->flags != 0) - { - DMN_FlaggedTrapTask *task = 0; - for(DMN_FlaggedTrapTask *t = first_task; t != 0; t = t->next) - { - if(dmn_handle_match(t->process, trap->process)) - { - task = t; - break; - } - } - if(task == 0) - { - task = push_array(scratch.arena, DMN_FlaggedTrapTask, 1); - SLLQueuePush(first_task, last_task, task); - task->process = trap->process; - } - dmn_trap_chunk_list_push(scratch.arena, &task->traps, 8, trap); - } - } - } - //- rjf: for each flagged trap task, iterate all threads in the // associated process, and prepare debug registers accordingly - for(DMN_FlaggedTrapTask *t = first_task; t != 0; t = t->next) + for(DMN_FlaggedTrapTask *t = first_flagged_trap_task; t != 0; t = t->next) { DMN_Handle process = t->process; DMN_W32_Entity *process_entity = dmn_w32_entity_from_handle(process); @@ -1648,9 +1650,11 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) case 3:{addr_reg = ®s.dr3;}break; } addr_reg->u64 = trap->vaddr; + regs.dr7.u64 |= bit9|bit10|bit11; regs.dr7.u64 |= (1ull << (trap_idx*2)); - regs.dr7.u64 |= (1ull << (trap_idx*2+1)); + // NOTE(rjf): global-enable regs.dr7.u64 |= (1ull << (trap_idx*2+1)); regs.dr7.u64 &= ~((U64)(bit17|bit18|bit19|bit20) << (trap_idx*4)); + regs.dr7.u64 &= ~((U64)(bit15|bit16)); switch(trap->flags) { case DMN_TrapFlag_BreakOnExecute: @@ -2217,6 +2221,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) //- rjf: check if this trap is a usage-code-specified trap or something else B32 hit_user_trap = 0; + U64 user_trap_id = 0; if(is_trap) { for(DMN_TrapChunkNode *n = ctrls->traps.first; n != 0; n = n->next) @@ -2226,6 +2231,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) if(dmn_handle_match(n->v[idx].process, dmn_w32_handle_from_entity(process)) && n->v[idx].vaddr == instruction_pointer) { hit_user_trap = 1; + user_trap_id = n->v[idx].id; break; } } @@ -2314,6 +2320,7 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) e->code = exception->ExceptionCode; e->flags = exception->ExceptionFlags; e->instruction_pointer = (U64)exception->ExceptionAddress; + e->user_data = user_trap_id; //- rjf: fill according to exception code switch(exception->ExceptionCode) @@ -2343,6 +2350,71 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) case DMN_W32_EXCEPTION_SINGLE_STEP: { e->kind = DMN_EventKind_SingleStep; + + // NOTE(rjf): data breakpoints are reported via single-steps + // over the instructions which caused the breakpoint to be + // hit - so if we have data breakpoints set, we need to + // check this thread's debug registers, to determine if this + // is a regular single-step or a data breakpoint hit. + if(first_flagged_trap_task != 0) + { + // rjf: first determine the flagged trap index + U64 flagged_trap_idx = 0; + switch(thread->arch) + { + default:{NotImplemented;}break; + case Arch_x64: + { + REGS_RegBlockX64 regs = {0}; + dmn_w32_thread_read_reg_block(thread->arch, thread->handle, ®s); + if(regs.dr6.u64 & 0xF) + { + e->kind = DMN_EventKind_Breakpoint; + if(0){} + else if(regs.dr7.u64 & (1ull<<0) && regs.dr6.u64 & (1ull<<0)) { flagged_trap_idx = 1; } + else if(regs.dr7.u64 & (1ull<<1) && regs.dr6.u64 & (1ull<<1)) { flagged_trap_idx = 2; } + else if(regs.dr7.u64 & (1ull<<2) && regs.dr6.u64 & (1ull<<2)) { flagged_trap_idx = 3; } + else if(regs.dr7.u64 & (1ull<<3) && regs.dr6.u64 & (1ull<<3)) { flagged_trap_idx = 3; } + } + }break; + } + + // rjf: find the flagged trap task for this thread's process + DMN_W32_Entity *process = thread->parent; + DMN_FlaggedTrapTask *task = 0; + for(DMN_FlaggedTrapTask *t = first_flagged_trap_task; t != 0; t = t->next) + { + if(dmn_handle_match(t->process, dmn_w32_handle_from_entity(process))) + { + task = t; + break; + } + } + + // rjf: find the trap + DMN_Trap *trap = 0; + if(task != 0) + { + U64 trap_idx = 0; + for(DMN_TrapChunkNode *n = task->traps.first; n != 0; n = n->next) + { + for(U64 n_idx = 0; n_idx < n->count; n_idx += 1, trap_idx += 1) + { + if(trap_idx == flagged_trap_idx) + { + trap = &n->v[n_idx]; + break; + } + } + } + } + + // rjf: fill event based on trap + if(trap != 0) + { + e->user_data = trap->id; + } + } }break; //- rjf: fill throw info @@ -2563,7 +2635,9 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) } } + //////////////////////// //- rjf: gather new thread-names + // ProfScope("gather new thread names") if(dmn_w32_GetThreadDescription != 0) { for(DMN_W32_Entity *process = dmn_w32_shared->entities_base->first; @@ -2628,6 +2702,39 @@ dmn_ctrl_run(Arena *arena, DMN_CtrlCtx *ctx, DMN_RunCtrls *ctrls) } } + ////////////////////////// + //- rjf: clear all debug register states, for flagged-traps + // + ProfScope("clear all debug register states, for flagged-traps") + { + for(DMN_FlaggedTrapTask *t = first_flagged_trap_task; t != 0; t = t->next) + { + DMN_Handle process = t->process; + DMN_W32_Entity *process_entity = dmn_w32_entity_from_handle(process); + for(DMN_W32_Entity *child = process_entity->first; + child != &dmn_w32_entity_nil; + child = child->next) + { + if(child->kind == DMN_W32_EntityKind_Thread) + { + switch(child->arch) + { + default:{}break; + + //- rjf: x64 + case Arch_x64: + { + REGS_RegBlockX64 regs = {0}; + dmn_w32_thread_read_reg_block(child->arch, child->handle, ®s); + regs.dr7.u64 = 0; + dmn_w32_thread_write_reg_block(child->arch, child->handle, ®s); + }break; + } + } + } + } + } + ////////////////////////// //- rjf: unset single step bit // diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 08616cbb..08b445c4 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -15804,6 +15804,7 @@ Z(getting_started) //- rjf: fill breakpoint D_Breakpoint *dst_bp = &breakpoints.v[idx]; dst_bp->flags = flags; + dst_bp->id = src_bp->id; dst_bp->file_path = src_bp_loc.file_path; dst_bp->pt = src_bp_loc.pt; dst_bp->vaddr_expr = src_bp_loc.expr; @@ -15944,31 +15945,13 @@ Z(getting_started) // rjf: increment breakpoint hit counts if(evt->cause == D_EventCause_UserBreakpoint) { - RD_CfgList bps = rd_cfg_top_level_list_from_string(scratch.arena, str8_lit("breakpoint")); - for(RD_CfgNode *n = bps.first; n != 0; n = n->next) + RD_Cfg *bp = rd_cfg_from_id(evt->id); + if(bp != &rd_nil_cfg) { - RD_Cfg *bp = n->v; RD_Cfg *hit_count_root = rd_cfg_child_from_string_or_alloc(bp, str8_lit("hit_count")); U64 hit_count = 0; try_u64_from_str8_c_rules(hit_count_root->first->string, &hit_count); - RD_Location loc = rd_location_from_cfg(bp); - D_LineList loc_lines = d_lines_from_file_path_line_num(scratch.arena, loc.file_path, loc.pt.line); - E_Value loc_value = e_value_from_string(loc.expr); - if(loc_lines.first != 0) - { - for(D_LineNode *n = loc_lines.first; n != 0; n = n->next) - { - if(contains_1u64(n->v.voff_range, voff)) - { - hit_count += 1; - break; - } - } - } - else if(loc_value.u64 != 0 && vaddr == loc_value.u64) - { - hit_count += 1; - } + hit_count += 1; rd_cfg_new_replacef(hit_count_root, "%I64u", hit_count); } }