]> git.xonotic.org Git - xonotic/darkplaces.git/blob - prvm_exec.c
sys: write significant engine and QC errors to stderr
[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
438         if(prog)
439         {
440                 dpsnprintf(buf, bufsize, "(%s) ", prog->name);
441         }
442         else
443         {
444                 strlcpy(buf, "<NO PROG>", bufsize);
445                 return;
446         }
447
448         prog->stack[prog->depth].s = prog->xstatement;
449         prog->stack[prog->depth].f = prog->xfunction;
450         for (i = prog->depth;i > 0;i--)
451         {
452                 f = prog->stack[i].f;
453
454                 if(strlcat(buf,
455                         f
456                                 ? 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)
457                                 : "<NULL> ",
458                         bufsize
459                 ) >= bufsize)
460                         break;
461         }
462 }
463
464
465 static void PRVM_CallProfile (prvm_prog_t *prog)
466 {
467         mfunction_t *f, *best;
468         int i;
469         double max;
470         double sum;
471         double newprofiletime;
472
473         Con_Printf( "%s Call Profile:\n", prog->name );
474
475         sum = 0;
476         do
477         {
478                 max = 0;
479                 best = NULL;
480                 for (i=0 ; i<prog->numfunctions ; i++)
481                 {
482                         f = &prog->functions[i];
483                         if (max < f->totaltime)
484                         {
485                                 max = f->totaltime;
486                                 best = f;
487                         }
488                 }
489                 if (best)
490                 {
491                         sum += best->totaltime;
492                         Con_Printf("%9.4f %s\n", best->totaltime, PRVM_GetString(prog, best->s_name));
493                         best->totaltime = 0;
494                 }
495         } while (best);
496
497         newprofiletime = Sys_DirtyTime();
498         Con_Printf("Total time since last profile reset: %9.4f\n", newprofiletime - prog->profiletime);
499         Con_Printf("       - used by QC code of this VM: %9.4f\n", sum);
500
501         prog->profiletime = newprofiletime;
502 }
503
504 void PRVM_Profile (prvm_prog_t *prog, int maxfunctions, double mintime, int sortby)
505 {
506         mfunction_t *f, *best;
507         int i, num;
508         double max;
509
510         if(!prvm_timeprofiling.integer)
511                 mintime *= 10000000; // count each statement as about 0.1µs
512
513         if(prvm_timeprofiling.integer)
514                 Con_Printf( "%s Profile:\n[CallCount]      [Time] [BuiltinTm] [Statement] [BuiltinCt] [TimeTotal] [StmtTotal] [BltnTotal] [self]\n", prog->name );
515                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
516         else
517                 Con_Printf( "%s Profile:\n[CallCount] [Statement] [BuiltinCt] [StmtTotal] [BltnTotal] [self]\n", prog->name );
518                 //                        12345678901 12345678901 12345678901 12345678901 12345678901 123.45%
519
520         num = 0;
521         do
522         {
523                 max = 0;
524                 best = NULL;
525                 for (i=0 ; i<prog->numfunctions ; i++)
526                 {
527                         f = &prog->functions[i];
528                         if(prvm_timeprofiling.integer)
529                         {
530                                 if(sortby)
531                                 {
532                                         if(f->first_statement < 0)
533                                         {
534                                                 if (max < f->tprofile)
535                                                 {
536                                                         max = f->tprofile;
537                                                         best = f;
538                                                 }
539                                         }
540                                         else
541                                         {
542                                                 if (max < f->tprofile_total)
543                                                 {
544                                                         max = f->tprofile_total;
545                                                         best = f;
546                                                 }
547                                         }
548                                 }
549                                 else
550                                 {
551                                         if (max < f->tprofile + f->tbprofile)
552                                         {
553                                                 max = f->tprofile + f->tbprofile;
554                                                 best = f;
555                                         }
556                                 }
557                         }
558                         else
559                         {
560                                 if(sortby)
561                                 {
562                                         if (max < f->profile_total + f->builtinsprofile_total + f->callcount)
563                                         {
564                                                 max = f->profile_total + f->builtinsprofile_total + f->callcount;
565                                                 best = f;
566                                         }
567                                 }
568                                 else
569                                 {
570                                         if (max < f->profile + f->builtinsprofile + f->callcount)
571                                         {
572                                                 max = f->profile + f->builtinsprofile + f->callcount;
573                                                 best = f;
574                                         }
575                                 }
576                         }
577                 }
578                 if (best)
579                 {
580                         if (num < maxfunctions && max > mintime)
581                         {
582                                 if(prvm_timeprofiling.integer)
583                                 {
584                                         if (best->first_statement < 0)
585                                                 Con_Printf("%11.0f %11.6f ------------- builtin ------------- %11.6f ----------- builtin ----------- %s\n", best->callcount, best->tprofile, best->tprofile, PRVM_GetString(prog, best->s_name));
586                                         //                 %11.6f 12345678901 12345678901 12345678901 %11.6f 12345678901 12345678901 123.45%
587                                         else
588                                                 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));
589                                 }
590                                 else
591                                 {
592                                         if (best->first_statement < 0)
593                                                 Con_Printf("%11.0f ----------------------- builtin ----------------------- %s\n", best->callcount, PRVM_GetString(prog, best->s_name));
594                                         //                 12345678901 12345678901 12345678901 12345678901 123.45%
595                                         else
596                                                 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));
597                                 }
598                         }
599                         num++;
600                         best->profile = 0;
601                         best->tprofile = 0;
602                         best->tbprofile = 0;
603                         best->builtinsprofile = 0;
604                         best->profile_total = 0;
605                         best->tprofile_total = 0;
606                         best->builtinsprofile_total = 0;
607                         best->callcount = 0;
608                 }
609         } while (best);
610 }
611
612 /*
613 ============
614 PRVM_CallProfile_f
615
616 ============
617 */
618 void PRVM_CallProfile_f(cmd_state_t *cmd)
619 {
620         prvm_prog_t *prog;
621         if (Cmd_Argc(cmd) != 2)
622         {
623                 Con_Print("prvm_callprofile <program name>\n");
624                 return;
625         }
626
627         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
628                 return;
629
630         PRVM_CallProfile(prog);
631 }
632
633 /*
634 ============
635 PRVM_Profile_f
636
637 ============
638 */
639 void PRVM_Profile_f(cmd_state_t *cmd)
640 {
641         prvm_prog_t *prog;
642         int howmany;
643
644         if (prvm_coverage.integer & 1)
645         {
646                 Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
647                 return;
648         }
649
650         howmany = 1<<30;
651         if (Cmd_Argc(cmd) == 3)
652                 howmany = atoi(Cmd_Argv(cmd, 2));
653         else if (Cmd_Argc(cmd) != 2)
654         {
655                 Con_Print("prvm_profile <program name>\n");
656                 return;
657         }
658
659         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
660                 return;
661
662         PRVM_Profile(prog, howmany, 0, 0);
663 }
664
665 void PRVM_ChildProfile_f(cmd_state_t *cmd)
666 {
667         prvm_prog_t *prog;
668         int howmany;
669
670         if (prvm_coverage.integer & 1)
671         {
672                 Con_Printf("Collecting function coverage, cannot profile - sorry!\n");
673                 return;
674         }
675
676         howmany = 1<<30;
677         if (Cmd_Argc(cmd) == 3)
678                 howmany = atoi(Cmd_Argv(cmd, 2));
679         else if (Cmd_Argc(cmd) != 2)
680         {
681                 Con_Print("prvm_childprofile <program name>\n");
682                 return;
683         }
684
685         if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(cmd, 1))))
686                 return;
687
688         PRVM_Profile(prog, howmany, 0, 1);
689 }
690
691 void PRVM_PrintState(prvm_prog_t *prog, int stack_index)
692 {
693         int i;
694         mfunction_t *func = prog->xfunction;
695         int st = prog->xstatement;
696         if (stack_index > 0 && stack_index <= prog->depth)
697         {
698                 func = prog->stack[prog->depth - stack_index].f;
699                 st = prog->stack[prog->depth - stack_index].s;
700         }
701         if (prog->statestring)
702         {
703                 Con_Printf("Caller-provided information: %s\n", prog->statestring);
704         }
705         if (func)
706         {
707                 for (i = -7; i <= 0;i++)
708                         if (st + i >= func->first_statement)
709                                 PRVM_PrintStatement(prog, prog->statements + st + i);
710         }
711         PRVM_StackTrace(prog);
712 }
713
714 extern cvar_t prvm_errordump;
715 void PRVM_Crash(prvm_prog_t *prog)
716 {
717         char vabuf[1024];
718         int outfd = sys.outfd;
719
720         cl.csqc_loaded = false;
721
722         if (prog == NULL)
723                 return;
724         if (!prog->loaded)
725                 return;
726
727         // set output to stderr
728         sys.outfd = fileno(stderr);
729
730         PRVM_serverfunction(SV_Shutdown) = 0; // don't call SV_Shutdown on crash
731
732         if( prog->depth > 0 )
733         {
734                 Con_Printf("QuakeC crash report for %s:\n", prog->name);
735                 PRVM_PrintState(prog, 0);
736         }
737
738         if(prvm_errordump.integer)
739         {
740                 // make a savegame
741                 SV_Savegame_to(prog, va(vabuf, sizeof(vabuf), "crash-%s.dmp", prog->name));
742         }
743
744         // dump the stack so host_error can shutdown functions
745         prog->depth = 0;
746         prog->localstack_used = 0;
747
748         // delete all tempstrings (FIXME: is this safe in VM->engine->VM recursion?)
749         prog->tempstringsbuf.cursize = 0;
750
751         // reset the prog pointer
752         prog = NULL;
753
754         // restore configured outfd
755         sys.outfd = outfd;
756 }
757
758 /*
759 ============================================================================
760 PRVM_ExecuteProgram
761
762 The interpretation main loop
763 ============================================================================
764 */
765
766 /*
767 ====================
768 PRVM_EnterFunction
769
770 Returns the new program statement counter
771 ====================
772 */
773 static int PRVM_EnterFunction (prvm_prog_t *prog, mfunction_t *f)
774 {
775         int             i, j, c, o;
776
777         if (!f)
778                 prog->error_cmd("PRVM_EnterFunction: NULL function in %s", prog->name);
779
780         prog->stack[prog->depth].s = prog->xstatement;
781         prog->stack[prog->depth].f = prog->xfunction;
782         prog->stack[prog->depth].profile_acc = -f->profile;
783         prog->stack[prog->depth].tprofile_acc = -f->tprofile + -f->tbprofile;
784         prog->stack[prog->depth].builtinsprofile_acc = -f->builtinsprofile;
785         prog->depth++;
786         if (prog->depth >=PRVM_MAX_STACK_DEPTH)
787                 prog->error_cmd("stack overflow");
788
789 // save off any locals that the new function steps on
790         c = f->locals;
791         if (prog->localstack_used + c > PRVM_LOCALSTACK_SIZE)
792                 prog->error_cmd("PRVM_ExecuteProgram: locals stack overflow in %s", prog->name);
793
794         for (i=0 ; i < c ; i++)
795                 prog->localstack[prog->localstack_used+i] = prog->globals.ip[f->parm_start + i];
796         prog->localstack_used += c;
797
798 // copy parameters
799         o = f->parm_start;
800         for (i=0 ; i<f->numparms ; i++)
801         {
802                 for (j=0 ; j<f->parm_size[i] ; j++)
803                 {
804                         prog->globals.ip[o] = prog->globals.ip[OFS_PARM0+i*3+j];
805                         o++;
806                 }
807         }
808
809         ++f->recursion;
810         prog->xfunction = f;
811         return f->first_statement - 1;  // offset the s++
812 }
813
814 /*
815 ====================
816 PRVM_LeaveFunction
817 ====================
818 */
819 static int PRVM_LeaveFunction (prvm_prog_t *prog)
820 {
821         int             i, c;
822         mfunction_t *f;
823
824         if (prog->depth <= 0)
825                 prog->error_cmd("prog stack underflow in %s", prog->name);
826
827         if (!prog->xfunction)
828                 prog->error_cmd("PR_LeaveFunction: NULL function in %s", prog->name);
829 // restore locals from the stack
830         c = prog->xfunction->locals;
831         prog->localstack_used -= c;
832         if (prog->localstack_used < 0)
833                 prog->error_cmd("PRVM_ExecuteProgram: locals stack underflow in %s", prog->name);
834
835         for (i=0 ; i < c ; i++)
836                 prog->globals.ip[prog->xfunction->parm_start + i] = prog->localstack[prog->localstack_used+i];
837
838 // up stack
839         prog->depth--;
840         f = prog->xfunction;
841         --f->recursion;
842         prog->xfunction = prog->stack[prog->depth].f;
843         prog->stack[prog->depth].profile_acc += f->profile;
844         prog->stack[prog->depth].tprofile_acc += f->tprofile + f->tbprofile;
845         prog->stack[prog->depth].builtinsprofile_acc += f->builtinsprofile;
846         if(prog->depth > 0)
847         {
848                 prog->stack[prog->depth-1].profile_acc += prog->stack[prog->depth].profile_acc;
849                 prog->stack[prog->depth-1].tprofile_acc += prog->stack[prog->depth].tprofile_acc;
850                 prog->stack[prog->depth-1].builtinsprofile_acc += prog->stack[prog->depth].builtinsprofile_acc;
851         }
852         if(!f->recursion)
853         {
854                 // if f is already on the call stack...
855                 // we cannot add this profile data to it now
856                 // or we would add it more than once
857                 // so, let's only add to the function's profile if it is the outermost call
858                 f->profile_total += prog->stack[prog->depth].profile_acc;
859                 f->tprofile_total += prog->stack[prog->depth].tprofile_acc;
860                 f->builtinsprofile_total += prog->stack[prog->depth].builtinsprofile_acc;
861         }
862         
863         return prog->stack[prog->depth].s;
864 }
865
866 void PRVM_Init_Exec(prvm_prog_t *prog)
867 {
868         // dump the stack
869         prog->depth = 0;
870         prog->localstack_used = 0;
871         // reset the string table
872         // nothing here yet
873 }
874
875 /*
876 ==================
877 Coverage
878 ==================
879 */
880 // Note: in these two calls, prog->xfunction is assumed to be sane.
881 static const char *PRVM_WhereAmI(char *buf, size_t bufsize, prvm_prog_t *prog, mfunction_t *func, int statement)
882 {
883         if (prog->statement_linenums)
884         {
885                 if (prog->statement_columnnums)
886                         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);
887                 else
888                         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);
889         }
890         else
891                 return va(buf, bufsize, "%s(%s, %i)", PRVM_GetString(prog, func->s_file), PRVM_GetString(prog, func->s_name), statement - func->first_statement);
892 }
893 static void PRVM_FunctionCoverageEvent(prvm_prog_t *prog, mfunction_t *func)
894 {
895         ++prog->functions_covered;
896         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);
897 }
898 void PRVM_ExplicitCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
899 {
900         char vabuf[128];
901         ++prog->explicit_covered;
902         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);
903 }
904 static void PRVM_StatementCoverageEvent(prvm_prog_t *prog, mfunction_t *func, int statement)
905 {
906         char vabuf[128];
907         ++prog->statements_covered;
908         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);
909 }
910
911 #if defined (__GNUC__) || (__clang__) || (__TINYC__)
912 #  ifndef CONFIG_PEDANTIC
913 #  define HAVE_COMPUTED_GOTOS 1
914 #  endif
915 #endif
916
917 #define OPA ((prvm_eval_t *)&globals[st->operand[0]])
918 #define OPB ((prvm_eval_t *)&globals[st->operand[1]])
919 #define OPC ((prvm_eval_t *)&globals[st->operand[2]])
920 extern cvar_t prvm_traceqc;
921 extern cvar_t prvm_statementprofiling;
922 extern qbool prvm_runawaycheck;
923
924 #ifdef PROFILING
925 #ifdef CONFIG_MENU
926 /*
927 ====================
928 MVM_ExecuteProgram
929 ====================
930 */
931 void MVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
932 {
933         mstatement_t    *st, *startst;
934         mfunction_t             *func, *enterfunc;
935         prvm_edict_t    *ed;
936         prvm_eval_t     *ptr;
937         int             jumpcount, cachedpr_trace, exitdepth;
938         int             restorevm_tempstringsbuf_cursize;
939         double  calltime;
940         double tm, starttm;
941         prvm_vec_t tempfloat;
942         // these may become out of date when a builtin is called, and are updated accordingly
943         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
944         unsigned int cached_entityfields = prog->entityfields;
945         unsigned int cached_entityfields_3 = prog->entityfields - 3;
946         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
947         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
948         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
949         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
950         unsigned int cached_max_edicts = prog->max_edicts;
951         // these do not change
952         mstatement_t *cached_statements = prog->statements;
953         qbool cached_allowworldwrites = prog->allowworldwrites;
954         unsigned int cached_flag = prog->flag;
955
956         prvm_vec_t *globals = prog->globals.fp;
957
958         calltime = Sys_DirtyTime();
959
960         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
961         {
962                 if (PRVM_allglobaledict(self))
963                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
964                 prog->error_cmd("MVM_ExecuteProgram: %s", errormessage);
965         }
966
967         func = &prog->functions[fnum];
968
969         // after executing this function, delete all tempstrings it created
970         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
971
972         prog->trace = prvm_traceqc.integer;
973
974         // we know we're done when pr_depth drops to this
975         exitdepth = prog->depth;
976
977 // make a stack frame
978         st = &prog->statements[PRVM_EnterFunction(prog, func)];
979         // save the starting statement pointer for profiling
980         // (when the function exits or jumps, the (st - startst) integer value is
981         // added to the function's profile counter)
982         startst = st;
983         starttm = calltime;
984         // instead of counting instructions, we count jumps
985         jumpcount = 0;
986         // add one to the callcount of this function because otherwise engine-called functions aren't counted
987         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
988                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
989
990 chooseexecprogram:
991         cachedpr_trace = prog->trace;
992         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
993         {
994 #define PRVMSLOWINTERPRETER 1
995                 if (prvm_timeprofiling.integer)
996                 {
997 #define PRVMTIMEPROFILING 1
998 #include "prvm_execprogram.h"
999 #undef PRVMTIMEPROFILING
1000                 }
1001                 else
1002                 {
1003 #include "prvm_execprogram.h"
1004                 }
1005 #undef PRVMSLOWINTERPRETER
1006         }
1007         else
1008         {
1009                 if (prvm_timeprofiling.integer)
1010                 {
1011 #define PRVMTIMEPROFILING 1
1012 #include "prvm_execprogram.h"
1013 #undef PRVMTIMEPROFILING
1014                 }
1015                 else
1016                 {
1017 #include "prvm_execprogram.h"
1018                 }
1019         }
1020
1021 cleanup:
1022         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1023                 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);
1024         // delete tempstrings created by this function
1025         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1026
1027         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1028         func->totaltime += tm;
1029
1030         if (prog == SVVM_prog)
1031                 SV_FlushBroadcastMessages();
1032 }
1033 #endif
1034
1035 /*
1036 ====================
1037 CLVM_ExecuteProgram
1038 ====================
1039 */
1040 void CLVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1041 {
1042         mstatement_t    *st, *startst;
1043         mfunction_t             *func, *enterfunc;
1044         prvm_edict_t    *ed;
1045         prvm_eval_t     *ptr;
1046         int             jumpcount, cachedpr_trace, exitdepth;
1047         int             restorevm_tempstringsbuf_cursize;
1048         double  calltime;
1049         double tm, starttm;
1050         prvm_vec_t tempfloat;
1051         // these may become out of date when a builtin is called, and are updated accordingly
1052         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
1053         unsigned int cached_entityfields = prog->entityfields;
1054         unsigned int cached_entityfields_3 = prog->entityfields - 3;
1055         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
1056         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
1057         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
1058         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
1059         unsigned int cached_max_edicts = prog->max_edicts;
1060         // these do not change
1061         mstatement_t *cached_statements = prog->statements;
1062         qbool cached_allowworldwrites = prog->allowworldwrites;
1063         unsigned int cached_flag = prog->flag;
1064
1065         prvm_vec_t *globals = prog->globals.fp;
1066
1067         calltime = Sys_DirtyTime();
1068
1069         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
1070         {
1071                 if (PRVM_allglobaledict(self))
1072                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
1073                 prog->error_cmd("CLVM_ExecuteProgram: %s", errormessage);
1074         }
1075
1076         func = &prog->functions[fnum];
1077
1078         // after executing this function, delete all tempstrings it created
1079         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1080
1081         prog->trace = prvm_traceqc.integer;
1082
1083         // we know we're done when pr_depth drops to this
1084         exitdepth = prog->depth;
1085
1086 // make a stack frame
1087         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1088         // save the starting statement pointer for profiling
1089         // (when the function exits or jumps, the (st - startst) integer value is
1090         // added to the function's profile counter)
1091         startst = st;
1092         starttm = calltime;
1093         // instead of counting instructions, we count jumps
1094         jumpcount = 0;
1095         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1096         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1097                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1098
1099 chooseexecprogram:
1100         cachedpr_trace = prog->trace;
1101         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1102         {
1103 #define PRVMSLOWINTERPRETER 1
1104                 if (prvm_timeprofiling.integer)
1105                 {
1106 #define PRVMTIMEPROFILING 1
1107 #include "prvm_execprogram.h"
1108 #undef PRVMTIMEPROFILING
1109                 }
1110                 else
1111                 {
1112 #include "prvm_execprogram.h"
1113                 }
1114 #undef PRVMSLOWINTERPRETER
1115         }
1116         else
1117         {
1118                 if (prvm_timeprofiling.integer)
1119                 {
1120 #define PRVMTIMEPROFILING 1
1121 #include "prvm_execprogram.h"
1122 #undef PRVMTIMEPROFILING
1123                 }
1124                 else
1125                 {
1126 #include "prvm_execprogram.h"
1127                 }
1128         }
1129
1130 cleanup:
1131         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1132                 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);
1133         // delete tempstrings created by this function
1134         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1135
1136         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1137         func->totaltime += tm;
1138
1139         if (prog == SVVM_prog)
1140                 SV_FlushBroadcastMessages();
1141 }
1142 #endif
1143
1144 /*
1145 ====================
1146 SVVM_ExecuteProgram
1147 ====================
1148 */
1149 #ifdef PROFILING
1150 void SVVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1151 #else
1152 void PRVM_ExecuteProgram (prvm_prog_t *prog, func_t fnum, const char *errormessage)
1153 #endif
1154 {
1155         mstatement_t    *st, *startst;
1156         mfunction_t             *func, *enterfunc;
1157         prvm_edict_t    *ed;
1158         prvm_eval_t     *ptr;
1159         int             jumpcount, cachedpr_trace, exitdepth;
1160         int             restorevm_tempstringsbuf_cursize;
1161         double  calltime;
1162         double tm, starttm;
1163         prvm_vec_t tempfloat;
1164         // these may become out of date when a builtin is called, and are updated accordingly
1165         prvm_vec_t *cached_edictsfields = prog->edictsfields.fp;
1166         unsigned int cached_entityfields = prog->entityfields;
1167         unsigned int cached_entityfields_3 = prog->entityfields - 3;
1168         unsigned int cached_entityfieldsarea = prog->entityfieldsarea;
1169         unsigned int cached_entityfieldsarea_entityfields = prog->entityfieldsarea - prog->entityfields;
1170         unsigned int cached_entityfieldsarea_3 = prog->entityfieldsarea - 3;
1171         unsigned int cached_entityfieldsarea_entityfields_3 = prog->entityfieldsarea - prog->entityfields - 3;
1172         unsigned int cached_max_edicts = prog->max_edicts;
1173         // these do not change
1174         mstatement_t *cached_statements = prog->statements;
1175         qbool cached_allowworldwrites = prog->allowworldwrites;
1176         unsigned int cached_flag = prog->flag;
1177
1178         prvm_vec_t *globals = prog->globals.fp;
1179
1180         calltime = Sys_DirtyTime();
1181
1182         if (!fnum || fnum >= (unsigned int)prog->numfunctions)
1183         {
1184                 if (PRVM_allglobaledict(self))
1185                         PRVM_ED_Print(prog, PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)), NULL);
1186                 prog->error_cmd("SVVM_ExecuteProgram: %s", errormessage);
1187         }
1188
1189         func = &prog->functions[fnum];
1190
1191         // after executing this function, delete all tempstrings it created
1192         restorevm_tempstringsbuf_cursize = prog->tempstringsbuf.cursize;
1193
1194         prog->trace = prvm_traceqc.integer;
1195
1196         // we know we're done when pr_depth drops to this
1197         exitdepth = prog->depth;
1198
1199 // make a stack frame
1200         st = &prog->statements[PRVM_EnterFunction(prog, func)];
1201         // save the starting statement pointer for profiling
1202         // (when the function exits or jumps, the (st - startst) integer value is
1203         // added to the function's profile counter)
1204         startst = st;
1205         starttm = calltime;
1206         // instead of counting instructions, we count jumps
1207         jumpcount = 0;
1208         // add one to the callcount of this function because otherwise engine-called functions aren't counted
1209         if (prog->xfunction->callcount++ == 0 && (prvm_coverage.integer & 1))
1210                 PRVM_FunctionCoverageEvent(prog, prog->xfunction);
1211
1212 chooseexecprogram:
1213         cachedpr_trace = prog->trace;
1214         if (prog->trace || prog->watch_global_type != ev_void || prog->watch_field_type != ev_void || prog->break_statement >= 0)
1215         {
1216 #define PRVMSLOWINTERPRETER 1
1217                 if (prvm_timeprofiling.integer)
1218                 {
1219 #define PRVMTIMEPROFILING 1
1220 #include "prvm_execprogram.h"
1221 #undef PRVMTIMEPROFILING
1222                 }
1223                 else
1224                 {
1225 #include "prvm_execprogram.h"
1226                 }
1227 #undef PRVMSLOWINTERPRETER
1228         }
1229         else
1230         {
1231                 if (prvm_timeprofiling.integer)
1232                 {
1233 #define PRVMTIMEPROFILING 1
1234 #include "prvm_execprogram.h"
1235 #undef PRVMTIMEPROFILING
1236                 }
1237                 else
1238                 {
1239 #include "prvm_execprogram.h"
1240                 }
1241         }
1242
1243 cleanup:
1244         if (developer_insane.integer && prog->tempstringsbuf.cursize > restorevm_tempstringsbuf_cursize)
1245                 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);
1246         // delete tempstrings created by this function
1247         prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1248
1249         tm = Sys_DirtyTime() - calltime;if (tm < 0 || tm >= 1800) tm = 0;
1250         func->totaltime += tm;
1251
1252         if (prog == SVVM_prog)
1253                 SV_FlushBroadcastMessages();
1254 }