mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-07-23 07:57:50 +00:00
transmit user breakpoints hit from demon -> ctrl using ctrl-defined IDs, correllate those IDs to rd-defined IDs, use rd-defined IDs in stop events to count bp hits, rather than re-resolving breakpoints. further fixes & work on first pass of data breakpoints
This commit is contained in:
+25
-11
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -281,6 +281,7 @@ struct CTRL_UserBreakpoint
|
||||
{
|
||||
CTRL_UserBreakpointKind kind;
|
||||
CTRL_UserBreakpointFlags flags;
|
||||
U64 id;
|
||||
String8 string;
|
||||
TxtPt pt;
|
||||
U64 size;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user