mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 04:42:32 +00:00
[qwaq] Add initial qwaq debugger system
It's not hooked up yet, and only very basic functionality is implemented, but I think this approach will work.
This commit is contained in:
parent
e56d00f27f
commit
12f7b52fff
4 changed files with 356 additions and 0 deletions
|
@ -47,6 +47,7 @@ qwaq_curses_libs= \
|
|||
qwaq_curses_SOURCES= \
|
||||
main.c \
|
||||
qwaq-curses.c \
|
||||
qwaq-debug.c \
|
||||
qwaq-editbuffer-bi.c \
|
||||
qwaq-input.c
|
||||
qwaq_curses_LDADD= $(qwaq_curses_libs) $(QWAQ_LIBS) \
|
||||
|
|
321
ruamoko/qwaq/qwaq-debug.c
Normal file
321
ruamoko/qwaq/qwaq-debug.c
Normal file
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
qwaq-debug.c
|
||||
|
||||
Debugging support
|
||||
|
||||
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
|
||||
|
||||
Author: Bill Currie <bill@taniwha.org>
|
||||
Date: 2020/03/24
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
|
||||
Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA
|
||||
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/hash.h"
|
||||
#include "QF/keys.h"
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "qwaq.h"
|
||||
#include "event.h"
|
||||
#include "qwaq-curses.h"
|
||||
#include "qwaq-debug.h"
|
||||
|
||||
typedef struct qwaq_target_s {
|
||||
progs_t *pr;
|
||||
struct qwaq_debug_s *debugger;
|
||||
int handle;
|
||||
prdebug_t event;
|
||||
rwcond_t run_cond;
|
||||
} qwaq_target_t;
|
||||
|
||||
typedef struct qwaq_debug_s {
|
||||
progs_t *pr;
|
||||
qwaq_resources_t *qwaq; // to communicate with the debugger thread
|
||||
PR_RESMAP (qwaq_target_t) targets;
|
||||
} qwaq_debug_t;
|
||||
|
||||
#define always_inline inline __attribute__((__always_inline__))
|
||||
|
||||
static qwaq_target_t *
|
||||
target_new (qwaq_debug_t *debug)
|
||||
{
|
||||
PR_RESNEW (qwaq_target_t, debug->targets);
|
||||
}
|
||||
|
||||
static void
|
||||
target_free (qwaq_debug_t *debug, qwaq_target_t *target)
|
||||
{
|
||||
PR_RESFREE (qwaq_target_t, debug->targets, target);
|
||||
}
|
||||
|
||||
static void
|
||||
target_reset (qwaq_debug_t *debug)
|
||||
{
|
||||
PR_RESRESET (qwaq_target_t, debug->targets);
|
||||
}
|
||||
|
||||
static inline qwaq_target_t *
|
||||
target_get (qwaq_debug_t *debug, unsigned index)
|
||||
{
|
||||
PR_RESGET (debug->targets, index);
|
||||
}
|
||||
|
||||
static inline int
|
||||
target_index (qwaq_debug_t *debug, qwaq_target_t *target)
|
||||
{
|
||||
PR_RESINDEX (debug->targets, target);
|
||||
}
|
||||
|
||||
static always_inline qwaq_target_t * __attribute__((pure))
|
||||
get_target (qwaq_debug_t *debug, const char *name, int handle)
|
||||
{
|
||||
qwaq_target_t *target = target_get (debug, handle);
|
||||
|
||||
if (!target || !target->debugger) {
|
||||
PR_RunError (debug->pr, "invalid target passed to %s", name + 4);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
static void
|
||||
qwaq_debug_handler (prdebug_t debug_event, void *data)
|
||||
{
|
||||
__auto_type target = (qwaq_target_t *) data;
|
||||
qwaq_debug_t *debug = target->debugger;
|
||||
qwaq_event_t event = {};
|
||||
int ret;
|
||||
|
||||
target->event = debug_event;
|
||||
event.what = qe_debug_event;
|
||||
event.message.pointer_val = target->handle;
|
||||
|
||||
while ((ret = qwaq_add_event (debug->qwaq, &event)) == ETIMEDOUT) {
|
||||
// spin
|
||||
}
|
||||
if (ret == EINVAL) {
|
||||
Sys_Error ("event queue broke");
|
||||
}
|
||||
pthread_mutex_lock (&target->run_cond.mut);
|
||||
pthread_cond_wait (&target->run_cond.rcond, &target->run_cond.mut);
|
||||
pthread_mutex_unlock (&target->run_cond.mut);
|
||||
if (debug_event == prd_runerror || debug_event == prd_error) {
|
||||
pthread_exit ((void *) target->pr->error_string);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
qwaq_debug_clear (progs_t *pr, void *data)
|
||||
{
|
||||
__auto_type debug = (qwaq_debug_t *) data;
|
||||
target_reset (debug);
|
||||
}
|
||||
|
||||
static void
|
||||
qwaq_target_clear (progs_t *pr, void *data)
|
||||
{
|
||||
qwaq_target_t *target = pr->debug_data;
|
||||
target_free (target->debugger, target);
|
||||
}
|
||||
|
||||
//FIXME need a better way to get this from one thread to the others
|
||||
static qwaq_debug_t *qwaq_debug_data;
|
||||
|
||||
static int
|
||||
qwaq_target_load (progs_t *pr)
|
||||
{
|
||||
qwaq_target_t *target = target_new (qwaq_debug_data);
|
||||
target->pr = pr;
|
||||
target->debugger = qwaq_debug_data;
|
||||
target->handle = target_index (qwaq_debug_data, target);
|
||||
qwaq_init_cond (&target->run_cond);
|
||||
|
||||
pr->debug_handler = qwaq_debug_handler;
|
||||
pr->debug_data = target;
|
||||
|
||||
// start tracing immediately so the debugger has a chance to start up
|
||||
// before the target progs begin running
|
||||
pr->pr_trace = 1;
|
||||
pr->pr_trace_depth = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_set_trace (progs_t *pr)
|
||||
{
|
||||
__auto_type debug = PR_Resources_Find (pr, "qwaq-debug");
|
||||
pointer_t handle = P_INT (pr, 0);
|
||||
int state = P_INT (pr, 1);
|
||||
qwaq_target_t *target = get_target (debug, __FUNCTION__, handle);
|
||||
progs_t *tpr = target->pr;
|
||||
|
||||
tpr->pr_trace = state;
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_set_breakpoint (progs_t *pr)
|
||||
{
|
||||
__auto_type debug = PR_Resources_Find (pr, "qwaq-debug");
|
||||
pointer_t handle = P_INT (pr, 0);
|
||||
unsigned staddr = P_INT (pr, 1);
|
||||
qwaq_target_t *target = get_target (debug, __FUNCTION__, handle);
|
||||
progs_t *tpr = target->pr;
|
||||
|
||||
if (staddr >= tpr->progs->numstatements) {
|
||||
R_INT (pr) = -1;
|
||||
return;
|
||||
}
|
||||
tpr->pr_statements[staddr].op |= OP_BREAK;
|
||||
R_INT (pr) = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_clear_breakpoint (progs_t *pr)
|
||||
{
|
||||
__auto_type debug = PR_Resources_Find (pr, "qwaq-debug");
|
||||
pointer_t handle = P_INT (pr, 0);
|
||||
unsigned staddr = P_UINT (pr, 1);
|
||||
qwaq_target_t *target = get_target (debug, __FUNCTION__, handle);
|
||||
progs_t *tpr = target->pr;
|
||||
|
||||
if (staddr >= tpr->progs->numstatements) {
|
||||
R_INT (pr) = -1;
|
||||
return;
|
||||
}
|
||||
tpr->pr_statements[staddr].op &= ~OP_BREAK;
|
||||
R_INT (pr) = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_set_watchpoint (progs_t *pr)
|
||||
{
|
||||
__auto_type debug = PR_Resources_Find (pr, "qwaq-debug");
|
||||
pointer_t handle = P_INT (pr, 0);
|
||||
pointer_t offset = P_UINT (pr, 1);
|
||||
qwaq_target_t *target = get_target (debug, __FUNCTION__, handle);
|
||||
progs_t *tpr = target->pr;
|
||||
|
||||
if (offset >= tpr->globals_size) {
|
||||
R_INT (pr) = -1;
|
||||
return;
|
||||
}
|
||||
tpr->watch = &tpr->pr_globals[offset];
|
||||
R_INT (pr) = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_clear_watchpoint (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;
|
||||
|
||||
tpr->watch = 0;
|
||||
R_INT (pr) = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_continue (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);
|
||||
|
||||
pthread_mutex_lock (&target->run_cond.mut);
|
||||
pthread_cond_signal (&target->run_cond.rcond);
|
||||
pthread_mutex_unlock (&target->run_cond.mut);
|
||||
}
|
||||
|
||||
static void
|
||||
qdb_get_state (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;
|
||||
pr_lineno_t *lineno;
|
||||
pr_auxfunction_t *f;
|
||||
string_t file = 0;
|
||||
unsigned line = 0;
|
||||
unsigned staddr = tpr->pr_xstatement;
|
||||
func_t func = tpr->pr_xfunction - tpr->function_table;
|
||||
|
||||
lineno = PR_Find_Lineno (tpr, staddr);
|
||||
if (lineno) {
|
||||
f = PR_Get_Lineno_Func (pr, lineno);
|
||||
file = pr->pr_functions[f->function].s_file;
|
||||
func = f->function;
|
||||
line = PR_Get_Lineno_Line (pr, lineno);
|
||||
line += f->source_line;
|
||||
}
|
||||
|
||||
qdb_state_t state = {};
|
||||
state.staddr = staddr;
|
||||
state.func = func;
|
||||
state.file = file;
|
||||
state.line = line;
|
||||
|
||||
R_PACKED (pr, qdb_state_t) = state;
|
||||
}
|
||||
|
||||
static builtin_t builtins[] = {
|
||||
{"qdb_set_trace", qdb_set_trace, -1},
|
||||
{"qdb_set_breakpoint", qdb_set_breakpoint, -1},
|
||||
{"qdb_clear_breakpoint", qdb_clear_breakpoint, -1},
|
||||
{"qdb_set_watchpoint", qdb_set_watchpoint, -1},
|
||||
{"qdb_clear_watchpoint", qdb_clear_watchpoint, -1},
|
||||
{"qdb_continue", qdb_continue, -1},
|
||||
{"qdb_get_state", qdb_get_state, -1},
|
||||
{}
|
||||
};
|
||||
|
||||
void
|
||||
QWAQ_Debug_Init (progs_t *pr)
|
||||
{
|
||||
qwaq_debug_t *debug = calloc (sizeof (*debug), 1);
|
||||
qwaq_debug_data = debug; // FIXME ? see decl
|
||||
debug->pr = pr;
|
||||
debug->qwaq = PR_Resources_Find (pr, "qwaq");
|
||||
|
||||
PR_Resources_Register (pr, "qwaq-debug", debug, qwaq_debug_clear);
|
||||
PR_RegisterBuiltins (pr, builtins);
|
||||
}
|
||||
|
||||
void
|
||||
QWAQ_DebugTarget_Init (progs_t *pr)
|
||||
{
|
||||
PR_AddLoadFunc (pr, qwaq_target_load);
|
||||
PR_Resources_Register (pr, "qwaq-target", 0, qwaq_target_clear);
|
||||
}
|
31
ruamoko/qwaq/qwaq-debug.h
Normal file
31
ruamoko/qwaq/qwaq-debug.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef __qwaq_debug_h
|
||||
#define __qwaq_debug_h
|
||||
|
||||
#include "event.h"
|
||||
|
||||
enum {
|
||||
qe_debug_event = 0x0100,
|
||||
} qwaq_debug_messages;
|
||||
|
||||
#ifdef __QFCC__
|
||||
|
||||
//FIXME add unsigned to qfcc
|
||||
#ifndef unsigned
|
||||
#define unsigned int
|
||||
#define umax 0x7fffffff
|
||||
#endif
|
||||
|
||||
#else//GCC
|
||||
#endif
|
||||
|
||||
typedef struct qdb_state_s {
|
||||
unsigned staddr;
|
||||
unsigned func;
|
||||
unsigned file;
|
||||
unsigned line;
|
||||
} qdb_state_t;
|
||||
|
||||
void QWAQ_Debug_Init (progs_t *pr);
|
||||
void QWAQ_DebugTarget_Init (progs_t *pr);
|
||||
|
||||
#endif//__qwaq_debug_h
|
|
@ -5,8 +5,11 @@
|
|||
#include <Object.h>
|
||||
|
||||
//FIXME add unsigned to qfcc
|
||||
#ifndef unsigned
|
||||
#define unsigned int
|
||||
#define umax 0x7fffffff
|
||||
#endif
|
||||
|
||||
#endif//__QFCC__
|
||||
|
||||
typedef struct eb_sel_s {
|
||||
|
|
Loading…
Reference in a new issue