X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=prvm_exec.c;h=55a91d92d9438d148fe6312b951aa5181bfd7a86;hp=ab166f35bbe3b733864e290501a01a0f4b3b64bf;hb=a854130c17f1490cf5fb25650994c12d8c9ad0a7;hpb=56561de0acdb9cf29d155992440ac3e0d32909dd diff --git a/prvm_exec.c b/prvm_exec.c index ab166f35..55a91d92 100644 --- a/prvm_exec.c +++ b/prvm_exec.c @@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. char *prvm_opnames[] = { -"DONE", +"^5DONE", "MUL_F", "MUL_V", @@ -38,31 +38,31 @@ char *prvm_opnames[] = "SUB_F", "SUB_V", -"EQ_F", -"EQ_V", -"EQ_S", -"EQ_E", -"EQ_FNC", +"^2EQ_F", +"^2EQ_V", +"^2EQ_S", +"^2EQ_E", +"^2EQ_FNC", -"NE_F", -"NE_V", -"NE_S", -"NE_E", -"NE_FNC", +"^2NE_F", +"^2NE_V", +"^2NE_S", +"^2NE_E", +"^2NE_FNC", -"LE", -"GE", -"LT", -"GT", +"^2LE", +"^2GE", +"^2LT", +"^2GT", -"INDIRECT", -"INDIRECT", -"INDIRECT", -"INDIRECT", -"INDIRECT", -"INDIRECT", +"^6FIELD_F", +"^6FIELD_V", +"^6FIELD_S", +"^6FIELD_ENT", +"^6FIELD_FLD", +"^6FIELD_FNC", -"ADDRESS", +"^1ADDRESS", "STORE_F", "STORE_V", @@ -71,40 +71,40 @@ char *prvm_opnames[] = "STORE_FLD", "STORE_FNC", -"STOREP_F", -"STOREP_V", -"STOREP_S", -"STOREP_ENT", -"STOREP_FLD", -"STOREP_FNC", +"^1STOREP_F", +"^1STOREP_V", +"^1STOREP_S", +"^1STOREP_ENT", +"^1STOREP_FLD", +"^1STOREP_FNC", -"RETURN", +"^5RETURN", -"NOT_F", -"NOT_V", -"NOT_S", -"NOT_ENT", -"NOT_FNC", +"^2NOT_F", +"^2NOT_V", +"^2NOT_S", +"^2NOT_ENT", +"^2NOT_FNC", -"IF", -"IFNOT", +"^5IF", +"^5IFNOT", -"CALL0", -"CALL1", -"CALL2", -"CALL3", -"CALL4", -"CALL5", -"CALL6", -"CALL7", -"CALL8", +"^3CALL0", +"^3CALL1", +"^3CALL2", +"^3CALL3", +"^3CALL4", +"^3CALL5", +"^3CALL6", +"^3CALL7", +"^3CALL8", -"STATE", +"^1STATE", -"GOTO", +"^5GOTO", -"AND", -"OR", +"^2AND", +"^2OR", "BITAND", "BITOR" @@ -121,41 +121,127 @@ char *PRVM_GlobalStringNoContents (int ofs); PRVM_PrintStatement ================= */ +extern cvar_t prvm_statementprofiling; void PRVM_PrintStatement (dstatement_t *s) { - int i; + size_t i; + int opnum = (int)(s - prog->statements); + + Con_Printf("s%i: ", opnum); + if( prog->statement_linenums ) + Con_Printf( "%s:%i: ", PRVM_GetString( prog->xfunction->s_file ), prog->statement_linenums[ opnum ] ); + + if (prvm_statementprofiling.integer) + Con_Printf("%7.0f ", prog->statement_profile[s - prog->statements]); if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0])) { Con_Printf("%s ", prvm_opnames[s->op]); i = strlen(prvm_opnames[s->op]); + // don't count a preceding color tag when padding the name + if (prvm_opnames[s->op][0] == STRING_COLOR_TAG) + i -= 2; for ( ; i<10 ; i++) Con_Print(" "); } - if (s->op == OP_IF || s->op == OP_IFNOT) - Con_Printf("%sbranch %i",PRVM_GlobalString((unsigned short) s->a),s->b); + Con_Printf("%s, s%i",PRVM_GlobalString((unsigned short) s->a),(signed short)s->b + opnum); else if (s->op == OP_GOTO) - { - Con_Printf("branch %i",s->a); - } + Con_Printf("s%i",(signed short)s->a + opnum); else if ( (unsigned)(s->op - OP_STORE_F) < 6) { Con_Print(PRVM_GlobalString((unsigned short) s->a)); + Con_Print(", "); Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->b)); } + else if (s->op == OP_ADDRESS || (unsigned)(s->op - OP_LOAD_F) < 6) + { + if (s->a) + Con_Print(PRVM_GlobalString((unsigned short) s->a)); + if (s->b) + { + Con_Print(", "); + Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->b)); + } + if (s->c) + { + Con_Print(", "); + Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->c)); + } + } else { if (s->a) Con_Print(PRVM_GlobalString((unsigned short) s->a)); if (s->b) + { + Con_Print(", "); Con_Print(PRVM_GlobalString((unsigned short) s->b)); + } if (s->c) + { + Con_Print(", "); Con_Print(PRVM_GlobalStringNoContents((unsigned short) s->c)); + } } Con_Print("\n"); } +void PRVM_PrintFunctionStatements (const char *name) +{ + int i, firststatement, endstatement; + mfunction_t *func; + func = PRVM_ED_FindFunction (name); + if (!func) + { + Con_Printf("%s progs: no function named %s\n", PRVM_NAME, name); + return; + } + firststatement = func->first_statement; + if (firststatement < 0) + { + Con_Printf("%s progs: function %s is builtin #%i\n", PRVM_NAME, name, -firststatement); + return; + } + + // find the end statement + endstatement = prog->progs->numstatements; + for (i = 0;i < prog->progs->numfunctions;i++) + if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement) + endstatement = prog->functions[i].first_statement; + + // now print the range of statements + Con_Printf("%s progs: disassembly of function %s (statements %i-%i):\n", PRVM_NAME, name, firststatement, endstatement); + for (i = firststatement;i < endstatement;i++) + { + PRVM_PrintStatement(prog->statements + i); + prog->statement_profile[i] = 0; + } +} + +/* +============ +PRVM_PrintFunction_f + +============ +*/ +void PRVM_PrintFunction_f (void) +{ + if (Cmd_Argc() != 3) + { + Con_Printf("usage: prvm_printfunction \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + PRVM_PrintFunctionStatements(Cmd_Argv(2)); + + PRVM_End; +} + /* ============ PRVM_StackTrace @@ -179,32 +265,82 @@ void PRVM_StackTrace (void) } } +void PRVM_ShortStackTrace(char *buf, size_t bufsize) +{ + mfunction_t *f; + int i; -/* -============ -PRVM_Profile_f + if(prog) + { + dpsnprintf(buf, bufsize, "(%s) ", prog->name); + } + else + { + strlcpy(buf, "", bufsize); + return; + } -============ -*/ -void PRVM_Profile_f (void) + prog->stack[prog->depth].s = prog->xstatement; + prog->stack[prog->depth].f = prog->xfunction; + for (i = prog->depth;i > 0;i--) + { + f = prog->stack[i].f; + + if(strlcat(buf, + f + ? va("%s:%s(%i) ", PRVM_GetString(f->s_file), PRVM_GetString(f->s_name), prog->stack[i].s - f->first_statement) + : " ", + bufsize + ) >= bufsize) + break; + } +} + + +void PRVM_CallProfile () { mfunction_t *f, *best; - int i, num, max/*, howmany*/; + int i; + double max; + double sum; + + Con_Printf( "%s Call Profile:\n", PRVM_NAME ); - //howmany = 10; - //if (Cmd_Argc() == 2) - // howmany = atoi(Cmd_Argv(1)); - if(Cmd_Argc() != 2) + sum = 0; + do { - Con_Print("prvm_profile \n"); - return; - } - - PRVM_Begin; - if(!PRVM_SetProgFromString(Cmd_Argv(1))) - return; + max = 0; + best = NULL; + for (i=0 ; iprogs->numfunctions ; i++) + { + f = &prog->functions[i]; + if (max < f->totaltime) + { + max = f->totaltime; + best = f; + } + } + if (best) + { + sum += best->totaltime; + Con_Printf("%9.4f %s\n", best->totaltime, PRVM_GetString(best->s_name)); + best->totaltime = 0; + } + } while (best); - Con_Printf( "%s Profile:\n[Profile] [BuiltinProfile] [CallCount]\n", PRVM_NAME ); + Con_Printf("Total time since last profile reset: %9.4f\n", Sys_DoubleTime() - prog->starttime); + Con_Printf(" - used by QC code of this VM: %9.4f\n", sum); + + prog->starttime = Sys_DoubleTime(); +} + +void PRVM_Profile (int maxfunctions, int mininstructions) +{ + mfunction_t *f, *best; + int i, num; + double max; + + Con_Printf( "%s Profile:\n[CallCount] [Statements] [BuiltinCost]\n", PRVM_NAME ); num = 0; do @@ -214,21 +350,76 @@ void PRVM_Profile_f (void) for (i=0 ; iprogs->numfunctions ; i++) { f = &prog->functions[i]; - if (f->profile > max) + if (max < f->profile + f->builtinsprofile + f->callcount) { - max = f->profile; + max = f->profile + f->builtinsprofile + f->callcount; best = f; } } if (best) { - //if (num < howmany) - Con_Printf("%7i %7i %7i %s\n", best->profile, best->builtinsprofile, best->callcount, PRVM_GetString(best->s_name)); + if (num < maxfunctions && max >= mininstructions) + { + if (best->first_statement < 0) + Con_Printf("%9.0f ----- builtin ----- %s\n", best->callcount, PRVM_GetString(best->s_name)); + else + Con_Printf("%9.0f %9.0f %9.0f %s\n", best->callcount, best->profile, best->builtinsprofile, PRVM_GetString(best->s_name)); + } num++; best->profile = 0; best->builtinsprofile = 0; + best->callcount = 0; } } while (best); +} + +/* +============ +PRVM_CallProfile_f + +============ +*/ +void PRVM_CallProfile_f (void) +{ + if (Cmd_Argc() != 2) + { + Con_Print("prvm_callprofile \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + PRVM_CallProfile(); + + PRVM_End; +} + +/* +============ +PRVM_Profile_f + +============ +*/ +void PRVM_Profile_f (void) +{ + int howmany; + + howmany = 1<<30; + if (Cmd_Argc() == 3) + howmany = atoi(Cmd_Argv(2)); + else if (Cmd_Argc() != 2) + { + Con_Print("prvm_profile \n"); + return; + } + + PRVM_Begin; + if(!PRVM_SetProgFromString(Cmd_Argv(1))) + return; + + PRVM_Profile(howmany, 1); PRVM_End; } @@ -245,13 +436,17 @@ void PRVM_CrashAll() PRVM_SetProg(i); PRVM_Crash(); } - + prog = oldprog; } void PRVM_PrintState(void) { int i; + if(prog->statestring) + { + Con_Printf("Caller-provided information: %s\n", prog->statestring); + } if (prog->xfunction) { for (i = -7; i <= 0;i++) @@ -263,18 +458,35 @@ void PRVM_PrintState(void) PRVM_StackTrace (); } -void PRVM_Crash() +extern sizebuf_t vm_tempstringsbuf; +extern cvar_t prvm_errordump; +void Host_Savegame_to (const char *name); +void PRVM_Crash(void) { + if (prog == NULL) + return; + + prog->funcoffsets.SV_Shutdown = 0; // don't call SV_Shutdown on crash + if( prog->depth > 0 ) { Con_Printf("QuakeC crash report for %s:\n", PRVM_NAME); PRVM_PrintState(); } + if(prvm_errordump.integer) + { + // make a savegame + Host_Savegame_to(va("crash-%s.dmp", PRVM_NAME)); + } + // dump the stack so host_error can shutdown functions prog->depth = 0; prog->localstack_used = 0; + // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?) + vm_tempstringsbuf.cursize = 0; + // reset the prog pointer prog = NULL; } @@ -299,7 +511,7 @@ int PRVM_EnterFunction (mfunction_t *f) int i, j, c, o; if (!f) - PRVM_ERROR ("PRVM_EnterFunction: NULL function in %s\n", PRVM_NAME); + PRVM_ERROR ("PRVM_EnterFunction: NULL function in %s", PRVM_NAME); prog->stack[prog->depth].s = prog->xstatement; prog->stack[prog->depth].f = prog->xfunction; @@ -310,7 +522,7 @@ int PRVM_EnterFunction (mfunction_t *f) // save off any locals that the new function steps on c = f->locals; if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE) - PRVM_ERROR ("PRVM_ExecuteProgram: locals stack overflow in %s\n", PRVM_NAME); + PRVM_ERROR ("PRVM_ExecuteProgram: locals stack overflow in %s", PRVM_NAME); for (i=0 ; i < c ; i++) prog->localstack[prog->localstack_used+i] = ((int *)prog->globals.generic)[f->parm_start + i]; @@ -344,12 +556,12 @@ int PRVM_LeaveFunction (void) PRVM_ERROR ("prog stack underflow in %s", PRVM_NAME); if (!prog->xfunction) - PRVM_ERROR ("PR_LeaveFunction: NULL function in %s\n", PRVM_NAME); + PRVM_ERROR ("PR_LeaveFunction: NULL function in %s", PRVM_NAME); // restore locals from the stack c = prog->xfunction->locals; prog->localstack_used -= c; if (prog->localstack_used < 0) - PRVM_ERROR ("PRVM_ExecuteProgram: locals stack underflow in %s\n", PRVM_NAME); + PRVM_ERROR ("PRVM_ExecuteProgram: locals stack underflow in %s", PRVM_NAME); for (i=0 ; i < c ; i++) ((int *)prog->globals.generic)[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i]; @@ -378,27 +590,35 @@ PRVM_ExecuteProgram #define OPA ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->a]) #define OPB ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->b]) #define OPC ((prvm_eval_t *)&prog->globals.generic[(unsigned short) st->c]) -extern cvar_t prvm_boundscheck; extern cvar_t prvm_traceqc; -extern int PRVM_ED_FindFieldOffset (const char *field); -extern ddef_t* PRVM_ED_FindGlobal(const char *name); +extern cvar_t prvm_statementprofiling; +extern sizebuf_t vm_tempstringsbuf; +extern qboolean prvm_runawaycheck; +extern qboolean prvm_boundscheck; void PRVM_ExecuteProgram (func_t fnum, const char *errormessage) { - dstatement_t *st; + dstatement_t *st, *startst; mfunction_t *f, *newf; prvm_edict_t *ed; prvm_eval_t *ptr; - int profile, startprofile, cachedpr_trace, exitdepth; + int jumpcount, cachedpr_trace, exitdepth; + int restorevm_tempstringsbuf_cursize; + double calltime; + + calltime = Sys_DoubleTime(); if (!fnum || fnum >= (unsigned int)prog->progs->numfunctions) { - if (prog->self && PRVM_G_INT(prog->self->ofs)) - PRVM_ED_Print(PRVM_PROG_TO_EDICT(PRVM_G_INT(prog->self->ofs))); + if (prog->globaloffsets.self >= 0 && PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict) + PRVM_ED_Print(PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict), NULL); PRVM_ERROR ("PRVM_ExecuteProgram: %s", errormessage); } f = &prog->functions[fnum]; + // after executing this function, delete all tempstrings it created + restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize; + prog->trace = prvm_traceqc.integer; // we know we're done when pr_depth drops to this @@ -406,36 +626,161 @@ void PRVM_ExecuteProgram (func_t fnum, const char *errormessage) // make a stack frame st = &prog->statements[PRVM_EnterFunction (f)]; - startprofile = profile = 0; + // save the starting statement pointer for profiling + // (when the function exits or jumps, the (st - startst) integer value is + // added to the function's profile counter) + startst = st; + // instead of counting instructions, we count jumps + jumpcount = 0; + // add one to the callcount of this function because otherwise engine-called functions aren't counted + prog->xfunction->callcount++; chooseexecprogram: cachedpr_trace = prog->trace; - if (prvm_boundscheck.integer) + if (prvm_runawaycheck) { -#define PRVMBOUNDSCHECK 1 - if (prog->trace) +#define PRVMRUNAWAYCHECK 1 + if (prvm_statementprofiling.integer) { +#define PRVMSTATEMENTPROFILING 1 + if (prvm_boundscheck) + { +#define PRVMBOUNDSCHECK 1 + if (prog->trace) + { #define PRVMTRACE 1 #include "prvm_execprogram.h" +#undef PRVMTRACE + } + else + { +#include "prvm_execprogram.h" + } +#undef PRVMBOUNDSCHECK + } + else + { + if (prog->trace) + { +#define PRVMTRACE 1 +#include "prvm_execprogram.h" +#undef PRVMTRACE + } + else + { +#include "prvm_execprogram.h" + } + } +#undef PRVMSTATEMENTPROFILING } else { + if (prvm_boundscheck) + { +#define PRVMBOUNDSCHECK 1 + if (prog->trace) + { +#define PRVMTRACE 1 +#include "prvm_execprogram.h" #undef PRVMTRACE + } + else + { #include "prvm_execprogram.h" + } +#undef PRVMBOUNDSCHECK + } + else + { + if (prog->trace) + { +#define PRVMTRACE 1 +#include "prvm_execprogram.h" +#undef PRVMTRACE + } + else + { +#include "prvm_execprogram.h" + } + } } +#undef PRVMRUNAWAYCHECK } else { -#undef PRVMBOUNDSCHECK - if (prog->trace) + if (prvm_statementprofiling.integer) { +#define PRVMSTATEMENTPROFILING 1 + if (prvm_boundscheck) + { +#define PRVMBOUNDSCHECK 1 + if (prog->trace) + { #define PRVMTRACE 1 #include "prvm_execprogram.h" +#undef PRVMTRACE + } + else + { +#include "prvm_execprogram.h" + } +#undef PRVMBOUNDSCHECK + } + else + { + if (prog->trace) + { +#define PRVMTRACE 1 +#include "prvm_execprogram.h" +#undef PRVMTRACE + } + else + { +#include "prvm_execprogram.h" + } + } +#undef PRVMSTATEMENTPROFILING } else { + if (prvm_boundscheck) + { +#define PRVMBOUNDSCHECK 1 + if (prog->trace) + { +#define PRVMTRACE 1 +#include "prvm_execprogram.h" #undef PRVMTRACE + } + else + { +#include "prvm_execprogram.h" + } +#undef PRVMBOUNDSCHECK + } + else + { + if (prog->trace) + { +#define PRVMTRACE 1 #include "prvm_execprogram.h" +#undef PRVMTRACE + } + else + { +#include "prvm_execprogram.h" + } + } } } + +cleanup: + if (developer.integer >= 200 && vm_tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize) + Con_Printf("PRVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog->functions[fnum].s_name), vm_tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize); + // delete tempstrings created by this function + vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize; + + prog->functions[fnum].totaltime += (Sys_DoubleTime() - calltime); + + SV_FlushBroadcastMessages(); }