mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-02-17 01:11:45 +00:00
[qwaq] Implement step-over tracing
I decided that stopping in between function calls that are on the same line is a good thing as it gives a chance to skip over the first but step into the second.
This commit is contained in:
parent
c09f57c39e
commit
63caa5794d
7 changed files with 140 additions and 22 deletions
|
@ -73,6 +73,7 @@ typedef enum prdebug_e {
|
|||
prd_watchpoint,
|
||||
prd_subenter,
|
||||
prd_subexit, // current invocation of PR_ExecuteProgram finished
|
||||
prd_begin, // not sent by VM
|
||||
prd_terminate, // not sent by VM
|
||||
prd_runerror,
|
||||
prd_error, // lower level error thann prd_runerror
|
||||
|
|
|
@ -296,6 +296,41 @@ qdb_get_state (progs_t *pr)
|
|||
R_PACKED (pr, qdb_state_t) = state;
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_get_stack_depth (progs_t *pr)
|
||||
{
|
||||
__auto_type debug = PR_Resources_Find (pr, "qwaq-debug");
|
||||
pointer_t handle = P_INT (pr, 0);
|
||||
qwaq_target_t *target = get_target (debug, __FUNCTION__, handle);
|
||||
progs_t *tpr = target->pr;
|
||||
|
||||
R_INT (pr) = tpr->pr_depth;
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_get_stack (progs_t *pr)
|
||||
{
|
||||
__auto_type debug = PR_Resources_Find (pr, "qwaq-debug");
|
||||
pointer_t handle = P_INT (pr, 0);
|
||||
qwaq_target_t *target = get_target (debug, __FUNCTION__, handle);
|
||||
progs_t *tpr = target->pr;
|
||||
int count = tpr->pr_depth;
|
||||
|
||||
R_POINTER (pr) = 0;
|
||||
if (count > 0) {
|
||||
size_t size = count * sizeof (qdb_stack_t);
|
||||
string_t stack_block = PR_AllocTempBlock (pr, size);
|
||||
__auto_type stack = (qdb_stack_t *) PR_GetString (pr, stack_block);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
stack[i].staddr = tpr->pr_stack[i].staddr;
|
||||
stack[i].func = tpr->pr_stack[i].func - tpr->pr_xfunction;
|
||||
//XXX temp strings (need access somehow)
|
||||
}
|
||||
R_POINTER (pr) = PR_SetPointer (pr, stack);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_get_event (progs_t *pr)
|
||||
{
|
||||
|
@ -308,12 +343,15 @@ qdb_get_event (progs_t *pr)
|
|||
event->what = target->event;
|
||||
switch (event->what) {
|
||||
case prd_subenter:
|
||||
event->func = *(func_t *) target->param;
|
||||
event->function = *(func_t *) target->param;
|
||||
break;
|
||||
case prd_runerror:
|
||||
case prd_error:
|
||||
event->message = PR_SetReturnString (pr, *(char **) target->param);
|
||||
break;
|
||||
case prd_begin:
|
||||
event->function = *(func_t *) target->param;
|
||||
break;
|
||||
case prd_terminate:
|
||||
event->exit_code = *(int *) target->param;
|
||||
break;
|
||||
|
@ -411,7 +449,7 @@ qdb_find_field (progs_t *pr)
|
|||
static void
|
||||
return_function (progs_t *pr, dfunction_t *func)
|
||||
{
|
||||
R_INT (pr) = 0;
|
||||
R_POINTER (pr) = 0;
|
||||
if (func) {
|
||||
__auto_type f
|
||||
= (qdb_function_t *) PR_Zone_Malloc (pr, sizeof (qdb_function_t));
|
||||
|
@ -422,7 +460,7 @@ return_function (progs_t *pr, dfunction_t *func)
|
|||
f->name = func->s_name;
|
||||
f->file = func->s_file;
|
||||
f->num_params = func->numparms;
|
||||
R_INT (pr) = PR_SetPointer (pr, f);
|
||||
R_POINTER (pr) = PR_SetPointer (pr, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,7 +496,7 @@ qdb_get_function (progs_t *pr)
|
|||
static void
|
||||
return_auxfunction (progs_t *pr, pr_auxfunction_t *auxfunc)
|
||||
{
|
||||
R_INT (pr) = 0;
|
||||
R_POINTER (pr) = 0;
|
||||
if (auxfunc) {
|
||||
__auto_type f
|
||||
= (qdb_auxfunction_t *) PR_Zone_Malloc (pr,
|
||||
|
@ -469,7 +507,7 @@ return_auxfunction (progs_t *pr, pr_auxfunction_t *auxfunc)
|
|||
f->local_defs = auxfunc->local_defs;
|
||||
f->num_locals = auxfunc->num_locals;
|
||||
f->return_type = auxfunc->return_type;
|
||||
R_INT (pr) = PR_SetPointer (pr, f);
|
||||
R_POINTER (pr) = PR_SetPointer (pr, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,7 +552,7 @@ qdb_get_local_defs (progs_t *pr)
|
|||
pr_uint_t fnum = P_UINT (pr, 1);
|
||||
pr_auxfunction_t *auxfunc = PR_Debug_MappedAuxFunction (tpr, fnum);
|
||||
|
||||
R_INT (pr) = 0;
|
||||
R_POINTER (pr) = 0;
|
||||
if (auxfunc && auxfunc->num_locals) {
|
||||
pr_def_t *defs = PR_Debug_LocalDefs (tpr, auxfunc);
|
||||
__auto_type qdefs
|
||||
|
@ -526,7 +564,7 @@ qdb_get_local_defs (progs_t *pr)
|
|||
qdefs[i].name = defs[i].name;
|
||||
qdefs[i].type_encoding = defs[i].type_encoding;
|
||||
}
|
||||
R_INT (pr) = PR_SetPointer (pr, qdefs);
|
||||
R_POINTER (pr) = PR_SetPointer (pr, qdefs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,6 +576,8 @@ static builtin_t builtins[] = {
|
|||
{"qdb_clear_watchpoint", qdb_clear_watchpoint, -1},
|
||||
{"qdb_continue", qdb_continue, -1},
|
||||
{"qdb_get_state", qdb_get_state, -1},
|
||||
{"qdb_get_stack_depth", qdb_get_stack_depth, -1},
|
||||
{"qdb_get_stack", qdb_get_stack, -1},
|
||||
{"qdb_get_event", qdb_get_event, -1},
|
||||
{"qdb_get_data", qdb_get_data, -1},
|
||||
{"qdb_get_string|{tag qdb_target_s=}i", qdb_get_string, -1},
|
||||
|
|
|
@ -213,6 +213,11 @@ spawn_progs (qwaq_thread_t *thread)
|
|||
PR_Undefined (pr, "function", "main");
|
||||
}
|
||||
|
||||
if (thread->pr->debug_handler) {
|
||||
thread->pr->debug_handler (prd_begin, &thread->main_func,
|
||||
thread->pr->debug_data);
|
||||
}
|
||||
|
||||
if (!PR_RunPostLoadFuncs (pr)) {
|
||||
PR_Error (pr, "unable to load %s", pr->progs_name);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ typedef struct qdb_event_s {
|
|||
prdebug_t what;
|
||||
union {
|
||||
string_t message;
|
||||
unsigned func;
|
||||
unsigned function;
|
||||
int exit_code;
|
||||
};
|
||||
} qdb_event_t;
|
||||
|
@ -36,6 +36,12 @@ typedef struct qdb_state_s {
|
|||
unsigned line;
|
||||
} qdb_state_t;
|
||||
|
||||
typedef struct qdb_stack_s {
|
||||
unsigned staddr; // return address
|
||||
unsigned func; // calling function
|
||||
// FIXME temp strings
|
||||
} qdb_stack_t;
|
||||
|
||||
typedef struct qdb_def_s {
|
||||
unsigned type_size; // type in lower 16, size in upper 16
|
||||
unsigned offset;
|
||||
|
@ -73,6 +79,8 @@ int qdb_set_watchpoint (qdb_target_t target, unsigned offset);
|
|||
int qdb_clear_watchpoint (qdb_target_t target);
|
||||
int qdb_continue (qdb_target_t target);
|
||||
qdb_state_t qdb_get_state (qdb_target_t target);
|
||||
int qdb_get_stack_depth (qdb_target_t target);
|
||||
qdb_stack_t *qdb_get_stack (qdb_target_t target);
|
||||
int qdb_get_event (qdb_target_t target, qdb_event_t *event);
|
||||
int qdb_get_data (qdb_target_t target, unsigned src, unsigned len, void *dst);
|
||||
@overload string qdb_get_string (qdb_target_t target, unsigned str);
|
||||
|
|
|
@ -10,6 +10,8 @@ int qdb_set_watchpoint (qdb_target_t target, unsigned offset) = #0;
|
|||
int qdb_clear_watchpoint (qdb_target_t target) = #0;
|
||||
int qdb_continue (qdb_target_t target) = #0;
|
||||
qdb_state_t qdb_get_state (qdb_target_t target) = #0;
|
||||
int qdb_get_stack_depth (qdb_target_t target) = #0;
|
||||
qdb_stack_t *qdb_get_stack (qdb_target_t target) = #0;
|
||||
int qdb_get_event (qdb_target_t target, qdb_event_t *event) = #0;
|
||||
int qdb_get_data (qdb_target_t target, unsigned src, unsigned len,
|
||||
void *dst) = #0;
|
||||
|
|
|
@ -18,7 +18,16 @@
|
|||
{
|
||||
qdb_target_t target;
|
||||
qdb_event_t event;
|
||||
qdb_state_t last_state;
|
||||
struct {
|
||||
qdb_state_t state;
|
||||
int depth;
|
||||
int until_function;
|
||||
} trace_cond;
|
||||
struct {
|
||||
int onEnter;
|
||||
int onExit;
|
||||
} sub_cond;
|
||||
SEL traceHandler;
|
||||
|
||||
Window *source_window;
|
||||
ScrollBar *source_scrollbar;
|
||||
|
|
|
@ -137,8 +137,18 @@ proxy_event (Debugger *self, id proxy, qwaq_event_t *event)
|
|||
} else if (event.what == qe_keydown) {
|
||||
switch (event.key.code) {
|
||||
case QFK_F7:
|
||||
case 's':
|
||||
self.traceHandler = @selector(traceStep);
|
||||
qdb_set_trace (self.target, 1);
|
||||
self.last_state = qdb_get_state (self.target);
|
||||
self.trace_cond.state = qdb_get_state (self.target);
|
||||
qdb_continue (self.target);
|
||||
return 1;
|
||||
case QFK_F8:
|
||||
case 'n':
|
||||
self.traceHandler = @selector(traceNext);
|
||||
qdb_set_trace (self.target, 1);
|
||||
self.trace_cond.state = qdb_get_state (self.target);
|
||||
self.trace_cond.depth = qdb_get_stack_depth (self.target);
|
||||
qdb_continue (self.target);
|
||||
return 1;
|
||||
}
|
||||
|
@ -163,21 +173,50 @@ proxy_event (Debugger *self, id proxy, qwaq_event_t *event)
|
|||
return self;
|
||||
}
|
||||
|
||||
-trace
|
||||
// stop only if the progs have not advanced (may be a broken jump)
|
||||
// or the progs have advanced to a different source line
|
||||
static int
|
||||
is_new_line (qdb_state_t last_state, qdb_state_t state)
|
||||
{
|
||||
return !(last_state.staddr != state.staddr
|
||||
&& last_state.func == state.func
|
||||
&& last_state.file == state.file
|
||||
&& last_state.line == state.line);
|
||||
}
|
||||
|
||||
-traceStep
|
||||
{
|
||||
qdb_state_t state = qdb_get_state (target);
|
||||
|
||||
// stop only if the progs have not advanced (may be a broken jump)
|
||||
// or the progs have advanced to a different source line
|
||||
if (last_state.staddr != state.staddr
|
||||
&& last_state.func == state.func
|
||||
&& last_state.file == state.file
|
||||
&& last_state.line == state.line) {
|
||||
last_state = state;
|
||||
qdb_continue (self.target);
|
||||
if (trace_cond.until_function && trace_cond.until_function == state.func) {
|
||||
trace_cond.until_function = 0;
|
||||
[self stop:prd_trace];
|
||||
return self;
|
||||
}
|
||||
[self stop:prd_trace];
|
||||
if (is_new_line(trace_cond.state, state)) {
|
||||
[self stop:prd_trace];
|
||||
return self;
|
||||
}
|
||||
trace_cond.state = state;
|
||||
qdb_continue (self.target);
|
||||
return self;
|
||||
}
|
||||
|
||||
-traceNext
|
||||
{
|
||||
qdb_state_t state = qdb_get_state (target);
|
||||
if (trace_cond.until_function && trace_cond.until_function == state.func) {
|
||||
trace_cond.until_function = 0;
|
||||
[self stop:prd_trace];
|
||||
return self;
|
||||
}
|
||||
if (is_new_line(trace_cond.state, state)
|
||||
&& qdb_get_stack_depth (target) <= trace_cond.depth) {
|
||||
[self stop:prd_trace];
|
||||
return self;
|
||||
}
|
||||
trace_cond.state = state;
|
||||
qdb_continue (self.target);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -188,16 +227,30 @@ proxy_event (Debugger *self, id proxy, qwaq_event_t *event)
|
|||
case prd_none:
|
||||
break; // shouldn't happen
|
||||
case prd_trace:
|
||||
[self trace];
|
||||
[self performSelector:traceHandler];
|
||||
break;
|
||||
case prd_breakpoint:
|
||||
case prd_watchpoint:
|
||||
[self stop:event.what];
|
||||
break;
|
||||
case prd_subenter:
|
||||
[self stop:event.what];
|
||||
printf("subenter\n");
|
||||
if (sub_cond.onEnter) {
|
||||
[self stop:event.what];
|
||||
} else {
|
||||
qdb_continue (self.target);
|
||||
}
|
||||
break;
|
||||
case prd_subexit:
|
||||
printf("subexit\n");
|
||||
if (sub_cond.onExit) {
|
||||
[self stop:event.what];
|
||||
} else {
|
||||
qdb_continue (self.target);
|
||||
}
|
||||
break;
|
||||
case prd_begin:
|
||||
trace_cond.until_function = event.function;
|
||||
[self stop:event.what];
|
||||
break;
|
||||
case prd_terminate:
|
||||
|
|
Loading…
Reference in a new issue