quakeforge/ruamoko/qwaq/debugger/debugger.r
Bill Currie d23c9582f1 [qwaq] Implement execute-to-cursor
Support for finding the first address associated with a source line was
added to the engine, returning 0 if not found.

A temporary breakpoint is set and the progs allowed to run free.
However, better handling of temporary breakpoitns is needed as currently
a "permanent" breakpoint will be cleared without clearing the temporary
breakpoing if the permanent breakpoing is hit while execut-to-cursor is
running.
2021-06-08 16:54:04 +09:00

318 lines
7.7 KiB
R

#include <QF/keys.h>
#include <Array.h>
#include <string.h>
#include <types.h>
#include "ruamoko/qwaq/ui/curses.h"
#include "ruamoko/qwaq/ui/listener.h"
#include "ruamoko/qwaq/ui/proxyview.h"
#include "ruamoko/qwaq/ui/scrollbar.h"
#include "ruamoko/qwaq/ui/tableview.h"
#include "ruamoko/qwaq/ui/window.h"
#include "ruamoko/qwaq/debugger/debugger.h"
#include "ruamoko/qwaq/debugger/typeencodings.h"
#include "ruamoko/qwaq/editor/editor.h"
#include "ruamoko/qwaq/qwaq-app.h"
@implementation Debugger
+(Debugger *)withTarget:(qdb_target_t)target
{
return [[[self alloc] initWithTarget:target] autorelease];
}
-(qdb_target_t)target
{
return target;
}
-initWithTarget:(qdb_target_t) target
{
if (!(self = [super init])) {
return nil;
}
self.target = target;
Extent s = [application size];
files = [[Array array] retain];
source_window = [Window withRect: {nil, s}];
[application addView:source_window];
source_scrollbar = [ScrollBar vertical:s.height - 2 at:{s.width - 1, 1}];
[source_window insert:source_scrollbar];
return self;
}
-(void)dealloc
{
[files release];
[locals_data release];
[super dealloc];
}
-(Editor *) find_file:(string) filename
{
Editor *file;
string filepath = qdb_get_file_path (target, filename);
for (int i = [files count]; i-- > 0; ) {
file = [files objectAtIndex: i];
if ([file filepath] == filepath) {
return file;
}
}
Rect rect = {{1, 1}, [source_window size]};
rect.extent.width -= 2;
rect.extent.height -= 2;
file = [Editor withRect:rect file:filename path:filepath];
[files addObject: file];
return file;
}
-(void) setup
{
qdb_state_t state = qdb_get_state (target);
current_file = [self find_file: state.file];
file_proxy = [ProxyView withView: current_file];
[[current_file gotoLine:state.line - 1] highlightLine];
[[current_file onEvent] addListener: self :@selector(proxy_event::)];
[current_file setVerticalScrollBar:source_scrollbar];
//FIXME id<View>?
[source_window insertSelected: (View *) file_proxy];
[source_window setTitle:[current_file filename]];
[source_window redraw];
locals_window = [Window withRect:{{0, 0}, {40, 10}}];
[locals_window setBackground: color_palette[064]];
[locals_window setTitle: "Locals"];
locals_data = [[LocalsData withTarget:target] retain];
locals_view = [TableView withRect:{{1, 1}, {38, 8}}];
[locals_view addColumn:[TableViewColumn named:"name" width:12]];
[locals_view addColumn:[[TableViewColumn named:"value" width:26]
setGrowMode:gfGrowHiX]];
ScrollBar *sb = [ScrollBar vertical:8 at:{39, 1}];
[locals_view setVerticalScrollBar:sb];
[locals_view setDataSource:locals_data];
[locals_window insertSelected: locals_view];
[locals_window insert: sb];
[application addView: locals_window];
[[locals_view onEvent] addListener:self :@selector(proxy_event::)];
}
-(void) show_line
{
qdb_state_t state = qdb_get_state (target);
Editor *file = [self find_file: state.file];
if (current_file != file) {
[current_file setVerticalScrollBar:nil];
[[current_file onEvent] removeListener:self :@selector(proxy_event::)];
[file_proxy setView:file];
[[file onEvent] addListener:self :@selector(proxy_event::)];
[file setVerticalScrollBar:source_scrollbar];
[source_window setTitle:[file filename]];
current_file = file;
}
[[current_file gotoLine:state.line - 1] highlightLine];
[source_window redraw];
}
-(void)update_watchvars
{
qdb_state_t state = qdb_get_state (target);
[locals_data setFunction:state.func];
[locals_data fetchData];
[locals_view redraw];
}
static int
proxy_event_stopped (Debugger *self, id proxy, qwaq_event_t *event)
{
if (event.what == qe_mouseclick && !(event.mouse.buttons & 0x78)) {
if (proxy == self.current_file) {
printf ("%s\n", [proxy getWordAt: {event.mouse.x, event.mouse.y}]);
[self.source_window redraw];
return 1;
}
} 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.trace_cond.state = qdb_get_state (self.target);
self.running = 1;
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);
self.running = 1;
qdb_continue (self.target);
return 1;
case QFK_F4:
string file = [self.current_file filename];
unsigned line = [self.current_file cursor].y + 1;
unsigned addr = qdb_get_source_line_addr (self.target,
file, line);
int set = -1;
if (addr) {
set = qdb_set_breakpoint (self.target, addr);
}
if (set >= 0) {
qdb_set_trace (self.target, 0);
if (set) {
self.breakHandler = @selector(breakKeep);
} else {
self.breakHandler = @selector(breakClear);
}
self.running = 1;
qdb_continue (self.target);
} else {
}
return 1;
}
}
return 0;
}
static int
proxy_event_running (Debugger *self, id proxy, qwaq_event_t *event)
{
return 0;
}
-(void)proxy_event:(id)proxy :(qwaq_event_t *)event
{
if (running) {
if (proxy_event_running (self, proxy, event)) {
event.what = qe_none;
}
} else {
if (proxy_event_stopped (self, proxy, event)) {
event.what = qe_none;
}
}
}
-stop:(prdebug_t)reason
{
running = 0;
if (!file_proxy) {
[self setup];
}
[self show_line];
[self update_watchvars];
return self;
}
// 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);
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)) {
[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;
}
-breakKeep
{
return self;
}
-breakClear
{
qdb_state_t state = qdb_get_state (target);
qdb_clear_breakpoint (target, state.staddr);
return self;
}
-handleDebugEvent
{
if (qdb_get_event (target, &event)) {
switch (event.what) {
case prd_none:
break; // shouldn't happen
case prd_trace:
[self performSelector:traceHandler];
break;
case prd_breakpoint:
case prd_watchpoint:
[self performSelector:breakHandler];
[self stop:event.what];
break;
case prd_subenter:
if (sub_cond.onEnter) {
[self stop:event.what];
} else {
qdb_continue (self.target);
}
break;
case prd_subexit:
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:
wprintf(stdscr, "Program ended: %d\n", event.exit_code);
[self stop:event.what];
break;
case prd_runerror:
case prd_error:
wprintf(stdscr, "%s\n", event.message);
[self stop:event.what];
break;
}
}
return self;
}
@end