]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - prvm_edict.c
Leaktest: disable support for following target/targetname by default.
[xonotic/darkplaces.git] / prvm_edict.c
index 12781fdc10da743f51814dc2c34e07d371c45776..7dcf6daa39b6045760c2a311b153a30438ea920e 100644 (file)
@@ -35,8 +35,10 @@ cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as
 // LordHavoc: counts usage of each QuakeC statement
 cvar_t prvm_statementprofiling = {0, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
 cvar_t prvm_timeprofiling = {0, "prvm_timeprofiling", "0", "counts how long each function has been executed, these counts are displayed in prvm_profile output (if enabled)"};
+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"};
@@ -188,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);
@@ -261,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;
                }
        }
@@ -274,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;
 }
 
@@ -1316,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;
 }
@@ -1887,7 +1895,14 @@ static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) {
                (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs_numstatements )
        {
                prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
-               memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs_numstatements * sizeof( int ) );
+               memcpy( prog->statement_linenums, header + 6, prog->progs_numstatements * sizeof( int ) );
+
+               /* gmqcc suports columnums */
+               if ((unsigned int)filesize > ((6 + 2 * prog->progs_numstatements) * sizeof( int )))
+               {
+                       prog->statement_columnnums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof( int ) );
+                       memcpy( prog->statement_columnnums, header + 6 + prog->progs_numstatements, prog->progs_numstatements * sizeof( int ) );
+               }
        }
        Mem_Free( lno );
 }
@@ -2003,6 +2018,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
        prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t));
        // allocate space for profiling statement usage
        prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
+       prog->explicit_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(*prog->statement_profile));
        // functions need to be converted to the memory format
        prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs_numfunctions);
 
@@ -2208,6 +2224,11 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da
                        break;
                // 1 global
                case OP_CALL0:
+                       if ( a < prog->progs_numglobals)
+                               if ( prog->globals.ip[remapglobal(a)] >= 0 )
+                                       if ( prog->globals.ip[remapglobal(a)] < prog->progs_numfunctions )
+                                               if ( prog->functions[prog->globals.ip[remapglobal(a)]].first_statement == -642 )
+                                                       ++prog->numexplicitcoveragestatements;
                case OP_CALL1:
                case OP_CALL2:
                case OP_CALL3:
@@ -2438,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;
 }
 
 
@@ -2894,8 +2919,10 @@ void PRVM_Init (void)
        Cvar_RegisterVariable (&prvm_traceqc);
        Cvar_RegisterVariable (&prvm_statementprofiling);
        Cvar_RegisterVariable (&prvm_timeprofiling);
+       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);
@@ -3072,10 +3099,12 @@ int PRVM_SetTempString(prvm_prog_t *prog, const char *s)
                {
                        Con_DPrintf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, prog->tempstringsbuf.maxsize/1024);
                        prog->tempstringsbuf.data = (unsigned char *) Mem_Alloc(prog->progs_mempool, prog->tempstringsbuf.maxsize);
-                       if (old.cursize)
-                               memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
                        if (old.data)
+                       {
+                               if (old.cursize)
+                                       memcpy(prog->tempstringsbuf.data, old.data, old.cursize);
                                Mem_Free(old.data);
+                       }
                }
        }
        t = (char *)prog->tempstringsbuf.data + prog->tempstringsbuf.cursize;
@@ -3088,7 +3117,11 @@ int PRVM_AllocString(prvm_prog_t *prog, size_t bufferlength, char **pointer)
 {
        int i;
        if (!bufferlength)
+       {
+               if (pointer)
+                       *pointer = NULL;
                return 0;
+       }
        for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
                if (!prog->knownstrings[i])
                        break;
@@ -3192,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?
@@ -3242,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)