[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:
Bill Currie 2020-04-02 17:44:53 +09:00
parent c09f57c39e
commit 63caa5794d
7 changed files with 140 additions and 22 deletions

View file

@ -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

View file

@ -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},

View file

@ -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);
}

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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: