prvm_eval_t prvm_badvalue; // used only for error returns
-cvar_t prvm_language = {CVAR_SAVE, "prvm_language", "", "when set, loads progs.dat.LANGUAGENAME.po for string translations; when set to dump, progs.dat.pot is written from the strings in the progs"};
+cvar_t prvm_language = {CVAR_SAVE, "prvm_language", "", "when set, loads PROGSFILE.LANGUAGENAME.po and common.LANGUAGENAME.po for string translations; when set to dump, PROGSFILE.pot is written from the strings in the progs"};
// LordHavoc: prints every opcode as it executes - warning: this is significant spew
cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
// 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_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)"};
return SVVM_prog;
if (!strcmp(str, "client"))
return CLVM_prog;
+#ifdef CONFIG_MENU
if (!strcmp(str, "menu"))
return MVM_prog;
+#endif
return NULL;
}
++in;
}
}
-static po_t *PRVM_PO_Load(const char *filename, mempool_t *pool)
+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];
size_t decodedpos;
int hashindex;
po_string_t thisstr;
- const char *buf = (const char *) FS_LoadFile(filename, pool, true, NULL);
-
- if(!buf)
- return NULL;
-
- memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
-
- po = (po_t *)Mem_Alloc(pool, sizeof(*po));
- memset(po, 0, sizeof(*po));
+ int i;
- p = buf;
- while(*p)
+ for (i = 0; i < 2; ++i)
{
- if(*p == '#')
- {
- // skip to newline
- p = strchr(p, '\n');
- if(!p)
- break;
- ++p;
- continue;
- }
- if(*p == '\r' || *p == '\n')
- {
- ++p;
+ const char *buf = (const char *)
+ FS_LoadFile((i > 0 ? filename : filename2), pool, true, NULL);
+ // first read filename2, then read filename
+ // so that progs.dat.de.po wins over common.de.po
+ // and within file, last item wins
+
+ if(!buf)
continue;
- }
- if(!strncmp(p, "msgid \"", 7))
- {
- mode = 0;
- p += 6;
- }
- else if(!strncmp(p, "msgstr \"", 8))
+
+ if (!po)
{
- mode = 1;
- p += 7;
+ po = (po_t *)Mem_Alloc(pool, sizeof(*po));
+ memset(po, 0, sizeof(*po));
}
- else
+
+ memset(&thisstr, 0, sizeof(thisstr)); // hush compiler warning
+
+ p = buf;
+ while(*p)
{
- p = strchr(p, '\n');
- if(!p)
- break;
- ++p;
- continue;
+ if(*p == '#')
+ {
+ // skip to newline
+ p = strchr(p, '\n');
+ if(!p)
+ break;
+ ++p;
+ continue;
+ }
+ if(*p == '\r' || *p == '\n')
+ {
+ ++p;
+ continue;
+ }
+ if(!strncmp(p, "msgid \"", 7))
+ {
+ mode = 0;
+ p += 6;
+ }
+ else if(!strncmp(p, "msgstr \"", 8))
+ {
+ mode = 1;
+ p += 7;
+ }
+ else
+ {
+ p = strchr(p, '\n');
+ if(!p)
+ break;
+ ++p;
+ continue;
+ }
+ decodedpos = 0;
+ while(*p == '"')
+ {
+ ++p;
+ q = strchr(p, '\n');
+ if(!q)
+ break;
+ if(*(q-1) == '\r')
+ --q;
+ if(*(q-1) != '"')
+ break;
+ if((size_t)(q - p) >= (size_t) sizeof(inbuf))
+ break;
+ strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
+ PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
+ decodedpos += strlen(decodedbuf + decodedpos);
+ if(*q == '\r')
+ ++q;
+ if(*q == '\n')
+ ++q;
+ p = q;
+ }
+ if(mode == 0)
+ {
+ if(thisstr.key)
+ Mem_Free(thisstr.key);
+ thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
+ memcpy(thisstr.key, decodedbuf, decodedpos + 1);
+ }
+ else if(decodedpos > 0 && thisstr.key) // skip empty translation results
+ {
+ thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
+ memcpy(thisstr.value, decodedbuf, decodedpos + 1);
+ hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
+ thisstr.nextonhashchain = po->hashtable[hashindex];
+ po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
+ memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
+ memset(&thisstr, 0, sizeof(thisstr));
+ }
}
- decodedpos = 0;
- while(*p == '"')
- {
- ++p;
- q = strchr(p, '\n');
- if(!q)
- break;
- if(*(q-1) == '\r')
- --q;
- if(*(q-1) != '"')
- break;
- if((size_t)(q - p) >= (size_t) sizeof(inbuf))
- break;
- strlcpy(inbuf, p, q - p); // not - 1, because this adds a NUL
- PRVM_PO_ParseString(decodedbuf + decodedpos, inbuf, sizeof(decodedbuf) - decodedpos);
- decodedpos += strlen(decodedbuf + decodedpos);
- if(*q == '\r')
- ++q;
- if(*q == '\n')
- ++q;
- p = q;
- }
- if(mode == 0)
- {
- if(thisstr.key)
- Mem_Free(thisstr.key);
- thisstr.key = (char *)Mem_Alloc(pool, decodedpos + 1);
- memcpy(thisstr.key, decodedbuf, decodedpos + 1);
- }
- else if(decodedpos > 0 && thisstr.key) // skip empty translation results
- {
- thisstr.value = (char *)Mem_Alloc(pool, decodedpos + 1);
- memcpy(thisstr.value, decodedbuf, decodedpos + 1);
- hashindex = CRC_Block((const unsigned char *) thisstr.key, strlen(thisstr.key)) % PO_HASHSIZE;
- thisstr.nextonhashchain = po->hashtable[hashindex];
- po->hashtable[hashindex] = (po_string_t *)Mem_Alloc(pool, sizeof(thisstr));
- memcpy(po->hashtable[hashindex], &thisstr, sizeof(thisstr));
- memset(&thisstr, 0, sizeof(thisstr));
- }
- }
-
- Mem_Free((char *) buf);
+
+ Mem_Free((char *) buf);
+ }
+
return po;
}
static const char *PRVM_PO_Lookup(po_t *po, const char *str)
}
memset(prog,0,sizeof(prvm_prog_t));
prog->break_statement = -1;
- prog->watch_global = -1;
- prog->watch_edict = -1;
+ prog->watch_global_type = ev_void;
+ prog->watch_field_type = ev_void;
}
/*
(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 );
}
u;
unsigned int d;
char vabuf[1024];
+ char vabuf2[1024];
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;
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:
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:
}
else
{
- po_t *po = PRVM_PO_Load(va(vabuf, sizeof(vabuf), "%s.%s.po", realfilename, prvm_language.string), 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 ; i<prog->numglobaldefs ; i++)
Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name));
}
+void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n)
+{
+ size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
+ if (memcmp(o, n, sz))
+ {
+ char buf[1024];
+ char valuebuf_o[128];
+ char valuebuf_n[128];
+ PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o));
+ PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n));
+ dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n);
+ PRVM_Breakpoint(prog, stack_index, buf);
+ memcpy(o, n, sz);
+ }
+}
+
static void PRVM_UpdateBreakpoints(prvm_prog_t *prog)
{
debug_data_t *debug = &debug_data[prog - prvm_prog_list];
if( !global )
{
Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global );
- prog->watch_global = -1;
+ prog->watch_global_type = ev_void;
}
else
{
+ size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
prog->watch_global = global->ofs;
- prog->watch_global_value = PRVM_GLOBALFIELDFLOAT(prog->watch_global);
+ prog->watch_global_type = (etype_t)global->type;
+ memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz);
}
- if (prog->watch_global >= -1)
+ if (prog->watch_global_type != ev_void)
Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global);
}
else
- prog->watch_global = -1;
+ prog->watch_global_type = ev_void;
if (debug->watch_field[0])
{
if( !field )
{
Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field );
- prog->watch_edict = -1;
+ prog->watch_field_type = ev_void;
}
else
{
+ size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1);
prog->watch_edict = debug->watch_edict;
prog->watch_field = field->ofs;
+ prog->watch_field_type = (etype_t)field->type;
if (prog->watch_edict < prog->num_edicts)
- prog->watch_edictfield_value = PRVM_EDICTFIELDFLOAT(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field);
+ memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz);
else
- prog->watch_edictfield_value = 0;
+ memset(&prog->watch_edictfield_value, 0, sz);
}
- if (prog->watch_edict >= -1)
+ if (prog->watch_edict != ev_void)
Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field);
}
else
- prog->watch_edict = -1;
+ prog->watch_field_type = ev_void;
}
static void PRVM_Breakpoint_f(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_ignore_classnames);