]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_exec.c
cmd: Use reentrant mutex for cbufs. Fixes deadlock when expanding aliases in some...
[xonotic/darkplaces.git] / prvm_exec.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "progsvm.h"
23
24 const char *prvm_opnames[] =
25 {
26 "^5DONE",
27
28 "MUL_F",
29 "MUL_V",
30 "MUL_FV",
31 "MUL_VF",
32
33 "DIV",
34
35 "ADD_F",
36 "ADD_V",
37
38 "SUB_F",
39 "SUB_V",
40
41 "^2EQ_F",
42 "^2EQ_V",
43 "^2EQ_S",
44 "^2EQ_E",
45 "^2EQ_FNC",
46
47 "^2NE_F",
48 "^2NE_V",
49 "^2NE_S",
50 "^2NE_E",
51 "^2NE_FNC",
52
53 "^2LE",
54 "^2GE",
55 "^2LT",
56 "^2GT",
57
58 "^6FIELD_F",
59 "^6FIELD_V",
60 "^6FIELD_S",
61 "^6FIELD_ENT",
62 "^6FIELD_FLD",
63 "^6FIELD_FNC",
64
65 "^1ADDRESS",
66
67 "STORE_F",
68 "STORE_V",
69 "STORE_S",
70 "STORE_ENT",
71 "STORE_FLD",
72 "STORE_FNC",
73
74 "^1STOREP_F",
75 "^1STOREP_V",
76 "^1STOREP_S",
77 "^1STOREP_ENT",
78 "^1STOREP_FLD",
79 "^1STOREP_FNC",
80
81 "^5RETURN",
82
83 "^2NOT_F",
84 "^2NOT_V",
85 "^2NOT_S",
86 "^2NOT_ENT",
87 "^2NOT_FNC",
88
89 "^5IF",
90 "^5IFNOT",
91
92 "^3CALL0",
93 "^3CALL1",
94 "^3CALL2",
95 "^3CALL3",
96 "^3CALL4",
97 "^3CALL5",
98 "^3CALL6",
99 "^3CALL7",
100 "^3CALL8",
101
102 "^1STATE",
103
104 "^5GOTO",
105
106 "^2AND",
107 "^2OR",
108
109 "BITAND",
110 "BITOR"
111 };
112
113
114
115 //=============================================================================
116
117 /*
118 =================
119 PRVM_PrintStatement
120 =================
121 */
122 extern cvar_t prvm_coverage;
123 extern cvar_t prvm_statementprofiling;
124 extern cvar_t prvm_timeprofiling;
125 static void PRVM_PrintStatement(prvm_prog_t *prog, mstatement_t *s)
126 {
127         size_t i;
128         int opnum = (int)(s - prog->statements);
129         char valuebuf[MAX_INPUTLINE];
130
131         Con_Printf("s%i: ", opnum);
132         if( prog->statement_linenums )
133         {
134                 if ( prog->statement_columnnums )
135                         Con_Printf( "%s:%i:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ], prog->statement_columnnums[ opnum ] );
136                 else
137                         Con_Printf( "%s:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ] );
138         }
139
140         if (prvm_statementprofiling.integer)
141                 Con_Printf("%7.0f ", prog->statement_profile[s - prog->statements]);
142
143         if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]))
144         {
145                 Con_Printf("%s ",  prvm_opnames[s->op]);
146                 i = strlen(prvm_opnames[s->op]);
147                 // don't count a preceding color tag when padding the name
148                 if (prvm_opnames[s->op][0] == STRING_COLOR_TAG)
149                         i -= 2;
150                 for ( ; i<10 ; i++)
151                         Con_Print(" ");
152         }
153         if (s->operand[0] >= 0) Con_Printf(  "%s", PRVM_GlobalString(prog, s->operand[0], valuebuf, sizeof(valuebuf)));
154         if (s->operand[1] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[1], valuebuf, sizeof(valuebuf)));
155         if (s->operand[2] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[2], valuebuf, sizeof(valuebuf)));
156         if (s->jumpabsolute >= 0) Con_Printf(", statement %i", s->jumpabsolute);
157         Con_Print("\n");
158 }
159
160 void PRVM_PrintFunctionStatements (prvm_prog_t *prog, const char *name)
161 {
162         int i, firststatement, endstatement;
163         mfunction_t *func;
164         func = PRVM_ED_FindFunction (prog, name);
165         if (!func)
166         {
167                 Con_Printf("%s progs: no function named %s\n", prog->name, name);
168                 return;
169         }
170         firststatement = func->first_statement;
171         if (firststatement < 0)
172         {
173                 Con_Printf("%s progs: function %s is builtin #%i\n", prog->name, name, -firststatement);
174                 return;
175         }
176
177         // find the end statement
178         endstatement = prog->numstatements;
179         for (i = 0;i < prog->numfunctions;i++)
180                 if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement)
181                         endstatement = prog->functions[i].first_statement;
182
183         // now print the range of statements
184         Con_Printf("%s progs: disassembly of function %s (statements %i-%i, locals %i-%i):\n", prog->name, name, firststatement, endstatement, func->parm_start, func->parm_start + func->locals - 1);
185         prog->xfunction = func;
186         for (i = firststatement;i < endstatement;i++)
187         {
188                 PRVM_PrintStatement(prog, prog->statements + i);
189                 if (!(prvm_coverage.integer & 4))
190                         prog->statement_profile[i] = 0;
191         }
192         if (prvm_coverage.integer & 4)
193                 Con_Printf("Collecting statement coverage, not flushing statement profile.\n");
194 }
195
196 /*
197 ============
198 PRVM_PrintFunction_f
199
200 ============
201 */
202 void PRVM_PrintFunction_f(cmd_state_t *cmd)
203 {
204         prvm_prog_t *prog;
205         if (Cmd_Argc(cmd) != 3)
206         {
207                 Con_Printf("usage: prvm_printfunction <program name> <function name>\n");
208                 return;
209         }
210
211         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
212                 return;
213
214         PRVM_PrintFunctionStatements(prog, Cmd_Argv(cmd, 2));
215 }
216
217 /*
218 ============
219 PRVM_StackTrace
220 ============
221 */
222 void PRVM_StackTrace (prvm_prog_t *prog)
223 {
224         mfunction_t     *f;
225         int                     i;
226
227         prog->stack[prog->depth].s = prog->xstatement;
228         prog->stack[prog->depth].f = prog->xfunction;
229         for (i = prog->depth;i > 0;i--)
230         {
231                 f = prog->stack[i].f;
232
233                 if (!f)
234                         Con_Print("<NULL FUNCTION>\n");
235                 else
236                 {
237                         if (prog->statement_linenums)
238                         {
239                                 if (prog->statement_columnnums)
240                                         Con_Printf("%12s:%i:%i : %s : statement %i\n", PRVM_GetString(prog, f->s_file), prog->statement_linenums[prog->stack[i].s], prog->statement_columnnums[prog->stack[i].s], PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement);
241                                 else
242                                         Con_Printf("%12s:%i : %s : statement %i\n", PRVM_GetString(prog, f->s_file), prog->statement_linenums[prog->stack[i].s], PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement);
243                         }
244                         else
245                                 Con_Printf("%12s : %s : statement %i\n", PRVM_GetString(prog, f->s_file), PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement);
246                 }
247         }
248 }
249
250 void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
251 {
252         mfunction_t     *f;
253         int                     i;
254         char vabuf[1024];
255
256         if(prog)
257         {
258                 dpsnprintf(buf, bufsize, "(%s) ", prog->name);
259         }
260         else
261         {
262                 strlcpy(buf, "<NO PROG>", bufsize);
263                 return;
264         }
265
266         prog->stack[prog->depth].s = prog->xstatement;
267         prog->stack[prog->depth].f = prog->xfunction;
268         for (i = prog->depth;i > 0;i--)
269         {
270                 f = prog->stack[i].f;
271
272                 if(strlcat(buf,
273                         f
274                                 ? va(vabuf, sizeof(vabuf), "%s:%s(%i) ", PRVM_GetString(prog, f->s_file), PRVM_GetString(prog, f->s_name), prog->stack[i].s - f->first_statement)
275                                 : "<NULL> ",
276                         bufsize
277                 ) >= bufsize)
278                         break;
279         }
280 }
281
282
283 static void PRVM_CallProfile (prvm_prog_t *prog)
284 {
285         mfunction_t *f, *best;
286         int i;
287         double max;
288         double sum;
289         double newprofiletime;
290
291         Con_Printf( "%s Call Profile:\n", prog->name );
292
293         sum = 0;
294         do
295         {
296                 max = 0;
297                 best = NULL;
298                 for (i=0 ; i<prog->numfunctions ; i++)
299                 {
300                         f = &prog->functions[i];
301                         if (max < f->totaltime)
302                         {
303                                 max = f->totaltime;
304                                 best = f;
305                         }
306                 }
307                 if (best)
308                 {
309                         sum += best->totaltime;
310                         Con_Printf("%9.4f %s\n", best->totaltime, PRVM_GetString(prog, best->s_name));
311                         best->totaltime = 0;
312                 }
313         } while (best);
314
315         newprofiletime = Sys_DirtyTime();
316         Con_Printf("Total time since last profile reset: %9.4f\n", newprofiletime - prog->profiletime);
317         Con_Printf("       - used by QC code of this VM: %9.4f\n", sum);
318
319         prog->profiletime = newprofiletime;
320 }
321
322 void PRVM_Profile (prvm_prog_t *prog, int maxfunctions, double mintime, int sortby)
323 {
324         mfunction_t *f, *best;
325         int i, num;
326         double max;
327
328         if(!prvm_timeprofiling.integer)
329                 mintime *= 10000000; // count each statement as about 0.1µs
330
331         if(prvm_timeprofiling.integer)
332                 Con_Printf( "%s Profile:\n[CallCount]      [Time] [BuiltinTm] [Statement] [BuiltinCt] [TimeTotal] [StmtTotal] [BltnTotal] [self]\n", prog->name );
333                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
334         else
335                 Con_Printf( "%s Profile:\n[CallCount] [Statement] [BuiltinCt] [StmtTotal] [BltnTotal] [self]\n", prog->name );
336                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
337
338         num = 0;
339         do
340         {
341                 max = 0;
342                 best = NULL;
343                 for (i=0 ; i<prog->numfunctions ; i++)
344                 {
345                         f = &prog->functions[i];
346                         if(prvm_timeprofiling.integer)
347                         {
348                                 if(sortby)
349                                 {
350                                         if(f->first_statement < 0)
351                                         {
352                                                 if (max < f->tprofile)
353                                                 {
354                                                         max = f->tprofile;
355                                                         best = f;
356                                                 }
357                                         }
358                                         else
359                                         {
360                                                 if (max < f->tprofile_total)
361                                                 {
362                                                         max = f->tprofile_total;
363                                                         best = f;
364                                                 }
365                                         }
366                                 }
367                                 else
368                                 {
369                                         if (max < f->tprofile + f->tbprofile)
370                                         {
371                                                 max = f->tprofile + f->tbprofile;
372                                                 best = f;
373                                         }
374                                 }
375                         }
376                         else
377                         {
378                                 if(sortby)
379                                 {
380                                         if (max < f->profile_total + f->builtinsprofile_total + f->callcount)
381                                         {
382                                                 max = f->profile_total + f->builtinsprofile_total + f->callcount;
383                                                 best = f;
384                                         }
385                                 }
386                                 else
387                                 {
388                                         if (max < f->profile + f->builtinsprofile + f->callcount)
389                                         {
390                                                 max = f->profile + f->builtinsprofile + f->callcount;
391                                                 best = f;
392                                         }
393                                 }
394                         }
395                 }
396                 if (best)
397                 {
398                         if (num < maxfunctions && max > mintime)
399                         {
400                                 if(prvm_timeprofiling.integer)
401                                 {
402                                         if (best->first_statement < 0)
403                                                 Con_Printf("%11.0f %11.6f ------------- builtin ------------- %11.6f ----------- builtin ----------- %s\n", best->callcount, best->tprofile, best->tprofile, PRVM_GetString(prog, best->s_name));
404                                         //                 %11.6f 12345678901 12345678901 12345678901 %11.6f 12345678901 12345678901 123.45%
405                                         else
406                                                 Con_Printf("%11.0f %11.6f %11.6f %11.0f %11.0f %11.6f %11.0f %11.0f %6.2f%% %s\n", best->callcount, best->tprofile, best->tbprofile, best->profile, best->builtinsprofile, best->tprofile_total, best->profile_total, best->builtinsprofile_total, (best->tprofile_total > 0) ? ((best->tprofile) * 100.0 / (best->tprofile_total)) : -99.99, PRVM_GetString(prog, best->s_name));
407                                 }
408                                 else
409                                 {
410                                         if (best->first_statement < 0)
411                                                 Con_Printf("%11.0f ----------------------- builtin ----------------------- %s\n", best->callcount, PRVM_GetString(prog, best->s_name));
412                                         //                 12345678901 12345678901 12345678901 12345678901 123.45%
413                                         else
414                                                 Con_Printf("%11.0f %11.0f %11.0f %11.0f %11.0f %6.2f%% %s\n", best->callcount, best->profile, best->builtinsprofile, best->profile_total, best->builtinsprofile_total, (best->profile + best->builtinsprofile) * 100.0 / (best->profile_total + best->builtinsprofile_total), PRVM_GetString(prog, best->s_name));
415                                 }
416                         }
417                         num++;
418                         best->profile = 0;
419                         best->tprofile = 0;
420                         best->tbprofile = 0;
421                         best->builtinsprofile = 0;
422                         best->profile_total = 0;
423                         best->tprofile_total = 0;
424                         best->builtinsprofile_total = 0;
425                         best->callcount = 0;
426                 }
427         } while (best);
428 }
429
430 /*
431 ============
432 PRVM_CallProfile_f
433
434 ============
435 */
436 void PRVM_CallProfile_f(cmd_state_t *cmd)
437 {
438         prvm_prog_t *prog;
439         if (Cmd_Argc(cmd) != 2)
440         {
441                 Con_Print("prvm_callprofile <program name>\n");
442                 return;
443         }
444
445         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
446                 return;
447
448         PRVM_CallProfile(prog);
449 }
450
451 /*
452 ============
453 PRVM_Profile_f
454
455 ============
456 */
457 void PRVM_Profile_f(cmd_state_t *cmd)
458 {
459         prvm_prog_t *prog;
460         int howmany;
461
462         if (prvm_coverage.integer & 1)
463         {
464                 Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
465                 return;
466         }
467
468         howmany = 1<<30;
469         if (Cmd_Argc(cmd) == 3)
470                 howmany = atoi(Cmd_Argv(cmd, 2));
471         else if (Cmd_Argc(cmd) != 2)
472         {
473                 Con_Print("prvm_profile <program name>\n");
474                 return;
475         }
476
477         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
478                 return;
479
480         PRVM_Profile(prog, howmany, 0, 0);
481 }
482
483 void PRVM_ChildProfile_f(cmd_state_t *cmd)
484 {
485         prvm_prog_t *prog;
486         int howmany;
487
488         if (prvm_coverage.integer & 1)
489         {
490                 Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
491                 return;
492         }
493
494         howmany = 1<<30;
495         if (Cmd_Argc(cmd) == 3)
496                 howmany = atoi(Cmd_Argv(cmd, 2));
497         else if (Cmd_Argc(cmd) != 2)
498         {
499                 Con_Print("prvm_childprofile <program name>\n");
500                 return;
501         }
502
503         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
504                 return;
505
506         PRVM_Profile(prog, howmany, 0, 1);
507 }
508
509 void PRVM_PrintState(prvm_prog_t *prog, int stack_index)
510 {
511         int i;
512         mfunction_t *func = prog->xfunction;
513         int st = prog->xstatement;
514         if (stack_index > 0 && stack_index <= prog->depth)
515         {
516                 func = prog->stack[prog->depth - stack_index].f;
517                 st = prog->stack[prog->depth - stack_index].s;
518         }
519         if (prog->statestring)
520         {
521                 Con_Printf("Caller-provided information: %s\n", prog->statestring);
522         }
523         if (func)
524         {
525                 for (i = -7; i <= 0;i++)
526                         if (st + i >= func->first_statement)
527                                 PRVM_PrintStatement(prog, prog->statements + st + i);
528         }
529         PRVM_StackTrace(prog);
530 }
531
532 extern cvar_t prvm_errordump;
533 void PRVM_Crash(prvm_prog_t *prog)
534 {
535         char vabuf[1024];
536         if (prog == NULL)
537                 return;
538         if (!prog->loaded)
539                 return;
540
541         PRVM_serverfunction(SV_Shutdown) = 0; // don't call SV_Shutdown on crash
542
543         if( prog->depth > 0 )
544         {
545                 Con_Printf("QuakeC crash report for %s:\n", prog->name);
546                 PRVM_PrintState(prog, 0);
547         }
548
549         if(prvm_errordump.integer)
550         {
551                 // make a savegame
552                 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
553         }
554
555         // dump the stack so host_error can shutdown functions
556         prog->depth = 0;
557         prog->localstack_used = 0;
558
559         // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
560         prog->tempstringsbuf.cursize = 0;
561
562         // reset the prog pointer
563         prog = NULL;
564 }
565
566 /*
567 ============================================================================
568 PRVM_ExecuteProgram
569
570 The interpretation main loop
571 ============================================================================
572 */
573
574 /*
575 ====================
576 PRVM_EnterFunction
577
578 Returns the new program statement counter
579 ====================
580 */
581 static int PRVM_EnterFunction (prvm_prog_t *prog, mfunction_t *f)
582 {
583         int             i, j, c, o;
584
585         if (!f)
586                 prog->error_cmd("PRVM_EnterFunction: NULL function in %s", prog->name);
587
588         prog->stack[prog->depth].s = prog->xstatement;
589         prog->stack[prog->depth].f = prog->xfunction;
590         prog->stack[prog->depth].profile_acc = -f->profile;
591         prog->stack[prog->depth].tprofile_acc = -f->tprofile + -f->tbprofile;
592         prog->stack[prog->depth].builtinsprofile_acc = -f->builtinsprofile;
593         prog->depth++;
594         if (prog->depth >=PRVM_MAX_STACK_DEPTH)
595                 prog->error_cmd("stack overflow");
596
597 // save off any locals that the new function steps on
598         c = f->locals;
599         if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
600                 prog->error_cmd("PRVM_ExecuteProgram: locals stack overflow in %s", prog->name);
601
602         for (i=0 ; i < c ; i++)
603                 prog->localstack[prog->localstack_used+i] = prog->globals.ip[f->parm_start + i];
604         prog->localstack_used += c;
605
606 // copy parameters
607         o = f->parm_start;
608         for (i=0 ; i<f->numparms ; i++)
609         {
610                 for (j=0 ; j<f->parm_size[i] ; j++)
611                 {
612                         prog->globals.ip[o] = prog->globals.ip[OFS_PARM0+i*3+j];
613                         o++;
614                 }
615         }
616
617         ++f->recursion;
618         prog->xfunction = f;
619         return f->first_statement - 1;  // offset the s++
620 }
621
622 /*
623 ====================
624 PRVM_LeaveFunction
625 ====================
626 */
627 static int PRVM_LeaveFunction (prvm_prog_t *prog)
628 {
629         int             i, c;
630         mfunction_t *f;
631
632         if (prog->depth <= 0)
633                 prog->error_cmd("prog stack underflow in %s", prog->name);
634
635         if (!prog->xfunction)
636                 prog->error_cmd("PR_LeaveFunction: NULL function in %s", prog->name);
637 // restore locals from the stack
638         c = prog->xfunction->locals;
639         prog->localstack_used -= c;
640         if (prog->localstack_used < 0)
641                 prog->error_cmd("PRVM_ExecuteProgram: locals stack underflow in %s", prog->name);
642
643         for (i=0 ; i < c ; i++)
644                 prog->globals.ip[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
645
646 // up stack
647         prog->depth--;
648         f = prog->xfunction;
649         --f->recursion;
650         prog->xfunction = prog->stack[prog->depth].f;
651         prog->stack[prog->depth].profile_acc += f->profile;
652         prog->stack[prog->depth].tprofile_acc += f->tprofile + f->tbprofile;
653         prog->stack[prog->depth].builtinsprofile_acc += f->builtinsprofile;
654         if(prog->depth > 0)
655         {
656                 prog->stack[prog->depth-1].profile_acc += prog->stack[prog->depth].profile_acc;
657                 prog->stack[prog->depth-1].tprofile_acc += prog->stack[prog->depth].tprofile_acc;
658                 prog->stack[prog->depth-1].builtinsprofile_acc += prog->stack[prog->depth].builtinsprofile_acc;
659         }
660         if(!f->recursion)
661         {
662                 // if f is already on the call stack...
663                 // we cannot add this profile data to it now
664                 // or we would add it more than once
665                 // so, let's only add to the function's profile if it is the outermost call
666                 f->profile_total += prog->stack[prog->depth].profile_acc;
667                 f->tprofile_total += prog->stack[prog->depth].tprofile_acc;
668                 f->builtinsprofile_total += prog->stack[prog->depth].builtinsprofile_acc;
669         }
670         
671         return prog->stack[prog->depth].s;
672 }
673
674 void PRVM_Init_Exec(prvm_prog_t *prog)
675 {
676         // dump the stack
677         prog->depth = 0;
678         prog->localstack_used = 0;
679         // reset the string table
680         // nothing here yet
681 }
682
683 /*
684 ==================
685 Coverage
686 ==================
687 */
688 // Note: in these two calls, prog->xfunction is assumed to be sane.
689 static const char *PRVM_WhereAmI(char *buf, size_t bufsize, prvm_prog_t *prog, mfunction_t *func, int statement)
690 {
691         if (prog->statement_linenums)
692         {
693                 if (prog->statement_columnnums)
694                         return va(buf, bufsize, "%s:%i:%i(%s, %i)", PRVM_GetString(prog, func->s_file), prog->statement_linenums[statement], prog->statement_columnnums[statement], PRVM_GetString(prog, func->s_name), statement - func->first_statement);
695                 else
696                         return va(buf, bufsize, "%s:%i(%s, %i)", PRVM_GetString(prog, func->s_file), prog->statement_linenums[statement], PRVM_GetString(prog, func->s_name), statement - func->first_statement);
697         }
698         else
699                 return va(buf, bufsize, "%s(%s, %i)", PRVM_GetString(prog, func->s_file), PRVM_GetString(prog, func->s_name), statement - func->first_statement);
700 }
701 static void PRVM_FunctionCoverageEvent(prvm_prog_t *prog, mfunction_t *func)
702 {
703         ++prog->functions_covered;
704         Con_Printf("prvm_coverage: %s just called %s for the first time. Coverage: %.2f%%.\n", prog->name, PRVM_GetString(prog, func->s_name), prog->functions_covered * 100.0 / prog->numfunctions);
705 }
706 void PRVM_ExplicitCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
707 {
708         char vabuf[128];
709         ++prog->explicit_covered;
710         Con_Printf("prvm_coverage: %s just executed a coverage() statement at %s for the first time. Coverage: %.2f%%.\n", prog->name, PRVM_WhereAmI(vabuf, sizeof(vabuf), prog, func, statement), prog->explicit_covered * 100.0 / prog->numexplicitcoveragestatements);
711 }
712 static void PRVM_StatementCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
713 {
714         char vabuf[128];
715         ++prog->statements_covered;
716         Con_Printf("prvm_coverage: %s just executed a statement at %s for the first time. Coverage: %.2f%%.\n", prog->name, PRVM_WhereAmI(vabuf, sizeof(vabuf), prog, func, statement), prog->statements_covered * 100.0 / prog->numstatements);
717 }
718
719 #if defined (__GNUC__) || (__clang__) || (__TINYC__)
720 #  ifndef CONFIG_PEDANTIC
721 #  define HAVE_COMPUTED_GOTOS 1
722 #  endif
723 #endif
724
725 #define OPA ((prvm_eval_t *)&globals[st->operand[0]])
726 #define OPB ((prvm_eval_t *)&globals[st->operand[1]])
727 #define OPC ((prvm_eval_t *)&globals[st->operand[2]])
728 extern cvar_t prvm_traceqc;
729 extern cvar_t prvm_statementprofiling;
730 extern qboolean prvm_runawaycheck;
731
732 #ifdef PROFILING
733 #ifdef CONFIG_MENU
734 /*
735 ====================
736 MVM_ExecuteProgram
737 ====================
738 */
739 void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
740 {
741         mstatement_t    *st, *startst;
742         mfunction_t             *func, *enterfunc;
743         prvm_edict_t    *ed;
744         prvm_eval_t     *ptr;
745         int             jumpcount, cachedpr_trace, exitdepth;
746         int             restorevm_tempstringsbuf_cursize;
747         double  calltime;
748         double tm, starttm;
749         prvm_vec_t tempfloat;
750         // these may become out of date when a builtin is called, and are updated accordingly
751         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
752         unsigned int cached_entityfields = prog->entityfields;
753         unsigned int cached_entityfields_3 = prog->entityfields - 3;
754         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
755         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
756         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
757         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
758         unsigned int cached_max_edicts = prog->max_edicts;
759         // these do not change
760         mstatement_t *cached_statements = prog->statements;
761         qboolean cached_allowworldwrites = prog->allowworldwrites;
762         unsigned int cached_flag = prog->flag;
763
764         prvm_vec_t *globals = prog->globals.fp;
765
766         calltime = Sys_DirtyTime();
767
768         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
769         {
770                 if (PRVM_allglobaledict(self))
771                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
772                 prog->error_cmd("MVM_ExecuteProgram: %s", errormessage);
773         }
774
775         func = &prog->functions[fnum];
776
777         // after executing this function, delete all tempstrings it created
778         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
779
780         prog->trace = prvm_traceqc.integer;
781
782         // we know we're done when pr_depth drops to this
783         exitdepth = prog->depth;
784
785 // make a stack frame
786         st = &prog->statements[PRVM_EnterFunction(prog, func)];
787         // save the starting statement pointer for profiling
788         // (when the function exits or jumps, the (st - startst) integer value is
789         // added to the function's profile counter)
790         startst = st;
791         starttm = calltime;
792         // instead of counting instructions, we count jumps
793         jumpcount = 0;
794         // add one to the callcount of this function because otherwise engine-called functions aren't counted
795         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
796                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
797
798 chooseexecprogram:
799         cachedpr_trace = prog->trace;
800         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
801         {
802 #define PRVMSLOWINTERPRETER 1
803                 if (prvm_timeprofiling.integer)
804                 {
805 #define PRVMTIMEPROFILING 1
806 #include "prvm_execprogram.h"
807 #undef PRVMTIMEPROFILING
808                 }
809                 else
810                 {
811 #include "prvm_execprogram.h"
812                 }
813 #undef PRVMSLOWINTERPRETER
814         }
815         else
816         {
817                 if (prvm_timeprofiling.integer)
818                 {
819 #define PRVMTIMEPROFILING 1
820 #include "prvm_execprogram.h"
821 #undef PRVMTIMEPROFILING
822                 }
823                 else
824                 {
825 #include "prvm_execprogram.h"
826                 }
827         }
828
829 cleanup:
830         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
831                 Con_DPrintf("MVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog, prog->functions[fnum].s_name), prog->tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
832         // delete tempstrings created by this function
833         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
834
835         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
836         func->totaltime += tm;
837
838         if (prog == SVVM_prog)
839                 SV_FlushBroadcastMessages();
840 }
841 #endif
842
843 /*
844 ====================
845 CLVM_ExecuteProgram
846 ====================
847 */
848 void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
849 {
850         mstatement_t    *st, *startst;
851         mfunction_t             *func, *enterfunc;
852         prvm_edict_t    *ed;
853         prvm_eval_t     *ptr;
854         int             jumpcount, cachedpr_trace, exitdepth;
855         int             restorevm_tempstringsbuf_cursize;
856         double  calltime;
857         double tm, starttm;
858         prvm_vec_t tempfloat;
859         // these may become out of date when a builtin is called, and are updated accordingly
860         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
861         unsigned int cached_entityfields = prog->entityfields;
862         unsigned int cached_entityfields_3 = prog->entityfields - 3;
863         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
864         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
865         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
866         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
867         unsigned int cached_max_edicts = prog->max_edicts;
868         // these do not change
869         mstatement_t *cached_statements = prog->statements;
870         qboolean cached_allowworldwrites = prog->allowworldwrites;
871         unsigned int cached_flag = prog->flag;
872
873         prvm_vec_t *globals = prog->globals.fp;
874
875         calltime = Sys_DirtyTime();
876
877         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
878         {
879                 if (PRVM_allglobaledict(self))
880                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
881                 prog->error_cmd("CLVM_ExecuteProgram: %s", errormessage);
882         }
883
884         func = &prog->functions[fnum];
885
886         // after executing this function, delete all tempstrings it created
887         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
888
889         prog->trace = prvm_traceqc.integer;
890
891         // we know we're done when pr_depth drops to this
892         exitdepth = prog->depth;
893
894 // make a stack frame
895         st = &prog->statements[PRVM_EnterFunction(prog, func)];
896         // save the starting statement pointer for profiling
897         // (when the function exits or jumps, the (st - startst) integer value is
898         // added to the function's profile counter)
899         startst = st;
900         starttm = calltime;
901         // instead of counting instructions, we count jumps
902         jumpcount = 0;
903         // add one to the callcount of this function because otherwise engine-called functions aren't counted
904         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
905                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
906
907 chooseexecprogram:
908         cachedpr_trace = prog->trace;
909         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
910         {
911 #define PRVMSLOWINTERPRETER 1
912                 if (prvm_timeprofiling.integer)
913                 {
914 #define PRVMTIMEPROFILING 1
915 #include "prvm_execprogram.h"
916 #undef PRVMTIMEPROFILING
917                 }
918                 else
919                 {
920 #include "prvm_execprogram.h"
921                 }
922 #undef PRVMSLOWINTERPRETER
923         }
924         else
925         {
926                 if (prvm_timeprofiling.integer)
927                 {
928 #define PRVMTIMEPROFILING 1
929 #include "prvm_execprogram.h"
930 #undef PRVMTIMEPROFILING
931                 }
932                 else
933                 {
934 #include "prvm_execprogram.h"
935                 }
936         }
937
938 cleanup:
939         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
940                 Con_DPrintf("CLVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog, prog->functions[fnum].s_name), prog->tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
941         // delete tempstrings created by this function
942         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
943
944         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
945         func->totaltime += tm;
946
947         if (prog == SVVM_prog)
948                 SV_FlushBroadcastMessages();
949 }
950 #endif
951
952 /*
953 ====================
954 SVVM_ExecuteProgram
955 ====================
956 */
957 #ifdef PROFILING
958 void SVVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
959 #else
960 void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
961 #endif
962 {
963         mstatement_t    *st, *startst;
964         mfunction_t             *func, *enterfunc;
965         prvm_edict_t    *ed;
966         prvm_eval_t     *ptr;
967         int             jumpcount, cachedpr_trace, exitdepth;
968         int             restorevm_tempstringsbuf_cursize;
969         double  calltime;
970         double tm, starttm;
971         prvm_vec_t tempfloat;
972         // these may become out of date when a builtin is called, and are updated accordingly
973         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
974         unsigned int cached_entityfields = prog->entityfields;
975         unsigned int cached_entityfields_3 = prog->entityfields - 3;
976         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
977         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
978         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
979         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
980         unsigned int cached_max_edicts = prog->max_edicts;
981         // these do not change
982         mstatement_t *cached_statements = prog->statements;
983         qboolean cached_allowworldwrites = prog->allowworldwrites;
984         unsigned int cached_flag = prog->flag;
985
986         prvm_vec_t *globals = prog->globals.fp;
987
988         calltime = Sys_DirtyTime();
989
990         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
991         {
992                 if (PRVM_allglobaledict(self))
993                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
994                 prog->error_cmd("SVVM_ExecuteProgram: %s", errormessage);
995         }
996
997         func = &prog->functions[fnum];
998
999         // after executing this function, delete all tempstrings it created
1000         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1001
1002         prog->trace = prvm_traceqc.integer;
1003
1004         // we know we're done when pr_depth drops to this
1005         exitdepth = prog->depth;
1006
1007 // make a stack frame
1008         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1009         // save the starting statement pointer for profiling
1010         // (when the function exits or jumps, the (st - startst) integer value is
1011         // added to the function's profile counter)
1012         startst = st;
1013         starttm = calltime;
1014         // instead of counting instructions, we count jumps
1015         jumpcount = 0;
1016         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1017         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1018                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1019
1020 chooseexecprogram:
1021         cachedpr_trace = prog->trace;
1022         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1023         {
1024 #define PRVMSLOWINTERPRETER 1
1025                 if (prvm_timeprofiling.integer)
1026                 {
1027 #define PRVMTIMEPROFILING 1
1028 #include "prvm_execprogram.h"
1029 #undef PRVMTIMEPROFILING
1030                 }
1031                 else
1032                 {
1033 #include "prvm_execprogram.h"
1034                 }
1035 #undef PRVMSLOWINTERPRETER
1036         }
1037         else
1038         {
1039                 if (prvm_timeprofiling.integer)
1040                 {
1041 #define PRVMTIMEPROFILING 1
1042 #include "prvm_execprogram.h"
1043 #undef PRVMTIMEPROFILING
1044                 }
1045                 else
1046                 {
1047 #include "prvm_execprogram.h"
1048                 }
1049         }
1050
1051 cleanup:
1052         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1053                 Con_DPrintf("SVVM_ExecuteProgram: %s used %i bytes of tempstrings\n", PRVM_GetString(prog, prog->functions[fnum].s_name), prog->tempstringsbuf.cursize - restorevm_tempstringsbuf_cursize);
1054         // delete tempstrings created by this function
1055         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1056
1057         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1058         func->totaltime += tm;
1059
1060         if (prog == SVVM_prog)
1061                 SV_FlushBroadcastMessages();
1062 }