From 12f7b52fff8dab33fd0d2316f659ac38d2e01942 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 24 Mar 2020 19:52:35 +0900 Subject: [PATCH] [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. --- ruamoko/qwaq/Makefile.am | 1 + ruamoko/qwaq/qwaq-debug.c | 321 +++++++++++++++++++++++++++++++++ ruamoko/qwaq/qwaq-debug.h | 31 ++++ ruamoko/qwaq/qwaq-editbuffer.h | 3 + 4 files changed, 356 insertions(+) create mode 100644 ruamoko/qwaq/qwaq-debug.c create mode 100644 ruamoko/qwaq/qwaq-debug.h diff --git a/ruamoko/qwaq/Makefile.am b/ruamoko/qwaq/Makefile.am index 194c31933..182ed2565 100644 --- a/ruamoko/qwaq/Makefile.am +++ b/ruamoko/qwaq/Makefile.am @@ -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) \ diff --git a/ruamoko/qwaq/qwaq-debug.c b/ruamoko/qwaq/qwaq-debug.c new file mode 100644 index 000000000..8edcf992d --- /dev/null +++ b/ruamoko/qwaq/qwaq-debug.c @@ -0,0 +1,321 @@ +/* + qwaq-debug.c + + Debugging support + + Copyright (C) 2020 Bill Currie + + Author: Bill Currie + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/ruamoko/qwaq/qwaq-debug.h b/ruamoko/qwaq/qwaq-debug.h new file mode 100644 index 000000000..c95788951 --- /dev/null +++ b/ruamoko/qwaq/qwaq-debug.h @@ -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 diff --git a/ruamoko/qwaq/qwaq-editbuffer.h b/ruamoko/qwaq/qwaq-editbuffer.h index 215937a55..4e5062ab2 100644 --- a/ruamoko/qwaq/qwaq-editbuffer.h +++ b/ruamoko/qwaq/qwaq-editbuffer.h @@ -5,8 +5,11 @@ #include //FIXME add unsigned to qfcc +#ifndef unsigned #define unsigned int #define umax 0x7fffffff +#endif + #endif//__QFCC__ typedef struct eb_sel_s {