From d7bc164563cf037b23be8b97b07575b2c79ff5d9 Mon Sep 17 00:00:00 2001 From: divverent Date: Tue, 24 Nov 2015 14:57:58 +0000 Subject: [PATCH] Leaktest: disable support for following target/targetname by default. Instead, mark any entities created during initialization as non-leaky. This should solve everything that the targetname support was meant to solve (namely info_notnull entities), but is more general (e.g. would also work with Xonotic's killtarget/target2/target3/target4 and target0/name of Doom 3). git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12233 d7cf8633-e32d-0410-b094-e92efae38249 ::stable-branch::merge=f85803e7bc2d0612763bdb8548c7060ddb542afb --- csprogs.c | 3 +++ menu.c | 3 +++ progsvm.h | 3 ++- prvm_edict.c | 28 +++++++++++++++++++++------- sv_main.c | 3 +++ 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/csprogs.c b/csprogs.c index b1598dda..fa364ce7 100644 --- a/csprogs.c +++ b/csprogs.c @@ -1145,6 +1145,9 @@ void CL_VM_Init (void) // call the prog init prog->ExecuteProgram(prog, PRVM_clientfunction(CSQC_Init), "QC function CSQC_Init is missing"); + // Once CSQC_Init was called, we consider csqc code fully initialized. + prog->inittime = realtime; + cl.csqc_loaded = true; cl.csqc_vidvars.drawcrosshair = false; diff --git a/menu.c b/menu.c index d3e2b8db..fc76081c 100644 --- a/menu.c +++ b/menu.c @@ -5407,6 +5407,9 @@ static void MP_Init (void) // call the prog init prog->ExecuteProgram(prog, PRVM_menufunction(m_init),"m_init() required"); + + // Once m_init was called, we consider menuqc code fully initialized. + prog->inittime = realtime; } //============================================================================ diff --git a/progsvm.h b/progsvm.h index 02cb4bea..8483fae9 100644 --- a/progsvm.h +++ b/progsvm.h @@ -70,7 +70,7 @@ typedef struct prvm_required_field_s typedef struct prvm_edict_private_s { qboolean free; - float freetime; + float freetime; // realtime of last change to "free" (i.e. also set on allocation) int mark; // used during leaktest (0 = unref, >0 = referenced); special values during server physics: #define PRVM_EDICT_MARK_WAIT_FOR_SETORIGIN -1 #define PRVM_EDICT_MARK_SETORIGIN_CAUGHT -2 @@ -531,6 +531,7 @@ prvm_stringbuffer_t; typedef struct prvm_prog_s { double starttime; // system time when PRVM_Prog_Load was called + double inittime; // system time when QC initialization code finished (any entity created before is not a leak) double profiletime; // system time when last PRVM_CallProfile was called (or PRVM_Prog_Load initially) unsigned int id; // increasing unique id of progs instance mfunction_t *functions; diff --git a/prvm_edict.c b/prvm_edict.c index a002fd43..7dcf6daa 100644 --- a/prvm_edict.c +++ b/prvm_edict.c @@ -38,6 +38,7 @@ cvar_t prvm_timeprofiling = {0, "prvm_timeprofiling", "0", "counts how long each cvar_t prvm_coverage = {0, "prvm_coverage", "0", "report and count coverage events (1: per-function, 2: coverage() builtin, 4: per-statement)"}; cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"}; cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"}; +cvar_t prvm_leaktest_follow_targetname = {0, "prvm_leaktest_follow_targetname", "0", "if set, target/targetname links are considered when leak testing; this should normally not be required, as entities created during startup - e.g. info_notnull - are never considered leaky"}; cvar_t prvm_leaktest_ignore_classnames = {0, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"}; cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"}; cvar_t prvm_breakpointdump = {0, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"}; @@ -189,13 +190,20 @@ prvm_prog_t *PRVM_FriendlyProgFromString(const char *str) ================= PRVM_ED_ClearEdict -Sets everything to NULL +Sets everything to NULL. + +Nota bene: this also marks the entity as allocated if it has been previously +freed and sets the allocation origin. ================= */ void PRVM_ED_ClearEdict(prvm_prog_t *prog, prvm_edict_t *e) { memset(e->fields.fp, 0, prog->entityfields * sizeof(prvm_vec_t)); e->priv.required->free = false; + e->priv.required->freetime = realtime; + if(e->priv.required->allocation_origin) + Mem_Free((char *)e->priv.required->allocation_origin); + e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog); // AK: Let the init_edict function determine if something needs to be initialized prog->init_edict(prog, e); @@ -262,7 +270,6 @@ prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog) if(PRVM_ED_CanAlloc(prog, e)) { PRVM_ED_ClearEdict (prog, e); - e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog); return e; } } @@ -275,10 +282,8 @@ prvm_edict_t *PRVM_ED_Alloc(prvm_prog_t *prog) PRVM_MEM_IncreaseEdicts(prog); e = PRVM_EDICT_NUM(i); - PRVM_ED_ClearEdict(prog, e); - - e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog); + PRVM_ED_ClearEdict(prog, e); return e; } @@ -1317,8 +1322,10 @@ const char *PRVM_ED_ParseEdict (prvm_prog_t *prog, const char *data, prvm_edict_ prog->error_cmd("PRVM_ED_ParseEdict: parse error"); } - if (!init) + if (!init) { ent->priv.required->free = true; + ent->priv.required->freetime = realtime; + } return data; } @@ -2452,6 +2459,10 @@ fail: // init mempools PRVM_MEM_Alloc(prog); + + // Inittime is at least the time when this function finished. However, + // later events may bump it. + prog->inittime = realtime; } @@ -2911,6 +2922,7 @@ void PRVM_Init (void) Cvar_RegisterVariable (&prvm_coverage); Cvar_RegisterVariable (&prvm_backtraceforwarnings); Cvar_RegisterVariable (&prvm_leaktest); + Cvar_RegisterVariable (&prvm_leaktest_follow_targetname); Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames); Cvar_RegisterVariable (&prvm_errordump); Cvar_RegisterVariable (&prvm_breakpointdump); @@ -3213,6 +3225,8 @@ static qboolean PRVM_IsEdictRelevant(prvm_prog_t *prog, prvm_edict_t *edict) char vabuf2[1024]; if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts) return true; // world or clients + if (edict->priv.required->freetime <= prog->inittime) + return true; // created during startup if (prog == SVVM_prog) { if(PRVM_serveredictfloat(edict, solid)) // can block other stuff, or is a trigger? @@ -3263,7 +3277,7 @@ static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, i int edictnum = PRVM_NUM_FOR_EDICT(edict); const char *targetname = NULL; - if (prog == SVVM_prog) + if (prog == SVVM_prog && prvm_leaktest_follow_targetname.integer) targetname = PRVM_GetString(prog, PRVM_serveredictstring(edict, targetname)); if(targetname) diff --git a/sv_main.c b/sv_main.c index b6e3b3f1..d0738905 100644 --- a/sv_main.c +++ b/sv_main.c @@ -3500,6 +3500,9 @@ void SV_SpawnServer (const char *server) SV_Physics (); } + // Once all init frames have been run, we consider svqc code fully initialized. + prog->inittime = realtime; + if (cls.state == ca_dedicated) Mod_PurgeUnused(); -- 2.39.2