From d7bc164563cf037b23be8b97b07575b2c79ff5d9 Mon Sep 17 00:00:00 2001
From: divverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
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.5