]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_exec.c
sys: improve error and crash handling
[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 static 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 NULL,
116 NULL,
117 NULL,
118 NULL,
119 NULL,
120 NULL,
121 NULL,
122 NULL,
123 NULL,
124 NULL,
125 NULL,
126 NULL,
127 NULL,
128 NULL,
129 NULL,
130 NULL,
131 NULL,
132 NULL,
133 NULL,
134 NULL,
135 NULL,
136 NULL,
137 NULL,
138 NULL,
139 NULL,
140 NULL,
141 NULL,
142 NULL,
143 NULL,
144 NULL,
145 NULL,
146 NULL,
147 NULL,
148 NULL,
149 NULL,
150 NULL,
151 NULL,
152 NULL,
153 NULL,
154 NULL,
155 NULL,
156 NULL,
157 NULL,
158 NULL,
159 NULL,
160 NULL,
161 NULL,
162
163 "STORE_I",
164
165 NULL,
166 NULL,
167
168 "ADD_I",
169 "ADD_FI",
170 "ADD_IF",
171
172 "SUB_I",
173 "SUB_FI",
174 "SUB_IF",
175 "CONV_IF",
176 "CONV_FI",
177
178 NULL,
179 NULL,
180
181 "LOAD_I",
182 "STOREP_I",
183
184 NULL,
185 NULL,
186
187 "BITAND_I",
188 "BITOR_I",
189
190 "MUL_I",
191 "DIV_I",
192 "EQ_I",
193 "NE_I",
194
195 NULL,
196 NULL,
197
198 "NOT_I",
199
200 "DIV_VF",
201
202 NULL,
203 NULL,
204 NULL,
205 NULL,
206 NULL,
207 NULL,
208 NULL,
209 NULL,
210 NULL,
211 NULL,
212 NULL,
213 NULL,
214
215 "STORE_P",
216
217 NULL,
218 NULL,
219 NULL,
220 NULL,
221 NULL,
222 NULL,
223 NULL,
224 NULL,
225
226 "LE_I",
227 "GE_I",
228 "LT_I",
229 "GT_I",
230
231 "LE_IF",
232 "GE_IF",
233 "LT_IF",
234 "GT_IF",
235
236 "LE_FI",
237 "GE_FI",
238 "LT_FI",
239 "GT_FI",
240
241 "EQ_IF",
242 "EQ_FI",
243
244 NULL,
245 NULL,
246 NULL,
247 NULL,
248
249 "MUL_IF",
250 "MUL_FI",
251 "MUL_VI",
252
253 NULL,
254
255 "DIV_IF",
256 "DIV_FI",
257 "BITAND_IF",
258 "BITOR_IF",
259 "BITAND_FI",
260 "BITOR_FI",
261 "AND_I",
262 "OR_I",
263 "AND_IF",
264 "OR_IF",
265 "AND_FI",
266 "OR_FI",
267 "NE_IF",
268 "NE_FI",
269
270 "GSTOREP_I",
271 "GSTOREP_F",
272 "GSTOREP_ENT",
273 "GSTOREP_FLD",
274 "GSTOREP_S",
275 "GSTOREP_FNC",
276 "GSTOREP_V",
277 "GADDRESS",
278 "GLOAD_I",
279 "GLOAD_F",
280 "GLOAD_FLD",
281 "GLOAD_ENT",
282 "GLOAD_S",
283 "GLOAD_FNC",
284 "BOUNDCHECK",
285 NULL,
286 NULL,
287 NULL,
288 NULL,
289 "GLOAD_V",
290 };
291
292
293
294 //=============================================================================
295
296 /*
297 =================
298 PRVM_PrintStatement
299 =================
300 */
301 extern cvar_t prvm_coverage;
302 extern cvar_t prvm_statementprofiling;
303 extern cvar_t prvm_timeprofiling;
304 static void PRVM_PrintStatement(prvm_prog_t *prog, mstatement_t *s)
305 {
306         size_t i;
307         int opnum = (int)(s - prog->statements);
308         char valuebuf[MAX_INPUTLINE];
309         const char *opname;
310
311         Con_Printf("s%i: ", opnum);
312         if( prog->statement_linenums )
313         {
314                 if ( prog->statement_columnnums )
315                         Con_Printf( "%s:%i:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ], prog->statement_columnnums[ opnum ] );
316                 else
317                         Con_Printf( "%s:%i: ", PRVM_GetString( prog, prog->xfunction->s_file ), prog->statement_linenums[ opnum ] );
318         }
319
320         if (prvm_statementprofiling.integer)
321                 Con_Printf("%7.0f ", prog->statement_profile[s - prog->statements]);
322
323         if ( (unsigned)s->op < sizeof(prvm_opnames)/sizeof(prvm_opnames[0]) && prvm_opnames[s->op])
324                 opname = prvm_opnames[s->op];
325         else
326                 opname = valuebuf, dpsnprintf(valuebuf, sizeof(valuebuf), "OPCODE_%u", (unsigned)s->op);
327         Con_Printf("%s ",  opname);
328         i = strlen(opname);
329         // don't count a preceding color tag when padding the name
330         if (opname[0] == STRING_COLOR_TAG)
331                 i -= 2;
332         for ( ; i<10 ; i++)
333                 Con_Print(" ");
334
335         if (s->operand[0] >= 0) Con_Printf(  "%s", PRVM_GlobalString(prog, s->operand[0], valuebuf, sizeof(valuebuf)));
336         if (s->operand[1] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[1], valuebuf, sizeof(valuebuf)));
337         if (s->operand[2] >= 0) Con_Printf(", %s", PRVM_GlobalString(prog, s->operand[2], valuebuf, sizeof(valuebuf)));
338         if (s->jumpabsolute >= 0) Con_Printf(", statement %i", s->jumpabsolute);
339         Con_Print("\n");
340 }
341
342 void PRVM_PrintFunctionStatements (prvm_prog_t *prog, const char *name)
343 {
344         int i, firststatement, endstatement;
345         mfunction_t *func;
346         func = PRVM_ED_FindFunction (prog, name);
347         if (!func)
348         {
349                 Con_Printf("%s progs: no function named %s\n", prog->name, name);
350                 return;
351         }
352         firststatement = func->first_statement;
353         if (firststatement < 0)
354         {
355                 Con_Printf("%s progs: function %s is builtin #%i\n", prog->name, name, -firststatement);
356                 return;
357         }
358
359         // find the end statement
360         endstatement = prog->numstatements;
361         for (i = 0;i < prog->numfunctions;i++)
362                 if (endstatement > prog->functions[i].first_statement && firststatement < prog->functions[i].first_statement)
363                         endstatement = prog->functions[i].first_statement;
364
365         // now print the range of statements
366         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);
367         prog->xfunction = func;
368         for (i = firststatement;i < endstatement;i++)
369         {
370                 PRVM_PrintStatement(prog, prog->statements + i);
371                 if (!(prvm_coverage.integer & 4))
372                         prog->statement_profile[i] = 0;
373         }
374         if (prvm_coverage.integer & 4)
375                 Con_Printf("Collecting statement coverage, not flushing statement profile.\n");
376 }
377
378 /*
379 ============
380 PRVM_PrintFunction_f
381
382 ============
383 */
384 void PRVM_PrintFunction_f(cmd_state_t *cmd)
385 {
386         prvm_prog_t *prog;
387         if (Cmd_Argc(cmd) != 3)
388         {
389                 Con_Printf("usage: prvm_printfunction <program name> <function name>\n");
390                 return;
391         }
392
393         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
394                 return;
395
396         PRVM_PrintFunctionStatements(prog, Cmd_Argv(cmd, 2));
397 }
398
399 /*
400 ============
401 PRVM_StackTrace
402 ============
403 */
404 void PRVM_StackTrace (prvm_prog_t *prog)
405 {
406         mfunction_t     *f;
407         int                     i;
408
409         prog->stack[prog->depth].s = prog->xstatement;
410         prog->stack[prog->depth].f = prog->xfunction;
411         for (i = prog->depth;i > 0;i--)
412         {
413                 f = prog->stack[i].f;
414
415                 if (!f)
416                         Con_Print("<NULL FUNCTION>\n");
417                 else
418                 {
419                         if (prog->statement_linenums)
420                         {
421                                 if (prog->statement_columnnums)
422                                         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);
423                                 else
424                                         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);
425                         }
426                         else
427                                 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);
428                 }
429         }
430 }
431
432 void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
433 {
434         mfunction_t *f;
435         int i;
436         char vabuf[1024];
437         char *p;
438
439         if(prog)
440         {
441                 i = dpsnprintf(buf, bufsize, "(%s) ", prog->name);
442                 p = buf + max(0, i);
443         }
444         else
445         {
446                 dp_strlcpy(buf, "<NO PROG>", bufsize);
447                 return;
448         }
449
450         prog->stack[prog->depth].s = prog->xstatement;
451         prog->stack[prog->depth].f = prog->xfunction;
452         for (i = prog->depth;i > 0;i--)
453         {
454                 f = prog->stack[i].f;
455                 p = dp_stpecpy(
456                         p,
457                         buf + bufsize,
458                         f
459                                 ? 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)
460                                 : "<NULL> "
461                         );
462                 if (p == buf + bufsize)
463                         break;
464         }
465 }
466
467
468 static void PRVM_CallProfile (prvm_prog_t *prog)
469 {
470         mfunction_t *f, *best;
471         int i;
472         double max;
473         double sum;
474         double newprofiletime;
475
476         Con_Printf( "%s Call Profile:\n", prog->name );
477
478         sum = 0;
479         do
480         {
481                 max = 0;
482                 best = NULL;
483                 for (i=0 ; i<prog->numfunctions ; i++)
484                 {
485                         f = &prog->functions[i];
486                         if (max < f->totaltime)
487                         {
488                                 max = f->totaltime;
489                                 best = f;
490                         }
491                 }
492                 if (best)
493                 {
494                         sum += best->totaltime;
495                         Con_Printf("%9.4f %s\n", best->totaltime, PRVM_GetString(prog, best->s_name));
496                         best->totaltime = 0;
497                 }
498         } while (best);
499
500         newprofiletime = Sys_DirtyTime();
501         Con_Printf("Total time since last profile reset: %9.4f\n", newprofiletime - prog->profiletime);
502         Con_Printf("       - used by QC code of this VM: %9.4f\n", sum);
503
504         prog->profiletime = newprofiletime;
505 }
506
507 void PRVM_Profile (prvm_prog_t *prog, int maxfunctions, double mintime, int sortby)
508 {
509         mfunction_t *f, *best;
510         int i, num;
511         double max;
512
513         if(!prvm_timeprofiling.integer)
514                 mintime *= 10000000; // count each statement as about 0.1µs
515
516         if(prvm_timeprofiling.integer)
517                 Con_Printf( "%s Profile:\n[CallCount]      [Time] [BuiltinTm] [Statement] [BuiltinCt] [TimeTotal] [StmtTotal] [BltnTotal] [self]\n", prog->name );
518                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
519         else
520                 Con_Printf( "%s Profile:\n[CallCount] [Statement] [BuiltinCt] [StmtTotal] [BltnTotal] [self]\n", prog->name );
521                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
522
523         num = 0;
524         do
525         {
526                 max = 0;
527                 best = NULL;
528                 for (i=0 ; i<prog->numfunctions ; i++)
529                 {
530                         f = &prog->functions[i];
531                         if(prvm_timeprofiling.integer)
532                         {
533                                 if(sortby)
534                                 {
535                                         if(f->first_statement < 0)
536                                         {
537                                                 if (max < f->tprofile)
538                                                 {
539                                                         max = f->tprofile;
540                                                         best = f;
541                                                 }
542                                         }
543                                         else
544                                         {
545                                                 if (max < f->tprofile_total)
546                                                 {
547                                                         max = f->tprofile_total;
548                                                         best = f;
549                                                 }
550                                         }
551                                 }
552                                 else
553                                 {
554                                         if (max < f->tprofile + f->tbprofile)
555                                         {
556                                                 max = f->tprofile + f->tbprofile;
557                                                 best = f;
558                                         }
559                                 }
560                         }
561                         else
562                         {
563                                 if(sortby)
564                                 {
565                                         if (max < f->profile_total + f->builtinsprofile_total + f->callcount)
566                                         {
567                                                 max = f->profile_total + f->builtinsprofile_total + f->callcount;
568                                                 best = f;
569                                         }
570                                 }
571                                 else
572                                 {
573                                         if (max < f->profile + f->builtinsprofile + f->callcount)
574                                         {
575                                                 max = f->profile + f->builtinsprofile + f->callcount;
576                                                 best = f;
577                                         }
578                                 }
579                         }
580                 }
581                 if (best)
582                 {
583                         if (num < maxfunctions && max > mintime)
584                         {
585                                 if(prvm_timeprofiling.integer)
586                                 {
587                                         if (best->first_statement < 0)
588                                                 Con_Printf("%11.0f %11.6f ------------- builtin ------------- %11.6f ----------- builtin ----------- %s\n", best->callcount, best->tprofile, best->tprofile, PRVM_GetString(prog, best->s_name));
589                                         //                 %11.6f 12345678901 12345678901 12345678901 %11.6f 12345678901 12345678901 123.45%
590                                         else
591                                                 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));
592                                 }
593                                 else
594                                 {
595                                         if (best->first_statement < 0)
596                                                 Con_Printf("%11.0f ----------------------- builtin ----------------------- %s\n", best->callcount, PRVM_GetString(prog, best->s_name));
597                                         //                 12345678901 12345678901 12345678901 12345678901 123.45%
598                                         else
599                                                 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));
600                                 }
601                         }
602                         num++;
603                         best->profile = 0;
604                         best->tprofile = 0;
605                         best->tbprofile = 0;
606                         best->builtinsprofile = 0;
607                         best->profile_total = 0;
608                         best->tprofile_total = 0;
609                         best->builtinsprofile_total = 0;
610                         best->callcount = 0;
611                 }
612         } while (best);
613 }
614
615 /*
616 ============
617 PRVM_CallProfile_f
618
619 ============
620 */
621 void PRVM_CallProfile_f(cmd_state_t *cmd)
622 {
623         prvm_prog_t *prog;
624         if (Cmd_Argc(cmd) != 2)
625         {
626                 Con_Print("prvm_callprofile <program name>\n");
627                 return;
628         }
629
630         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
631                 return;
632
633         PRVM_CallProfile(prog);
634 }
635
636 /*
637 ============
638 PRVM_Profile_f
639
640 ============
641 */
642 void PRVM_Profile_f(cmd_state_t *cmd)
643 {
644         prvm_prog_t *prog;
645         int howmany;
646
647         if (prvm_coverage.integer & 1)
648         {
649                 Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
650                 return;
651         }
652
653         howmany = 1<<30;
654         if (Cmd_Argc(cmd) == 3)
655                 howmany = atoi(Cmd_Argv(cmd, 2));
656         else if (Cmd_Argc(cmd) != 2)
657         {
658                 Con_Print("prvm_profile <program name>\n");
659                 return;
660         }
661
662         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
663                 return;
664
665         PRVM_Profile(prog, howmany, 0, 0);
666 }
667
668 void PRVM_ChildProfile_f(cmd_state_t *cmd)
669 {
670         prvm_prog_t *prog;
671         int howmany;
672
673         if (prvm_coverage.integer & 1)
674         {
675                 Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
676                 return;
677         }
678
679         howmany = 1<<30;
680         if (Cmd_Argc(cmd) == 3)
681                 howmany = atoi(Cmd_Argv(cmd, 2));
682         else if (Cmd_Argc(cmd) != 2)
683         {
684                 Con_Print("prvm_childprofile <program name>\n");
685                 return;
686         }
687
688         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
689                 return;
690
691         PRVM_Profile(prog, howmany, 0, 1);
692 }
693
694 void PRVM_PrintState(prvm_prog_t *prog, int stack_index)
695 {
696         int i;
697         mfunction_t *func = prog->xfunction;
698         int st = prog->xstatement;
699         if (stack_index > 0 && stack_index <= prog->depth)
700         {
701                 func = prog->stack[prog->depth - stack_index].f;
702                 st = prog->stack[prog->depth - stack_index].s;
703         }
704         if (prog->statestring)
705         {
706                 Con_Printf("Caller-provided information: %s\n", prog->statestring);
707         }
708         if (func)
709         {
710                 for (i = -7; i <= 0;i++)
711                         if (st + i >= func->first_statement)
712                                 PRVM_PrintStatement(prog, prog->statements + st + i);
713         }
714         PRVM_StackTrace(prog);
715 }
716
717 extern cvar_t prvm_errordump;
718 void PRVM_Crash(prvm_prog_t *prog)
719 {
720         char vabuf[1024];
721         int outfd = sys.outfd;
722
723         cl.csqc_loaded = false;
724
725         if (prog == NULL)
726                 return;
727         if (!prog->loaded)
728                 return;
729
730         // set output to stderr
731         sys.outfd = fileno(stderr);
732
733         PRVM_serverfunction(SV_Shutdown) = 0; // don't call SV_Shutdown on crash
734
735         if( prog->depth > 0 )
736         {
737                 Con_Printf("QuakeC crash report for %s:\n", prog->name);
738                 PRVM_PrintState(prog, 0);
739         }
740
741         if(prvm_errordump.integer)
742         {
743                 // make a savegame
744                 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
745         }
746
747         // dump the stack so host_error can shutdown functions
748         prog->depth = 0;
749         prog->localstack_used = 0;
750
751         // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
752         prog->tempstringsbuf.cursize = 0;
753
754         // reset the prog pointer
755         prog = NULL;
756
757         // restore configured outfd
758         sys.outfd = outfd;
759 }
760
761 /*
762 ============================================================================
763 PRVM_ExecuteProgram
764
765 The interpretation main loop
766 ============================================================================
767 */
768
769 /*
770 ====================
771 PRVM_EnterFunction
772
773 Returns the new program statement counter
774 ====================
775 */
776 static int PRVM_EnterFunction (prvm_prog_t *prog, mfunction_t *f)
777 {
778         int             i, j, c, o;
779
780         if (!f)
781                 prog->error_cmd("PRVM_EnterFunction: NULL function in %s", prog->name);
782
783         prog->stack[prog->depth].s = prog->xstatement;
784         prog->stack[prog->depth].f = prog->xfunction;
785         prog->stack[prog->depth].profile_acc = -f->profile;
786         prog->stack[prog->depth].tprofile_acc = -f->tprofile + -f->tbprofile;
787         prog->stack[prog->depth].builtinsprofile_acc = -f->builtinsprofile;
788         prog->depth++;
789         if (prog->depth >=PRVM_MAX_STACK_DEPTH)
790                 prog->error_cmd("stack overflow");
791
792 // save off any locals that the new function steps on
793         c = f->locals;
794         if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
795                 prog->error_cmd("PRVM_ExecuteProgram: locals stack overflow in %s", prog->name);
796
797         for (i=0 ; i < c ; i++)
798                 prog->localstack[prog->localstack_used+i] = prog->globals.ip[f->parm_start + i];
799         prog->localstack_used += c;
800
801 // copy parameters
802         o = f->parm_start;
803         for (i=0 ; i<f->numparms ; i++)
804         {
805                 for (j=0 ; j<f->parm_size[i] ; j++)
806                 {
807                         prog->globals.ip[o] = prog->globals.ip[OFS_PARM0+i*3+j];
808                         o++;
809                 }
810         }
811
812         ++f->recursion;
813         prog->xfunction = f;
814         return f->first_statement - 1;  // offset the s++
815 }
816
817 /*
818 ====================
819 PRVM_LeaveFunction
820 ====================
821 */
822 static int PRVM_LeaveFunction (prvm_prog_t *prog)
823 {
824         int             i, c;
825         mfunction_t *f;
826
827         if (prog->depth <= 0)
828                 prog->error_cmd("prog stack underflow in %s", prog->name);
829
830         if (!prog->xfunction)
831                 prog->error_cmd("PR_LeaveFunction: NULL function in %s", prog->name);
832 // restore locals from the stack
833         c = prog->xfunction->locals;
834         prog->localstack_used -= c;
835         if (prog->localstack_used < 0)
836                 prog->error_cmd("PRVM_ExecuteProgram: locals stack underflow in %s", prog->name);
837
838         for (i=0 ; i < c ; i++)
839                 prog->globals.ip[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
840
841 // up stack
842         prog->depth--;
843         f = prog->xfunction;
844         --f->recursion;
845         prog->xfunction = prog->stack[prog->depth].f;
846         prog->stack[prog->depth].profile_acc += f->profile;
847         prog->stack[prog->depth].tprofile_acc += f->tprofile + f->tbprofile;
848         prog->stack[prog->depth].builtinsprofile_acc += f->builtinsprofile;
849         if(prog->depth > 0)
850         {
851                 prog->stack[prog->depth-1].profile_acc += prog->stack[prog->depth].profile_acc;
852                 prog->stack[prog->depth-1].tprofile_acc += prog->stack[prog->depth].tprofile_acc;
853                 prog->stack[prog->depth-1].builtinsprofile_acc += prog->stack[prog->depth].builtinsprofile_acc;
854         }
855         if(!f->recursion)
856         {
857                 // if f is already on the call stack...
858                 // we cannot add this profile data to it now
859                 // or we would add it more than once
860                 // so, let's only add to the function's profile if it is the outermost call
861                 f->profile_total += prog->stack[prog->depth].profile_acc;
862                 f->tprofile_total += prog->stack[prog->depth].tprofile_acc;
863                 f->builtinsprofile_total += prog->stack[prog->depth].builtinsprofile_acc;
864         }
865         
866         return prog->stack[prog->depth].s;
867 }
868
869 void PRVM_Init_Exec(prvm_prog_t *prog)
870 {
871         // dump the stack
872         prog->depth = 0;
873         prog->localstack_used = 0;
874         // reset the string table
875         // nothing here yet
876 }
877
878 /*
879 ==================
880 Coverage
881 ==================
882 */
883 // Note: in these two calls, prog->xfunction is assumed to be sane.
884 static const char *PRVM_WhereAmI(char *buf, size_t bufsize, prvm_prog_t *prog, mfunction_t *func, int statement)
885 {
886         if (prog->statement_linenums)
887         {
888                 if (prog->statement_columnnums)
889                         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);
890                 else
891                         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);
892         }
893         else
894                 return va(buf, bufsize, "%s(%s, %i)", PRVM_GetString(prog, func->s_file), PRVM_GetString(prog, func->s_name), statement - func->first_statement);
895 }
896 static void PRVM_FunctionCoverageEvent(prvm_prog_t *prog, mfunction_t *func)
897 {
898         ++prog->functions_covered;
899         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);
900 }
901 void PRVM_ExplicitCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
902 {
903         char vabuf[128];
904         ++prog->explicit_covered;
905         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);
906 }
907 static void PRVM_StatementCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
908 {
909         char vabuf[128];
910         ++prog->statements_covered;
911         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);
912 }
913
914 #if defined (__GNUC__) || (__clang__) || (__TINYC__)
915 #  ifndef CONFIG_PEDANTIC
916 #  define HAVE_COMPUTED_GOTOS 1
917 #  endif
918 #endif
919
920 #define OPA ((prvm_eval_t *)&globals[st->operand[0]])
921 #define OPB ((prvm_eval_t *)&globals[st->operand[1]])
922 #define OPC ((prvm_eval_t *)&globals[st->operand[2]])
923 extern cvar_t prvm_traceqc;
924 extern cvar_t prvm_statementprofiling;
925 extern qbool prvm_runawaycheck;
926
927 #ifdef PROFILING
928 #ifdef CONFIG_MENU
929 /*
930 ====================
931 MVM_ExecuteProgram
932 ====================
933 */
934 void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
935 {
936         mstatement_t    *st, *startst;
937         mfunction_t             *func, *enterfunc;
938         prvm_edict_t    *ed;
939         prvm_eval_t     *ptr;
940         int             jumpcount, cachedpr_trace, exitdepth;
941         int             restorevm_tempstringsbuf_cursize;
942         double  calltime;
943         double tm, starttm;
944         prvm_vec_t tempfloat;
945         // these may become out of date when a builtin is called, and are updated accordingly
946         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
947         unsigned int cached_entityfields = prog->entityfields;
948         unsigned int cached_entityfields_3 = prog->entityfields - 3;
949         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
950         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
951         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
952         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
953         unsigned int cached_max_edicts = prog->max_edicts;
954         // these do not change
955         mstatement_t *cached_statements = prog->statements;
956         qbool cached_allowworldwrites = prog->allowworldwrites;
957         unsigned int cached_flag = prog->flag;
958
959         prvm_vec_t *globals = prog->globals.fp;
960
961         calltime = Sys_DirtyTime();
962
963         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
964         {
965                 if (PRVM_allglobaledict(self))
966                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
967                 prog->error_cmd("MVM_ExecuteProgram: %s", errormessage);
968         }
969
970         func = &prog->functions[fnum];
971
972         // after executing this function, delete all tempstrings it created
973         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
974
975         prog->trace = prvm_traceqc.integer;
976
977         // we know we're done when pr_depth drops to this
978         exitdepth = prog->depth;
979
980 // make a stack frame
981         st = &prog->statements[PRVM_EnterFunction(prog, func)];
982         // save the starting statement pointer for profiling
983         // (when the function exits or jumps, the (st - startst) integer value is
984         // added to the function's profile counter)
985         startst = st;
986         starttm = calltime;
987         // instead of counting instructions, we count jumps
988         jumpcount = 0;
989         // add one to the callcount of this function because otherwise engine-called functions aren't counted
990         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
991                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
992
993 chooseexecprogram:
994         cachedpr_trace = prog->trace;
995         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
996         {
997 #define PRVMSLOWINTERPRETER 1
998                 if (prvm_timeprofiling.integer)
999                 {
1000 #define PRVMTIMEPROFILING 1
1001 #include "prvm_execprogram.h"
1002 #undef PRVMTIMEPROFILING
1003                 }
1004                 else
1005                 {
1006 #include "prvm_execprogram.h"
1007                 }
1008 #undef PRVMSLOWINTERPRETER
1009         }
1010         else
1011         {
1012                 if (prvm_timeprofiling.integer)
1013                 {
1014 #define PRVMTIMEPROFILING 1
1015 #include "prvm_execprogram.h"
1016 #undef PRVMTIMEPROFILING
1017                 }
1018                 else
1019                 {
1020 #include "prvm_execprogram.h"
1021                 }
1022         }
1023
1024 cleanup:
1025         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1026                 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);
1027         // delete tempstrings created by this function
1028         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1029
1030         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1031         func->totaltime += tm;
1032
1033         if (prog == SVVM_prog)
1034                 SV_FlushBroadcastMessages();
1035 }
1036 #endif
1037
1038 /*
1039 ====================
1040 CLVM_ExecuteProgram
1041 ====================
1042 */
1043 void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1044 {
1045         mstatement_t    *st, *startst;
1046         mfunction_t             *func, *enterfunc;
1047         prvm_edict_t    *ed;
1048         prvm_eval_t     *ptr;
1049         int             jumpcount, cachedpr_trace, exitdepth;
1050         int             restorevm_tempstringsbuf_cursize;
1051         double  calltime;
1052         double tm, starttm;
1053         prvm_vec_t tempfloat;
1054         // these may become out of date when a builtin is called, and are updated accordingly
1055         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
1056         unsigned int cached_entityfields = prog->entityfields;
1057         unsigned int cached_entityfields_3 = prog->entityfields - 3;
1058         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
1059         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
1060         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
1061         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
1062         unsigned int cached_max_edicts = prog->max_edicts;
1063         // these do not change
1064         mstatement_t *cached_statements = prog->statements;
1065         qbool cached_allowworldwrites = prog->allowworldwrites;
1066         unsigned int cached_flag = prog->flag;
1067
1068         prvm_vec_t *globals = prog->globals.fp;
1069
1070         calltime = Sys_DirtyTime();
1071
1072         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
1073         {
1074                 if (PRVM_allglobaledict(self))
1075                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
1076                 prog->error_cmd("CLVM_ExecuteProgram: %s", errormessage);
1077         }
1078
1079         func = &prog->functions[fnum];
1080
1081         // after executing this function, delete all tempstrings it created
1082         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1083
1084         prog->trace = prvm_traceqc.integer;
1085
1086         // we know we're done when pr_depth drops to this
1087         exitdepth = prog->depth;
1088
1089 // make a stack frame
1090         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1091         // save the starting statement pointer for profiling
1092         // (when the function exits or jumps, the (st - startst) integer value is
1093         // added to the function's profile counter)
1094         startst = st;
1095         starttm = calltime;
1096         // instead of counting instructions, we count jumps
1097         jumpcount = 0;
1098         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1099         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1100                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1101
1102 chooseexecprogram:
1103         cachedpr_trace = prog->trace;
1104         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1105         {
1106 #define PRVMSLOWINTERPRETER 1
1107                 if (prvm_timeprofiling.integer)
1108                 {
1109 #define PRVMTIMEPROFILING 1
1110 #include "prvm_execprogram.h"
1111 #undef PRVMTIMEPROFILING
1112                 }
1113                 else
1114                 {
1115 #include "prvm_execprogram.h"
1116                 }
1117 #undef PRVMSLOWINTERPRETER
1118         }
1119         else
1120         {
1121                 if (prvm_timeprofiling.integer)
1122                 {
1123 #define PRVMTIMEPROFILING 1
1124 #include "prvm_execprogram.h"
1125 #undef PRVMTIMEPROFILING
1126                 }
1127                 else
1128                 {
1129 #include "prvm_execprogram.h"
1130                 }
1131         }
1132
1133 cleanup:
1134         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1135                 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);
1136         // delete tempstrings created by this function
1137         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1138
1139         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1140         func->totaltime += tm;
1141
1142         if (prog == SVVM_prog)
1143                 SV_FlushBroadcastMessages();
1144 }
1145 #endif
1146
1147 /*
1148 ====================
1149 SVVM_ExecuteProgram
1150 ====================
1151 */
1152 #ifdef PROFILING
1153 void SVVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1154 #else
1155 void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1156 #endif
1157 {
1158         mstatement_t    *st, *startst;
1159         mfunction_t             *func, *enterfunc;
1160         prvm_edict_t    *ed;
1161         prvm_eval_t     *ptr;
1162         int             jumpcount, cachedpr_trace, exitdepth;
1163         int             restorevm_tempstringsbuf_cursize;
1164         double  calltime;
1165         double tm, starttm;
1166         prvm_vec_t tempfloat;
1167         // these may become out of date when a builtin is called, and are updated accordingly
1168         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
1169         unsigned int cached_entityfields = prog->entityfields;
1170         unsigned int cached_entityfields_3 = prog->entityfields - 3;
1171         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
1172         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
1173         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
1174         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
1175         unsigned int cached_max_edicts = prog->max_edicts;
1176         // these do not change
1177         mstatement_t *cached_statements = prog->statements;
1178         qbool cached_allowworldwrites = prog->allowworldwrites;
1179         unsigned int cached_flag = prog->flag;
1180
1181         prvm_vec_t *globals = prog->globals.fp;
1182
1183         calltime = Sys_DirtyTime();
1184
1185         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
1186         {
1187                 if (PRVM_allglobaledict(self))
1188                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
1189                 prog->error_cmd("SVVM_ExecuteProgram: %s", errormessage);
1190         }
1191
1192         func = &prog->functions[fnum];
1193
1194         // after executing this function, delete all tempstrings it created
1195         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1196
1197         prog->trace = prvm_traceqc.integer;
1198
1199         // we know we're done when pr_depth drops to this
1200         exitdepth = prog->depth;
1201
1202 // make a stack frame
1203         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1204         // save the starting statement pointer for profiling
1205         // (when the function exits or jumps, the (st - startst) integer value is
1206         // added to the function's profile counter)
1207         startst = st;
1208         starttm = calltime;
1209         // instead of counting instructions, we count jumps
1210         jumpcount = 0;
1211         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1212         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1213                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1214
1215 chooseexecprogram:
1216         cachedpr_trace = prog->trace;
1217         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1218         {
1219 #define PRVMSLOWINTERPRETER 1
1220                 if (prvm_timeprofiling.integer)
1221                 {
1222 #define PRVMTIMEPROFILING 1
1223 #include "prvm_execprogram.h"
1224 #undef PRVMTIMEPROFILING
1225                 }
1226                 else
1227                 {
1228 #include "prvm_execprogram.h"
1229                 }
1230 #undef PRVMSLOWINTERPRETER
1231         }
1232         else
1233         {
1234                 if (prvm_timeprofiling.integer)
1235                 {
1236 #define PRVMTIMEPROFILING 1
1237 #include "prvm_execprogram.h"
1238 #undef PRVMTIMEPROFILING
1239                 }
1240                 else
1241                 {
1242 #include "prvm_execprogram.h"
1243                 }
1244         }
1245
1246 cleanup:
1247         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1248                 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);
1249         // delete tempstrings created by this function
1250         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1251
1252         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1253         func->totaltime += tm;
1254
1255         if (prog == SVVM_prog)
1256                 SV_FlushBroadcastMessages();
1257 }