From 0df247d4c188133d5f8f309607d2278ebad4ce13 Mon Sep 17 00:00:00 2001 From: Shpoike Date: Sat, 28 Dec 2024 15:51:21 +0000 Subject: [PATCH] Add setwatchpoint builtin, for debugging stuff that would otherwise be hard to monitor. --- engine/client/pr_csqc.c | 7 ++++--- engine/client/pr_menu.c | 31 ++++++++++++++++++++++++++++--- engine/common/pr_bgcmd.c | 20 ++++++++++++++++++++ engine/common/pr_common.h | 1 + engine/qclib/execloop.h | 4 ++-- engine/qclib/pr_exec.c | 38 +++++++++++++++++++++++++++++++++----- engine/qclib/progsint.h | 2 +- engine/qclib/progslib.h | 4 ++-- engine/qclib/test.c | 3 ++- engine/server/pr_cmds.c | 4 ++-- engine/server/server.h | 2 +- engine/server/sv_main.c | 2 +- 12 files changed, 97 insertions(+), 21 deletions(-) diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 8af0aeb0c..09756e514 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -127,7 +127,7 @@ static csqcglobals_t csqcg; playerview_t csqc_nullview; -static void VARGS CSQC_Abort (char *format, ...); //an error occured. +static void VARGS CSQC_Abort (const char *format, ...); //an error occured. static void cs_set_input_state (usercmd_t *cmd); //fixme: we should be using entity numbers, not view numbers. @@ -7055,6 +7055,7 @@ static struct { {"cprint", PF_cl_cprint, 338}, // #338 void(string s) cprint (EXT_CSQC) {"print", PF_print, 339}, // #339 void(string s) print (EXT_CSQC) + {"setwatchpoint", PF_setwatchpoint, 0}, //340 {"keynumtostring", PF_cl_keynumtostring, 340}, // #340 string(float keynum) keynumtostring (EXT_CSQC) @@ -7522,7 +7523,7 @@ static progparms_t csqcprogparms; //Any menu builtin error or anything like that will come here. -static void VARGS CSQC_Abort (char *format, ...) //an error occured. +static void VARGS CSQC_Abort (const char *format, ...) //an error occured. { va_list argptr; char string[1024]; @@ -8679,7 +8680,7 @@ void CSQC_WatchPoint_f(void) Con_Printf("csqc not running\n"); return; } - if (csqcprogs->SetWatchPoint(csqcprogs, variable)) + if (csqcprogs->SetWatchPoint(csqcprogs, variable, variable)) Con_Printf("Watchpoint set\n"); else Con_Printf("Watchpoint cleared\n"); diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 2c3455e40..7600deed9 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -2631,6 +2631,7 @@ static struct { {"print_csqc", PF_print, 339}, + {"setwatchpoint", PF_setwatchpoint, 0}, {"keynumtostring_csqc", PF_cl_keynumtostring, 340}, {"stringtokeynum_csqc", PF_cl_stringtokeynum, 341}, {"getkeybind", PF_cl_getkeybind, 342}, @@ -3131,7 +3132,7 @@ void *VARGS PR_CB_Malloc(int size); //these functions should be tracked by the l void VARGS PR_CB_Free(void *mem); //Any menu builtin error or anything like that will come here. -void VARGS Menu_Abort (char *format, ...) +void VARGS Menu_Abort (const char *format, ...) { va_list argptr; char string[1024]; @@ -3444,7 +3445,7 @@ static void MP_Poke_f(void) Con_TPrintf ("not supported.\n"); } -void MP_Breakpoint_f(void) +static void MP_Breakpoint_f(void) { int wasset; int isset; @@ -3468,17 +3469,41 @@ void MP_Breakpoint_f(void) Cvar_Set(Cvar_FindVar("pr_debugger"), "1"); } +static void MP_Watchpoint_f(void) +{ + char *variable = Cmd_Argv(1); + if (!*variable) + variable = NULL; + + if (!menu_world.progs) + { + Con_Printf("menuqc not running\n"); + return; + } + if (menu_world.progs->SetWatchPoint(menu_world.progs, variable, variable)) + Con_Printf("Watchpoint set\n"); + else + Con_Printf("Watchpoint cleared\n"); +} +static void MP_Profile_f(void) +{ + if (menu_world.progs && menu_world.progs->DumpProfile) + if (!menu_world.progs->DumpProfile(menu_world.progs, !atof(Cmd_Argv(1)))) + Con_Printf("Enabled menuqc Profiling.\n"); +} void MP_RegisterCvarsAndCmds(void) { Cmd_AddCommand("coredump_menuqc", MP_CoreDump_f); Cmd_AddCommand("menu_cmd", MP_GameCommand_f); - Cmd_AddCommand("breakpoint_menu", MP_Breakpoint_f); + Cmd_AddCommand("breakpoint_menuqc", MP_Breakpoint_f); + Cmd_AddCommand("watchpoint_menuqc", MP_Watchpoint_f); #ifdef HAVE_LEGACY Cmd_AddCommand("loadfont", CL_LoadFont_f); #endif Cmd_AddCommand("poke_menuqc", MP_Poke_f); + Cmd_AddCommand("profile_menuqc", MP_Profile_f); Cvar_Register(&forceqmenu, MENUPROGSGROUP); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index de7fa4adc..16539e298 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -7103,6 +7103,26 @@ void QCBUILTIN PF_externcall (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl } } +void QCBUILTIN PF_setwatchpoint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + const char *desc = PR_GetStringOfs(prinst, OFS_PARM0); + int type = G_FLOAT(OFS_PARM1); + int qcptr = G_INT(OFS_PARM2); + char variable[64]; + + if (type == ev_float) + Q_snprintfz(variable,sizeof(variable), "*(float*)%#x", qcptr); + else if (type == ev_string) + Q_snprintfz(variable,sizeof(variable), "*(string*)%#x", qcptr); + else + Q_snprintfz(variable,sizeof(variable), "*(int*)%#x", qcptr); + + if (prinst->SetWatchPoint(prinst, desc, variable)) + Con_DPrintf("Watchpoint set\n"); + else + Con_Printf("Watchpoint failure\n"); +} + void QCBUILTIN PF_traceon (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { prinst->debug_trace = DEBUG_TRACE_INTO; diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index d26c542fe..63980f2ba 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -195,6 +195,7 @@ void QCBUILTIN PF_rotatevectorsbymatrix (pubprogfuncs_t *prinst, struct globalva void QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_coredump (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_setwatchpoint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_traceon (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_traceoff (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_eprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 4af2cd6d5..9a4da454a 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -57,9 +57,9 @@ //this will fire on the next instruction after the variable got changed. prinst.pr_xstatement = s; if (current_progstate->linenums) - externs->Printf("Watch point hit in %s:%u, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), current_progstate->linenums[s-1], prinst.watch_name); + externs->Printf("^b^3Watch point hit in %s:%u, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), current_progstate->linenums[s-1], prinst.watch_name); else - externs->Printf("Watch point hit in %s, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), prinst.watch_name); + externs->Printf("^b^3Watch point hit in %s, \"%s\" changed", PR_StringToNative(&progfuncs->funcs, prinst.pr_xfunction->s_name), prinst.watch_name); switch(prinst.watch_type) { case ev_float: diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 1d607e65f..e262b024d 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -715,6 +715,34 @@ pbool LocateDebugTerm(progfuncs_t *progfuncs, const char *key, eval_t **result, struct edictrun_s *ed; // etype_t ptrtype = ev_void; + if (!strncmp(key, "*(float*)", 9)) + { + fofs = strtoul(key+9, NULL, 0); + if (fofs < 0 || fofs+3 >= prinst.addressableused) + return false; + *result = (eval_t*)(pr_strings + fofs); + *rettype = ev_float; + return true; + } + if (!strncmp(key, "*(int*)", 7)) + { + fofs = strtoul(key+7, NULL, 0); + if (fofs < 0 || fofs+3 >= prinst.addressableused) + return false; + *result = (eval_t*)(pr_strings + fofs); + *rettype = ev_integer; + return true; + } + if (!strncmp(key, "*(string*)", 7)) + { + fofs = strtoul(key+7, NULL, 0); + if (fofs < 0 || fofs+3 >= prinst.addressableused) + return false; + *result = (eval_t*)(pr_strings + fofs); + *rettype = ev_string; + return true; + } + c = strchr(key, '.'); if (c) *c = '\0'; def = ED_FindLocalOrGlobal(progfuncs, key, &val); @@ -797,14 +825,14 @@ pbool LocateDebugTerm(progfuncs_t *progfuncs, const char *key, eval_t **result, return true; } -pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *key) +pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *desc, const char *location) { progfuncs_t *progfuncs = (progfuncs_t *)ppf; eval_t *val; eval_t fakeval; etype_t type; - if (!key) + if (!location) { free(prinst.watch_name); prinst.watch_name = NULL; @@ -812,9 +840,9 @@ pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *key) prinst.watch_type = ev_void; return false; } - if (!LocateDebugTerm(progfuncs, key, &val, &type, &fakeval)) + if (!LocateDebugTerm(progfuncs, location, &val, &type, &fakeval)) { - externs->Printf("Unable to evaluate watch term \"%s\"\n", key); + externs->Printf("Unable to evaluate watch term \"%s\"\n", location); return false; } if (val == &fakeval) @@ -829,7 +857,7 @@ pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *ppf, const char *key) } free(prinst.watch_name); - prinst.watch_name = strdup(key); + prinst.watch_name = strdup(desc); prinst.watch_ptr = val; prinst.watch_old = *prinst.watch_ptr; prinst.watch_type = type &~ DEF_SAVEGLOBAL; diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index 1c56a3503..acc08417d 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -328,7 +328,7 @@ typedef struct edictrun_s int PDECL Comp_Begin(pubprogfuncs_t *progfuncs, int nump, const char **parms); int PDECL Comp_Continue(pubprogfuncs_t *progfuncs); -pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, const char *key); +pbool PDECL PR_SetWatchPoint(pubprogfuncs_t *progfuncs, const char *desc, const char *location); char *PDECL PR_EvaluateDebugString(pubprogfuncs_t *progfuncs, const char *key); char *PDECL PR_SaveEnts(pubprogfuncs_t *progfuncs, char *mem, size_t *size, size_t maxsize, int mode); int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PDECL *memoryreset) (pubprogfuncs_t *progfuncs, void *ctx), void (PDECL *entspawned) (pubprogfuncs_t *progfuncs, struct edict_s *ed, void *ctx, const char *entstart, const char *entend), pbool(PDECL *extendedterm)(pubprogfuncs_t *progfuncs, void *ctx, const char **extline)); diff --git a/engine/qclib/progslib.h b/engine/qclib/progslib.h index 7f4693f69..ee9056c8d 100644 --- a/engine/qclib/progslib.h +++ b/engine/qclib/progslib.h @@ -213,7 +213,7 @@ struct pubprogfuncs_s void (PDECL *EntClear) (pubprogfuncs_t *progfuncs, struct edict_s *e); void (PDECL *FindPrefixGlobals) (pubprogfuncs_t *progfuncs, int prnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx); //calls the callback for each named global found - pbool (PDECL *SetWatchPoint) (pubprogfuncs_t *prinst, const char *key); + pbool (PDECL *SetWatchPoint) (pubprogfuncs_t *prinst, const char *desc, const char *location); void (PDECL *AddSharedVar) (pubprogfuncs_t *progfuncs, int start, int size); void (PDECL *AddSharedFieldVar) (pubprogfuncs_t *progfuncs, int num, char *relstringtable); @@ -246,7 +246,7 @@ typedef struct progexterns_s { int (VARGS *Printf) (const char *, ...) LIKEPRINTF(1); int (VARGS *DPrintf) (const char *, ...) LIKEPRINTF(1); void (VARGS *Sys_Error) (const char *, ...) LIKEPRINTF(1); - void (VARGS *Abort) (char *, ...) LIKEPRINTF(1); + void (VARGS *Abort) (const char *, ...) LIKEPRINTF(1); pbool (PDECL *CheckHeaderCrc) (pubprogfuncs_t *inst, progsnum_t idx, int crc, const char *filename); void (PDECL *entspawn) (struct edict_s *ent, int loading); //ent has been spawned, but may not have all the extra variables (that may need to be set) set diff --git a/engine/qclib/test.c b/engine/qclib/test.c index ee0589b39..8ef0848bc 100644 --- a/engine/qclib/test.c +++ b/engine/qclib/test.c @@ -548,7 +548,7 @@ builtin_t builtins[] = { //Called when the qc library has some sort of serious error. -void Sys_Abort(char *s, ...) +void Sys_Abort(const char *s, ...) { //quake handles this with a longjmp. va_list ap; va_start(ap, s); @@ -739,6 +739,7 @@ void runtest(const char *progsname, const char **args) ext.progsversion = PROGSTRUCT_VERSION; ext.ReadFile = Sys_ReadFile; ext.FileSize= Sys_FileSize; + ext.Sys_Error = Sys_Abort; ext.Abort = Sys_Abort; ext.Printf = printf; ext.stateop = StateOp; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 58fe6e72d..3e1e0f732 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1483,7 +1483,7 @@ static void PR_WatchPoint_f(void) pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, svs.clients[i].edict); } } - if (svprogfuncs->SetWatchPoint(svprogfuncs, variable)) + if (svprogfuncs->SetWatchPoint(svprogfuncs, variable, variable)) Con_Printf("Watchpoint set\n"); else Con_Printf("Watchpoint cleared\n"); @@ -11818,7 +11818,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"cprint", PF_Fixme, 0, 0, 0, 338, D("void(string s, ...)", "Print into the center of the screen just as ssqc's centerprint would appear.")},//(EXT_CSQC) {"print", PF_print, 0, 0, 0, 339, D("void(string s, ...)", "Unconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar).")},//(EXT_CSQC) - + {"setwatchpoint", PF_setwatchpoint,0, 0, 0, 0, D("void(string name, float evaltype, void *ptr)", "For debugging. Set a watchpoint to monitor an address for changes. This is equivelent to using the watchpoint_* console command, but done from qc can be used on abitrary addresses.")}, {"keynumtostring", PF_Fixme, 0, 0, 0, 340, D("string(float keynum)", "Returns a hunam-readable name for the given keycode, as a tempstring.")},// (EXT_CSQC) {"keynumtostring_csqc",PF_Fixme,0, 0, 0, 340, D("DEP string(float keynum)", "Returns a hunam-readable name for the given keycode, as a tempstring.")},// (found in menuqc) diff --git a/engine/server/server.h b/engine/server/server.h index ac5ad4083..a74ca0789 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -1157,7 +1157,7 @@ void SV_AutoBanSender (int duration, char *reason); //bans net_from // // sv_main.c // -NORETURN void VARGS SV_Error (char *error, ...) LIKEPRINTF(1); +NORETURN void VARGS SV_Error (const char *error, ...) LIKEPRINTF(1); void SV_Shutdown (void); float SV_Frame (void); void SV_ReadPacket(void); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 0178fef8e..e8febd6c2 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -321,7 +321,7 @@ Sends a datagram to all the clients informing them of the server crash, then exits ================ */ -void VARGS SV_Error (char *error, ...) +void VARGS SV_Error (const char *error, ...) { va_list argptr; static char string[1024];