summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Michael Smith <mikesmiffy128@gmail.com> 2025-04-30 22:55:05 +0100
committerGravatar Michael Smith <mikesmiffy128@gmail.com> 2025-04-30 22:55:05 +0100
commit7b7cb45f7c1ed307b585ab3506dc57854ec076a6 (patch)
treec09faf9f8462fd72ecdfc893e0c87aadc692a436
parent45a21380fcc2c9a0b9035814ab0f8cc38165bb64 (diff)
downloadsst-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-xcompile2
-rw-r--r--compile.bat2
-rw-r--r--src/build/gluegen.c48
-rw-r--r--src/build/mkentprops.c22
-rw-r--r--src/build/mkgamedata.c33
-rw-r--r--src/dbg.c76
-rw-r--r--src/gametype.h81
-rw-r--r--src/sst.c30
8 files changed, 242 insertions, 52 deletions
diff --git a/compile b/compile
index 1e9812d..96f7be6 100755
--- a/compile
+++ b/compile
@@ -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;
}
diff --git a/src/dbg.c b/src/dbg.c
index fa85ee8..17e88a0 100644
--- a/src/dbg.c
+++ b/src/dbg.c
@@ -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)
diff --git a/src/sst.c b/src/sst.c
index e02f196..a6e4d2d 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -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!