X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=prvm_edict.c;h=8633fdfedf4cb08ffcd5ec56628bbd67f21bda97;hb=fe52804d325b7688cab684f2b83ba1106702a2aa;hp=22be6c176e2e876cc9eba8f90be8b09ec1df24c7;hpb=668fc41f79b91c7c33baef71384a7c4f88d9a919;p=xonotic%2Fdarkplaces.git diff --git a/prvm_edict.c b/prvm_edict.c index 22be6c17..8633fdfe 100644 --- a/prvm_edict.c +++ b/prvm_edict.c @@ -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); @@ -206,8 +215,8 @@ const char *PRVM_AllocationOrigin(prvm_prog_t *prog) 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; } @@ -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; } @@ -515,7 +521,8 @@ char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, c line[i] = '\0'; break; case ev_entity: - dpsnprintf (line, linelength, "%i", val->edict); + i = val->edict; + dpsnprintf (line, linelength, "%i", i); break; case ev_function: f = prog->functions + val->function; @@ -1316,8 +1323,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; } @@ -1687,7 +1696,7 @@ static void PRVM_PO_ParseString(char *out, const char *in, size_t outsize) } static po_t *PRVM_PO_Load(const char *filename, const char *filename2, mempool_t *pool) { - po_t *po; + po_t *po = NULL; const char *p, *q; int mode; char inbuf[MAX_INPUTLINE]; @@ -1887,7 +1896,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 ); } @@ -1922,6 +1938,8 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da u; 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 ); @@ -2002,6 +2020,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); @@ -2125,6 +2144,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da break; default: Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", (int)op, i, prog->name); + break; // global global global case OP_ADD_F: case OP_ADD_V: @@ -2206,6 +2226,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: @@ -2303,7 +2328,10 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da } else { - po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), "common.po", prog->progs_mempool); + po_t *po = PRVM_PO_Load( + va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), + va(vabuf2, sizeof(vabuf2), "common.%s.po", prvm_language.string), + prog->progs_mempool); if(po) { for (i=0 ; inumglobaldefs ; i++) @@ -2327,6 +2355,9 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da } } + for (cvar = cvar_vars; cvar; cvar = cvar->next) + cvar->globaldefindex[prog - prvm_prog_list] = -1; + for (i=0 ; inumglobaldefs ; i++) { const char *name; @@ -2338,7 +2369,7 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da ) { 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) { @@ -2372,7 +2403,6 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da } 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) @@ -2409,7 +2439,6 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * da 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 @@ -2433,6 +2462,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; } @@ -2889,8 +2922,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); @@ -3067,10 +3102,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; @@ -3083,7 +3120,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; @@ -3187,6 +3228,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? @@ -3237,25 +3280,13 @@ 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) 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); @@ -3285,19 +3316,35 @@ static qboolean PRVM_IsEdictReferenced(prvm_prog_t *prog, prvm_edict_t *edict, i 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;