diff options
author | 2025-04-30 22:55:05 +0100 | |
---|---|---|
committer | 2025-04-30 22:55:05 +0100 | |
commit | 7b7cb45f7c1ed307b585ab3506dc57854ec076a6 (patch) | |
tree | c09faf9f8462fd72ecdfc893e0c87aadc692a436 | |
parent | 45a21380fcc2c9a0b9035814ab0f8cc38165bb64 (diff) | |
download | sst-7b7cb45f7c1ed307b585ab3506dc57854ec076a6.tar.gz sst-7b7cb45f7c1ed307b585ab3506dc57854ec076a6.zip |
Add some useful info printouts to debug builds
Specifically when building in debug mode, we now:
* Display all features on load, including skipped and internal ones,
sorted by internal name instead of display name.
* Print the names of all matched gametype tags after the feature list.
* Add an sst_dbg_getcmdcb command to get the address of a command
callback for quick breakpoint insertion or Ghidra lookup.
* Add an sst_dbg_sendtables command to dump out the full ServerClass
tree to help get names for entprops.txt. Note: this output is very
long so you'll likely need to log console output to a file to be able
to read it all.
There's a bunch of developer experience and debug help stuff I want to
get done eventually. This is just a very small piece, but it's a start.
-rwxr-xr-x | compile | 2 | ||||
-rw-r--r-- | compile.bat | 2 | ||||
-rw-r--r-- | src/build/gluegen.c | 48 | ||||
-rw-r--r-- | src/build/mkentprops.c | 22 | ||||
-rw-r--r-- | src/build/mkgamedata.c | 33 | ||||
-rw-r--r-- | src/dbg.c | 76 | ||||
-rw-r--r-- | src/gametype.h | 81 | ||||
-rw-r--r-- | src/sst.c | 30 |
8 files changed, 242 insertions, 52 deletions
@@ -25,7 +25,7 @@ stdflags="-std=c2x -D_DEFAULT_SOURCE -D_FILE_OFFSET_BITS=64 -D_TIME_BITS=64" dbg=0 if [ "$dbg" = 1 ]; then - cflags="-O0 -g3 -fsanitize-trap=undefined" + cflags="-O0 -g3 -fsanitize-trap=undefined -DSST_DBG" ldflags="-O0 -g3" else cflags="-O2 -fvisibility=hidden" diff --git a/compile.bat b/compile.bat index 0c3bead..bef5436 100644 --- a/compile.bat +++ b/compile.bat @@ -26,7 +26,7 @@ set dbg=0 :: XXX: -Og would be nice but apparently a bunch of stuff still gets inlined
:: which can be somewhat annoying so -O0 it is.
if "%dbg%"=="1" (
- set cflags=-O0 -g3 -fsanitize-trap=undefined
+ set cflags=-O0 -g3 -fsanitize-trap=undefined -DSST_DBG
set ldflags=-O0 -g3
) else (
set cflags=-O2
diff --git a/src/build/gluegen.c b/src/build/gluegen.c index 4abfd2b..5087a52 100644 --- a/src/build/gluegen.c +++ b/src/build/gluegen.c @@ -602,18 +602,44 @@ static cold noreturn diewrite() { die(100, "couldn't write to file"); } _("/* This file is autogenerated by src/build/gluegen.c. DO NOT EDIT! */") #define H() H_() _("") -static void recursefeatdescs(FILE *out, s16 node) { +static void recursedbgmodnames(FILE *out, s16 node) { if (node < 0) { + if (!(mod_flags[-node] & HAS_INIT)) return; +F( " switch (status_%.*s) {", + mod_names[-node].len, mod_names[-node].s) +_( " case FEAT_SKIP: colour = &grey; break;") +_( " case FEAT_OK: colour = &green; break;") +_( " default: colour = &red; break;") +_( " }") if (mod_featdescs[-node].s) { +F( " con_colourmsg(colour, featmsgs[status_%.*s], \"%.*s (\" %.*s \")\");", + mod_names[-node].len, mod_names[-node].s, + mod_names[-node].len, mod_names[-node].s, + mod_featdescs[-node].len, mod_featdescs[-node].s) + } + else { +F( " con_colourmsg(colour, featmsgs[status_%.*s], \"%.*s\");", + mod_names[-node].len, mod_names[-node].s, + mod_names[-node].len, mod_names[-node].s) + } + } + else if (node > 0) { + for (int i = 0; i < 16; ++i) { + recursedbgmodnames(out, radices[node].children[i]); + } + } +} + +static void recursefeatdescs(FILE *out, s16 node) { + if (node < 0) { F( " if (status_%.*s != FEAT_SKIP) {", - mod_names[-node].len, mod_names[-node].s) + mod_names[-node].len, mod_names[-node].s) F( " con_colourmsg(status_%.*s == FEAT_OK ? &green : &red,", - mod_names[-node].len, mod_names[-node].s) + mod_names[-node].len, mod_names[-node].s) F( " featmsgs[status_%.*s], %.*s);", - mod_names[-node].len, mod_names[-node].s, - mod_featdescs[-node].len, mod_featdescs[-node].s) + mod_names[-node].len, mod_names[-node].s, + mod_featdescs[-node].len, mod_featdescs[-node].s) _( " }") - } } else if (node > 0) { for (int i = 0; i < 16; ++i) { @@ -660,7 +686,7 @@ static int evargs_notype(FILE *out, s16 i, const char *suffix) { return j; } -static inline void gencode(FILE *out, s16 featdescs) { +static inline void gencode(FILE *out, s16 modnames, s16 featdescs) { for (int i = 1; i < nmods; ++i) { if (mod_flags[i] & HAS_INIT) { F( "extern int _feat_init_%.*s();", mod_names[i].len, mod_names[i].s) @@ -818,7 +844,13 @@ _( " struct rgba white = {255, 255, 255, 255};") _( " struct rgba green = {128, 255, 128, 255};") _( " struct rgba red = {255, 128, 128, 255};") _( " con_colourmsg(&white, \"---- List of plugin features ---\\n\");"); +_( "#ifdef SST_DBG") +_( " struct rgba grey = {192, 192, 192, 255};") +_( " struct rgba *colour;") + recursedbgmodnames(out, modnames); +_( "#else") recursefeatdescs(out, featdescs); +_( "#endif") _( "}") _( "") _( "static inline void endfeatures() {") @@ -953,7 +985,7 @@ int OS_MAIN(int argc, os_char *argv[]) { FILE *out = fopen(".build/include/glue.gen.h", "wb"); if_cold (!out) die(100, "couldn't open .build/include/glue.gen.h"); H() - gencode(out, featdesclookup); + gencode(out, modlookup, featdesclookup); if_cold (fflush(out)) die(100, "couldn't finish writing output"); return 0; } diff --git a/src/build/mkentprops.c b/src/build/mkentprops.c index bd4b082..17d9aa0 100644 --- a/src/build/mkentprops.c +++ b/src/build/mkentprops.c @@ -368,6 +368,21 @@ _( " }") _( "}") } +static inline void dodbgdump(FILE *out) { +_( "static inline void dumpentprops() {") +_( " con_msg(\"-- entprops.txt --\\n\");") + for (int i = 0; i < ndecls; ++i) { + const char *s = sbase + decls[i]; +F( " if (has_%s) {", s); +F( " con_msg(\" [x] %s = %%d\\n\", %s);", s, s) +_( " }") +_( " else {") +F( " con_msg(\" [ ] %s\\n\");", s) +_( " }") + } +_( "}") +} + int OS_MAIN(int argc, os_char *argv[]) { if_cold (argc != 2) die(1, "wrong number of arguments"); int f = os_open_read(argv[1]); @@ -389,6 +404,13 @@ int OS_MAIN(int argc, os_char *argv[]) { if_cold (!out) die(100, "couldn't open entpropsinit.gen.h"); H(); doinit(out); + + // technically we don't need this header in release builds, but whatever. + out = fopen(".build/include/entpropsdbg.gen.h", "wb"); + if_cold (!out) die(100, "couldn't open entpropsdbg.gen.h"); + H(); + dodbgdump(out); + return 0; } diff --git a/src/build/mkgamedata.c b/src/build/mkgamedata.c index 325cda2..f187028 100644 --- a/src/build/mkgamedata.c +++ b/src/build/mkgamedata.c @@ -265,6 +265,32 @@ _i("}") _( "}") } +static inline void dbgdump(FILE *out) { +_( "static void dumpgamedata() {") + int cursrc = -1; + for (int i = 0; i < nents; ++i) { + if (indents[i] != 0) continue; + if_cold (srcfiles[i] != cursrc) { + cursrc = srcfiles[i]; +F( " con_msg(\"-- %" fS " --\\n\");", srcnames[cursrc]) + } + const char *s = sbase + tags[i]; + int line = srclines[i]; + if (exprs[i]) { +F( " con_msg(\" [x] %s = %%d (line %d)\\n\", %s);", s, line, s) + } + else { +F( " if (has_%s) {", sbase + tags[i]) +F( " con_msg(\" [x] %s = %%d (line %d)\\n\", %s);", s, line, s) +_( " }") +_( " else {") +F( " con_msg(\" [ ] %s (line %d)\\n\");", s, line); +_( " }") + } + } +_( "}") +} + int OS_MAIN(int argc, os_char *argv[]) { srcnames = (const os_char *const *)argv; int sbase_len = 0, sbase_max = 65536; @@ -304,6 +330,13 @@ int OS_MAIN(int argc, os_char *argv[]) { defs(out); _("") init(out); + + // technically we don't need this header in release builds, but whatever. + out = fopen(".build/include/gamedatadbg.gen.h", "wb"); + if_cold (!out) die(100, "couldn't open gamedatadbg.gen.h"); + H(); + dbgdump(out); + return 0; } @@ -18,22 +18,29 @@ #include <Windows.h> #endif +#include "accessor.h" #include "con_.h" #include "engineapi.h" +#include "errmsg.h" +#include "gamedata.h" #include "intdefs.h" #include "ppmagic.h" #include "udis86.h" +#include "vcall.h" + +#include <gamedatadbg.gen.h> +#include <entpropsdbg.gen.h> #ifdef _WIN32 usize dbg_toghidra(const void *addr) { - const void *mod; + const char *mod; if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (ushort *)addr, - (HMODULE *)&mod /* please leave me alone */)) { + (HMODULE *)&mod)) { con_warn("dbg_toghidra: couldn't get base address\n"); return 0; } - return (const char *)addr - (const char *)mod + 0x10000000; + return (const char *)addr - mod + 0x10000000; } #endif @@ -48,8 +55,8 @@ void dbg_hexdump(const char *name, const void *p, int len) { for (const uchar *cp = p; cp - (uchar *)p < len; ++cp) { // group into words and wrap every 8 words switch ((cp - (uchar *)p) & 31) { - case 0: con_msg("\n"); break; - CASES(4, 8, 12, 16, 20, 24, 28): con_msg(" "); + case 0: con_colourmsg(&nice_colour, "\n"); break; + CASES(4, 8, 12, 16, 20, 24, 28): con_colourmsg(&nice_colour, " "); } con_colourmsg(&nice_colour, "%02X ", *cp); } @@ -74,4 +81,63 @@ void dbg_asmdump(const char *name, const void *p, int len) { } } +DEF_CCMD_HERE(sst_dbg_getcmdcb, "Get the address of a command callback", 0) { + if (cmd->argc != 2) { + con_warn("usage: sst_dbg_getcmdcb command\n"); + return; + } + struct con_cmd *thecmd = con_findcmd(cmd->argv[1]); + if (!thecmd) { + errmsg_errorstd("couldn't find command %s\n", cmd->argv[1]); + return; + } +#ifdef _WIN32 + con_msg("addr: %p\nghidra: %p\n", (void *)thecmd->cb, + (void *)dbg_toghidra((void *)thecmd->cb)); // ugh +#else + con_msg("addr: %p\n", (void *)thecmd->cb); +#endif +} + +DECL_VFUNC_DYN(struct IServerGameDLL, struct ServerClass *, GetAllServerClasses) +DEF_ARRAYIDX_ACCESSOR(struct SendProp, SendProp) +DEF_ACCESSORS(struct SendProp, const char *, SP_varname) +DEF_ACCESSORS(struct SendProp, int, SP_type) +DEF_ACCESSORS(struct SendProp, int, SP_offset) +DEF_ACCESSORS(struct SendProp, struct SendTable *, SP_subtable) + +static void dumptable(struct SendTable *st, int indent) { + for (int i = 0; i < st->nprops; ++i) { + for (int i = 0; i < indent; i++) con_msg(" "); + struct SendProp *p = arrayidx_SendProp(st->props, i); + const char *name = get_SP_varname(p); + if (get_SP_type(p) == DPT_DataTable) { + struct SendTable *st = get_SP_subtable(p); + if (!strcmp(name, "baseclass")) { + con_msg("baseclass -> table %s (skipped)\n", st->tablename); + } + else { + con_msg("%s -> subtable %s\n", name, st->tablename); + dumptable(st, indent + 1); + } + } + else { + con_msg("%s -> offset %d\n", name, get_SP_offset(p)); + } + } +} +DEF_CCMD_HERE(sst_dbg_sendtables, "Dump ServerClass/SendTable hierarchy", 0) { + for (struct ServerClass *class = GetAllServerClasses(srvdll); class; + class = class->next) { + struct SendTable *st = class->table; + con_msg("class %s (table %s)\n", class->name, st->tablename); + dumptable(st, 1); + } +} + +DEF_CCMD_HERE(sst_dbg_gamedata, "Dump current gamedata values", 0) { + dumpgamedata(); + dumpentprops(); +} + // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/gametype.h b/src/gametype.h index 51f110d..fa899c2 100644 --- a/src/gametype.h +++ b/src/gametype.h @@ -23,43 +23,54 @@ extern u32 _gametype_tag; -/* general engine branches used in a bunch of stuff */ -#define _gametype_tag_OE 1 -#define _gametype_tag_OrangeBox (1 << 1) -#define _gametype_tag_2013 (1 << 2) +#define GAMETYPE_BASETAGS(X) \ + /* general engine branches used in a bunch of stuff */ \ + X(OE) \ + X(OrangeBox) \ + X(2013) \ +\ + /* specific games with dedicated branches / engine changes */ \ + /* TODO(compat): detect dmomm, if only to fail (VEngineServer broke) */ \ + X(DMoMM) \ + X(L4D1) \ + X(L4D2) \ + X(L4DS) /* Survivors (weird arcade port) */ \ + X(Portal2) \ +\ + /* games needing game-specific stuff, but not tied to a singular branch */ \ + X(Portal1) \ + X(HL2series) /* HL2, episodes, mods */ \ +\ + /* VEngineClient versions */ \ + X(Client015) \ + X(Client014) \ + X(Client013) \ + X(Client012) \ +\ + /* VEngineServer versions */ \ + X(Server021) \ +\ + /* ServerGameDLL versions */ \ + X(SrvDLL009) /* 2013-ish */ \ + X(SrvDLL005) /* mostly everything else, it seems */ \ +\ + /* games needing version-specific stuff */ \ + X(Portal1_3420) \ + X(L4D1_1015plus) /* Crash Course update */ \ + X(L4D1_1022plus) /* Mac update, bunch of code reshuffling */ \ + X(L4D2_2125plus) \ + X(TheLastStand) /* The JAiZ update */ \ -/* specific games with dedicated branches / engine changes */ -// TODO(compat): detect dmomm, even if only just to fail (VEngineServer broke) -// TODO(compat): buy dmomm in a steam sale to implement and test the above, lol -#define _gametype_tag_DMoMM (1 << 3) -#define _gametype_tag_L4D1 (1 << 4) -#define _gametype_tag_L4D2 (1 << 5) -#define _gametype_tag_L4DS (1 << 6) /* Survivors (weird arcade port) */ -#define _gametype_tag_Portal2 (1 << 7) +enum { +#define _GAMETYPE_ENUMBIT(x) _gametype_tagbit_##x, +GAMETYPE_BASETAGS(_GAMETYPE_ENUMBIT) +#undef _GAMETYPE_ENUMBIT +#define _GAMETYPE_ENUMVAL(x) _gametype_tag_##x = 1 << _gametype_tagbit_##x, +GAMETYPE_BASETAGS(_GAMETYPE_ENUMVAL) +#undef _GAMETYPE_ENUMVAL +}; -/* games needing game-specific stuff, but not tied to a singular branch */ -#define _gametype_tag_Portal1 (1 << 8) -#define _gametype_tag_HL2series (1 << 9) /* HL2, episodes, and mods */ - -/* VEngineClient versions */ -#define _gametype_tag_Client015 (1 << 10) -#define _gametype_tag_Client014 (1 << 11) -#define _gametype_tag_Client013 (1 << 12) -#define _gametype_tag_Client012 (1 << 13) -#define _gametype_tag_Server021 (1 << 14) - -/* ServerGameDLL versions */ -#define _gametype_tag_SrvDLL009 (1 << 15) // 2013-ish -#define _gametype_tag_SrvDLL005 (1 << 16) // mostly everything else, it seems - -/* games needing version-specific stuff */ -#define _gametype_tag_Portal1_3420 (1 << 17) -#define _gametype_tag_L4D1_1015plus (1 << 18) // Crash Course update -#define _gametype_tag_L4D1_1022plus (1 << 19) // Mac update, with code shuffling -#define _gametype_tag_L4D2_2125plus (1 << 20) -#define _gametype_tag_TheLastStand (1 << 21) /* The JAiZ update */ - -/* Matches for any multiple possible tags */ +/* Matches for any of multiple possible tags */ #define _gametype_tag_L4D (_gametype_tag_L4D1 | _gametype_tag_L4D2) // XXX: *stupid* naming, refactor one day (damn Survivors ruining everything) #define _gametype_tag_L4D2x (_gametype_tag_L4D2 | _gametype_tag_L4DS) @@ -287,12 +287,24 @@ static const char *updatenotes = "\ * Rewrote and optimised a whole bunch of internal stuff\n\ "; -enum { // used in generated code, must line up with +enum { // used in generated code, must line up with featmsgs arrays below REQFAIL = _FEAT_INTERNAL_STATUSES, NOGD, NOGLOBAL }; -static const char *const featmsgs[] = { // " +#ifdef SST_DBG +static const char *const _featmsgs[] = { + "%s: SKIP\n", + "%s: OK\n", + "%s: FAIL\n", + "%s: INCOMPAT\n", + "%s: REQFAIL\n", + "%s: NOGD\n", + "%s: NOGLOBAL\n" +}; +#define featmsgs (_featmsgs + 1) +#else +static const char *const featmsgs[] = { " [ OK! ] %s\n", " [ FAILED! ] %s (error in initialisation)\n", " [ unsupported ] %s (incompatible with this game or engine)\n", @@ -300,6 +312,7 @@ static const char *const featmsgs[] = { // " " [ unsupported ] %s (missing required gamedata entry)\n", " [ FAILED! ] %s (failed to access engine)\n" }; +#endif static inline void successbanner() { // called by generated code con_colourmsg(&(struct rgba){64, 255, 64, 255}, @@ -336,6 +349,19 @@ static void do_featureinit() { } // ... and now for the real magic! (n.b. this also registers feature cvars) initfeatures(); +#ifdef SST_DBG + struct rgba purple = {192, 128, 240, 255}; + con_colourmsg(&purple, "Matched gametype tags: "); + bool first = true; +#define PRINTTAG(x) \ +if (GAMETYPE_MATCHES(x)) { \ + con_colourmsg(&purple, "%s%s", first ? "" : ", ", #x); \ + first = false; \ +} + GAMETYPE_BASETAGS(PRINTTAG) +#undef PRINTTAG + con_colourmsg(&purple, "\n"); // xkcd 2109-compliant whitespace +#endif // if we're autoloaded and the external autoupdate script downloaded a new // version, let the user know about the cool new stuff! |