// 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"};
=================
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);
if(prog->leaktest_active)
if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
{
- buf = (char *)PRVM_Alloc(128);
- PRVM_ShortStackTrace(prog, buf, 128);
+ buf = (char *)PRVM_Alloc(256);
+ PRVM_ShortStackTrace(prog, buf, 256);
}
return buf;
}
if(PRVM_ED_CanAlloc(prog, e))
{
PRVM_ED_ClearEdict (prog, e);
- e->priv.required->allocation_origin = PRVM_AllocationOrigin(prog);
return e;
}
}
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;
}
prog->error_cmd("PRVM_ED_ParseEdict: parse error");
}
- if (!init)
+ if (!init) {
ent->priv.required->free = true;
+ ent->priv.required->freetime = realtime;
+ }
return data;
}
unsigned int d;
char vabuf[1024];
char vabuf2[1024];
+ cvar_t *cvar;
if (prog->loaded)
prog->error_cmd("PRVM_LoadProgs: there is already a %s program loaded!", prog->name );
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);
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:
}
}
+ for (cvar = cvar_vars; cvar; cvar = cvar->next)
+ cvar->globaldefindex[prog - prvm_prog_list] = -1;
+
for (i=0 ; i<prog->numglobaldefs ; i++)
{
const char *name;
)
{
prvm_eval_t *val = PRVM_GLOBALFIELDVALUE(prog->globaldefs[i].ofs);
- cvar_t *cvar = Cvar_FindVar(name + 9);
+ cvar = Cvar_FindVar(name + 9);
//Con_Printf("PRVM_LoadProgs: autocvar global %s in %s, processing...\n", name, prog->name);
if(!cvar)
{
}
if(!cvar)
prog->error_cmd("PRVM_LoadProgs: could not create cvar for autocvar global %s in %s", name, prog->name);
- cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
cvar->globaldefindex[prog - prvm_prog_list] = i;
}
else if((cvar->flags & CVAR_PRIVATE) == 0)
Con_Printf("PRVM_LoadProgs: invalid type of autocvar global %s in %s\n", name, prog->name);
goto fail;
}
- cvar->globaldefindex_progid[prog - prvm_prog_list] = prog->id;
cvar->globaldefindex[prog - prvm_prog_list] = i;
}
else
// 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;
}
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);
{
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;
{
int i;
if (!bufferlength)
+ {
+ if (pointer)
+ *pointer = NULL;
return 0;
+ }
for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
if (!prog->knownstrings[i])
break;
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?
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)
if(!*targetname) // ""
targetname = NULL;
- if(mark == 0)
- {
- for (i = 0;i < prog->numglobaldefs;i++)
- {
- ddef_t *d = &prog->globaldefs[i];
- if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
- continue;
- if(edictnum == PRVM_GLOBALFIELDEDICT(d->ofs))
- return true;
- }
- }
-
for(j = 0; j < prog->num_edicts; ++j)
{
prvm_edict_t *ed = PRVM_EDICT_NUM(j);
static void PRVM_MarkReferencedEdicts(prvm_prog_t *prog)
{
- int j;
+ int i, j;
qboolean found_new;
int stage;
+ // Stage 1: world, all entities that are relevant, and all entities that are referenced by globals.
+ stage = 1;
for(j = 0; j < prog->num_edicts; ++j)
{
prvm_edict_t *ed = PRVM_EDICT_NUM(j);
if(ed->priv.required->free)
continue;
- ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? 1 : 0;
+ ed->priv.required->mark = PRVM_IsEdictRelevant(prog, ed) ? stage : 0;
+ }
+ for (i = 0;i < prog->numglobaldefs;i++)
+ {
+ ddef_t *d = &prog->globaldefs[i];
+ prvm_edict_t *ed;
+ if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
+ continue;
+ j = PRVM_GLOBALFIELDEDICT(d->ofs);
+ if (i < 0 || j >= prog->max_edicts) {
+ Con_Printf("Invalid entity reference from global %s.\n", PRVM_GetString(prog, d->s_name));
+ continue;
+ }
+ ed = PRVM_EDICT_NUM(j);;
+ ed->priv.required->mark = stage;
}
- stage = 1;
+ // Future stages: all entities that are referenced by an entity of the previous stage.
do
{
found_new = false;