Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

vm.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // vm.c -- virtual machine
00023 
00024 /*
00025 
00026 
00027 intermix code and data
00028 symbol table
00029 
00030 a dll has one imported function: VM_SystemCall
00031 and one exported function: Perform
00032 
00033 
00034 */
00035 
00036 #include "vm_local.h"
00037 
00038 
00039 vm_t    *currentVM = NULL; // bk001212
00040 vm_t    *lastVM    = NULL; // bk001212
00041 int     vm_debugLevel;
00042 
00043 #define MAX_VM      3
00044 vm_t    vmTable[MAX_VM];
00045 
00046 
00047 void VM_VmInfo_f( void );
00048 void VM_VmProfile_f( void );
00049 
00050 
00051 // converts a VM pointer to a C pointer and
00052 // checks to make sure that the range is acceptable
00053 void    *VM_VM2C( vmptr_t p, int length ) {
00054     return (void *)p;
00055 }
00056 
00057 void VM_Debug( int level ) {
00058     vm_debugLevel = level;
00059 }
00060 
00061 /*
00062 ==============
00063 VM_Init
00064 ==============
00065 */
00066 void VM_Init( void ) {
00067     Cvar_Get( "vm_cgame", "2", CVAR_ARCHIVE );  // !@# SHIP WITH SET TO 2
00068     Cvar_Get( "vm_game", "2", CVAR_ARCHIVE );   // !@# SHIP WITH SET TO 2
00069     Cvar_Get( "vm_ui", "2", CVAR_ARCHIVE );     // !@# SHIP WITH SET TO 2
00070 
00071     Cmd_AddCommand ("vmprofile", VM_VmProfile_f );
00072     Cmd_AddCommand ("vminfo", VM_VmInfo_f );
00073 
00074     Com_Memset( vmTable, 0, sizeof( vmTable ) );
00075 }
00076 
00077 
00078 /*
00079 ===============
00080 VM_ValueToSymbol
00081 
00082 Assumes a program counter value
00083 ===============
00084 */
00085 const char *VM_ValueToSymbol( vm_t *vm, int value ) {
00086     vmSymbol_t  *sym;
00087     static char     text[MAX_TOKEN_CHARS];
00088 
00089     sym = vm->symbols;
00090     if ( !sym ) {
00091         return "NO SYMBOLS";
00092     }
00093 
00094     // find the symbol
00095     while ( sym->next && sym->next->symValue <= value ) {
00096         sym = sym->next;
00097     }
00098 
00099     if ( value == sym->symValue ) {
00100         return sym->symName;
00101     }
00102 
00103     Com_sprintf( text, sizeof( text ), "%s+%i", sym->symName, value - sym->symValue );
00104 
00105     return text;
00106 }
00107 
00108 /*
00109 ===============
00110 VM_ValueToFunctionSymbol
00111 
00112 For profiling, find the symbol behind this value
00113 ===============
00114 */
00115 vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ) {
00116     vmSymbol_t  *sym;
00117     static vmSymbol_t   nullSym;
00118 
00119     sym = vm->symbols;
00120     if ( !sym ) {
00121         return &nullSym;
00122     }
00123 
00124     while ( sym->next && sym->next->symValue <= value ) {
00125         sym = sym->next;
00126     }
00127 
00128     return sym;
00129 }
00130 
00131 
00132 /*
00133 ===============
00134 VM_SymbolToValue
00135 ===============
00136 */
00137 int VM_SymbolToValue( vm_t *vm, const char *symbol ) {
00138     vmSymbol_t  *sym;
00139 
00140     for ( sym = vm->symbols ; sym ; sym = sym->next ) {
00141         if ( !strcmp( symbol, sym->symName ) ) {
00142             return sym->symValue;
00143         }
00144     }
00145     return 0;
00146 }
00147 
00148 
00149 /*
00150 =====================
00151 VM_SymbolForCompiledPointer
00152 =====================
00153 */
00154 const char *VM_SymbolForCompiledPointer( vm_t *vm, void *code ) {
00155     int         i;
00156 
00157     if ( code < (void *)vm->codeBase ) {
00158         return "Before code block";
00159     }
00160     if ( code >= (void *)(vm->codeBase + vm->codeLength) ) {
00161         return "After code block";
00162     }
00163 
00164     // find which original instruction it is after
00165     for ( i = 0 ; i < vm->codeLength ; i++ ) {
00166         if ( (void *)vm->instructionPointers[i] > code ) {
00167             break;
00168         }
00169     }
00170     i--;
00171 
00172     // now look up the bytecode instruction pointer
00173     return VM_ValueToSymbol( vm, i );
00174 }
00175 
00176 
00177 
00178 /*
00179 ===============
00180 ParseHex
00181 ===============
00182 */
00183 int ParseHex( const char *text ) {
00184     int     value;
00185     int     c;
00186 
00187     value = 0;
00188     while ( ( c = *text++ ) != 0 ) {
00189         if ( c >= '0' && c <= '9' ) {
00190             value = value * 16 + c - '0';
00191             continue;
00192         }
00193         if ( c >= 'a' && c <= 'f' ) {
00194             value = value * 16 + 10 + c - 'a';
00195             continue;
00196         }
00197         if ( c >= 'A' && c <= 'F' ) {
00198             value = value * 16 + 10 + c - 'A';
00199             continue;
00200         }
00201     }
00202 
00203     return value;
00204 }
00205 
00206 /*
00207 ===============
00208 VM_LoadSymbols
00209 ===============
00210 */
00211 void VM_LoadSymbols( vm_t *vm ) {
00212     int     len;
00213     char    *mapfile, *text_p, *token;
00214     char    name[MAX_QPATH];
00215     char    symbols[MAX_QPATH];
00216     vmSymbol_t  **prev, *sym;
00217     int     count;
00218     int     value;
00219     int     chars;
00220     int     segment;
00221     int     numInstructions;
00222 
00223     // don't load symbols if not developer
00224     if ( !com_developer->integer ) {
00225         return;
00226     }
00227 
00228     COM_StripExtension( vm->name, name );
00229     Com_sprintf( symbols, sizeof( symbols ), "vm/%s.map", name );
00230     len = FS_ReadFile( symbols, (void **)&mapfile );
00231     if ( !mapfile ) {
00232         Com_Printf( "Couldn't load symbol file: %s\n", symbols );
00233         return;
00234     }
00235 
00236     numInstructions = vm->instructionPointersLength >> 2;
00237 
00238     // parse the symbols
00239     text_p = mapfile;
00240     prev = &vm->symbols;
00241     count = 0;
00242 
00243     while ( 1 ) {
00244         token = COM_Parse( &text_p );
00245         if ( !token[0] ) {
00246             break;
00247         }
00248         segment = ParseHex( token );
00249         if ( segment ) {
00250             COM_Parse( &text_p );
00251             COM_Parse( &text_p );
00252             continue;       // only load code segment values
00253         }
00254 
00255         token = COM_Parse( &text_p );
00256         if ( !token[0] ) {
00257             Com_Printf( "WARNING: incomplete line at end of file\n" );
00258             break;
00259         }
00260         value = ParseHex( token );
00261 
00262         token = COM_Parse( &text_p );
00263         if ( !token[0] ) {
00264             Com_Printf( "WARNING: incomplete line at end of file\n" );
00265             break;
00266         }
00267         chars = strlen( token );
00268         sym = Hunk_Alloc( sizeof( *sym ) + chars, h_high );
00269         *prev = sym;
00270         prev = &sym->next;
00271         sym->next = NULL;
00272 
00273         // convert value from an instruction number to a code offset
00274         if ( value >= 0 && value < numInstructions ) {
00275             value = vm->instructionPointers[value];
00276         }
00277 
00278         sym->symValue = value;
00279         Q_strncpyz( sym->symName, token, chars + 1 );
00280 
00281         count++;
00282     }
00283 
00284     vm->numSymbols = count;
00285     Com_Printf( "%i symbols parsed from %s\n", count, symbols );
00286     FS_FreeFile( mapfile );
00287 }
00288 
00289 /*
00290 ============
00291 VM_DllSyscall
00292 
00293 Dlls will call this directly
00294 
00295  rcg010206 The horror; the horror.
00296 
00297   The syscall mechanism relies on stack manipulation to get it's args.
00298    This is likely due to C's inability to pass "..." parameters to
00299    a function in one clean chunk. On PowerPC Linux, these parameters
00300    are not necessarily passed on the stack, so while (&arg[0] == arg)
00301    is true, (&arg[1] == 2nd function parameter) is not necessarily
00302    accurate, as arg's value might have been stored to the stack or
00303    other piece of scratch memory to give it a valid address, but the
00304    next parameter might still be sitting in a register.
00305 
00306   Quake's syscall system also assumes that the stack grows downward,
00307    and that any needed types can be squeezed, safely, into a signed int.
00308 
00309   This hack below copies all needed values for an argument to a
00310    array in memory, so that Quake can get the correct values. This can
00311    also be used on systems where the stack grows upwards, as the
00312    presumably standard and safe stdargs.h macros are used.
00313 
00314   As for having enough space in a signed int for your datatypes, well,
00315    it might be better to wait for DOOM 3 before you start porting.  :)
00316 
00317   The original code, while probably still inherently dangerous, seems
00318    to work well enough for the platforms it already works on. Rather
00319    than add the performance hit for those platforms, the original code
00320    is still in use there.
00321 
00322   For speed, we just grab 15 arguments, and don't worry about exactly
00323    how many the syscall actually needs; the extra is thrown away.
00324  
00325 ============
00326 */
00327 int QDECL VM_DllSyscall( int arg, ... ) {
00328 #if ((defined __linux__) && (defined __powerpc__))
00329   // rcg010206 - see commentary above
00330   int args[16];
00331   int i;
00332   va_list ap;
00333   
00334   args[0] = arg;
00335   
00336   va_start(ap, arg);
00337   for (i = 1; i < sizeof (args) / sizeof (args[i]); i++)
00338     args[i] = va_arg(ap, int);
00339   va_end(ap);
00340   
00341   return currentVM->systemCall( args );
00342 #else // original id code
00343     return currentVM->systemCall( &arg );
00344 #endif
00345 }
00346 
00347 /*
00348 =================
00349 VM_Restart
00350 
00351 Reload the data, but leave everything else in place
00352 This allows a server to do a map_restart without changing memory allocation
00353 =================
00354 */
00355 vm_t *VM_Restart( vm_t *vm ) {
00356     vmHeader_t  *header;
00357     int         length;
00358     int         dataLength;
00359     int         i;
00360     char        filename[MAX_QPATH];
00361 
00362     // DLL's can't be restarted in place
00363     if ( vm->dllHandle ) {
00364         char    name[MAX_QPATH];
00365         int         (*systemCall)( int *parms );
00366         
00367         systemCall = vm->systemCall;    
00368         Q_strncpyz( name, vm->name, sizeof( name ) );
00369 
00370         VM_Free( vm );
00371 
00372         vm = VM_Create( name, systemCall, VMI_NATIVE );
00373         return vm;
00374     }
00375 
00376     // load the image
00377     Com_Printf( "VM_Restart()\n", filename );
00378     Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
00379     Com_Printf( "Loading vm file %s.\n", filename );
00380     length = FS_ReadFile( filename, (void **)&header );
00381     if ( !header ) {
00382         Com_Error( ERR_DROP, "VM_Restart failed.\n" );
00383     }
00384 
00385     // byte swap the header
00386     for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) {
00387         ((int *)header)[i] = LittleLong( ((int *)header)[i] );
00388     }
00389 
00390     // validate
00391     if ( header->vmMagic != VM_MAGIC
00392         || header->bssLength < 0 
00393         || header->dataLength < 0 
00394         || header->litLength < 0 
00395         || header->codeLength <= 0 ) {
00396         VM_Free( vm );
00397         Com_Error( ERR_FATAL, "%s has bad header", filename );
00398     }
00399 
00400     // round up to next power of 2 so all data operations can
00401     // be mask protected
00402     dataLength = header->dataLength + header->litLength + header->bssLength;
00403     for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
00404     }
00405     dataLength = 1 << i;
00406 
00407     // clear the data
00408     Com_Memset( vm->dataBase, 0, dataLength );
00409 
00410     // copy the intialized data
00411     Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength );
00412 
00413     // byte swap the longs
00414     for ( i = 0 ; i < header->dataLength ; i += 4 ) {
00415         *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
00416     }
00417 
00418     // free the original file
00419     FS_FreeFile( header );
00420 
00421     return vm;
00422 }
00423 
00424 /*
00425 ================
00426 VM_Create
00427 
00428 If image ends in .qvm it will be interpreted, otherwise
00429 it will attempt to load as a system dll
00430 ================
00431 */
00432 
00433 #define STACK_SIZE  0x20000
00434 
00435 vm_t *VM_Create( const char *module, int (*systemCalls)(int *), 
00436                 vmInterpret_t interpret ) {
00437     vm_t        *vm;
00438     vmHeader_t  *header;
00439     int         length;
00440     int         dataLength;
00441     int         i, remaining;
00442     char        filename[MAX_QPATH];
00443 
00444     if ( !module || !module[0] || !systemCalls ) {
00445         Com_Error( ERR_FATAL, "VM_Create: bad parms" );
00446     }
00447 
00448     remaining = Hunk_MemoryRemaining();
00449 
00450     // see if we already have the VM
00451     for ( i = 0 ; i < MAX_VM ; i++ ) {
00452         if (!Q_stricmp(vmTable[i].name, module)) {
00453             vm = &vmTable[i];
00454             return vm;
00455         }
00456     }
00457 
00458     // find a free vm
00459     for ( i = 0 ; i < MAX_VM ; i++ ) {
00460         if ( !vmTable[i].name[0] ) {
00461             break;
00462         }
00463     }
00464 
00465     if ( i == MAX_VM ) {
00466         Com_Error( ERR_FATAL, "VM_Create: no free vm_t" );
00467     }
00468 
00469     vm = &vmTable[i];
00470 
00471     Q_strncpyz( vm->name, module, sizeof( vm->name ) );
00472     vm->systemCall = systemCalls;
00473 
00474     // never allow dll loading with a demo
00475     if ( interpret == VMI_NATIVE ) {
00476         if ( Cvar_VariableValue( "fs_restrict" ) ) {
00477             interpret = VMI_COMPILED;
00478         }
00479     }
00480 
00481     if ( interpret == VMI_NATIVE ) {
00482         // try to load as a system dll
00483         Com_Printf( "Loading dll file %s.\n", vm->name );
00484         vm->dllHandle = Sys_LoadDll( module, vm->fqpath , &vm->entryPoint, VM_DllSyscall );
00485         if ( vm->dllHandle ) {
00486             return vm;
00487         }
00488 
00489         Com_Printf( "Failed to load dll, looking for qvm.\n" );
00490         interpret = VMI_COMPILED;
00491     }
00492 
00493     // load the image
00494     Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
00495     Com_Printf( "Loading vm file %s.\n", filename );
00496     length = FS_ReadFile( filename, (void **)&header );
00497     if ( !header ) {
00498         Com_Printf( "Failed.\n" );
00499         VM_Free( vm );
00500         return NULL;
00501     }
00502 
00503     // byte swap the header
00504     for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) {
00505         ((int *)header)[i] = LittleLong( ((int *)header)[i] );
00506     }
00507 
00508     // validate
00509     if ( header->vmMagic != VM_MAGIC
00510         || header->bssLength < 0 
00511         || header->dataLength < 0 
00512         || header->litLength < 0 
00513         || header->codeLength <= 0 ) {
00514         VM_Free( vm );
00515         Com_Error( ERR_FATAL, "%s has bad header", filename );
00516     }
00517 
00518     // round up to next power of 2 so all data operations can
00519     // be mask protected
00520     dataLength = header->dataLength + header->litLength + header->bssLength;
00521     for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
00522     }
00523     dataLength = 1 << i;
00524 
00525     // allocate zero filled space for initialized and uninitialized data
00526     vm->dataBase = Hunk_Alloc( dataLength, h_high );
00527     vm->dataMask = dataLength - 1;
00528 
00529     // copy the intialized data
00530     Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength );
00531 
00532     // byte swap the longs
00533     for ( i = 0 ; i < header->dataLength ; i += 4 ) {
00534         *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
00535     }
00536 
00537     // allocate space for the jump targets, which will be filled in by the compile/prep functions
00538     vm->instructionPointersLength = header->instructionCount * 4;
00539     vm->instructionPointers = Hunk_Alloc( vm->instructionPointersLength, h_high );
00540 
00541     // copy or compile the instructions
00542     vm->codeLength = header->codeLength;
00543 
00544     if ( interpret >= VMI_COMPILED ) {
00545         vm->compiled = qtrue;
00546         VM_Compile( vm, header );
00547     } else {
00548         vm->compiled = qfalse;
00549         VM_PrepareInterpreter( vm, header );
00550     }
00551 
00552     // free the original file
00553     FS_FreeFile( header );
00554 
00555     // load the map file
00556     VM_LoadSymbols( vm );
00557 
00558     // the stack is implicitly at the end of the image
00559     vm->programStack = vm->dataMask + 1;
00560     vm->stackBottom = vm->programStack - STACK_SIZE;
00561 
00562     Com_Printf("%s loaded in %d bytes on the hunk\n", module, remaining - Hunk_MemoryRemaining());
00563 
00564     return vm;
00565 }
00566 
00567 /*
00568 ==============
00569 VM_Free
00570 ==============
00571 */
00572 void VM_Free( vm_t *vm ) {
00573 
00574     if ( vm->dllHandle ) {
00575         Sys_UnloadDll( vm->dllHandle );
00576         Com_Memset( vm, 0, sizeof( *vm ) );
00577     }
00578 #if 0   // now automatically freed by hunk
00579     if ( vm->codeBase ) {
00580         Z_Free( vm->codeBase );
00581     }
00582     if ( vm->dataBase ) {
00583         Z_Free( vm->dataBase );
00584     }
00585     if ( vm->instructionPointers ) {
00586         Z_Free( vm->instructionPointers );
00587     }
00588 #endif
00589     Com_Memset( vm, 0, sizeof( *vm ) );
00590 
00591     currentVM = NULL;
00592     lastVM = NULL;
00593 }
00594 
00595 void VM_Clear(void) {
00596     int i;
00597     for (i=0;i<MAX_VM; i++) {
00598         if ( vmTable[i].dllHandle ) {
00599             Sys_UnloadDll( vmTable[i].dllHandle );
00600         }
00601         Com_Memset( &vmTable[i], 0, sizeof( vm_t ) );
00602     }
00603     currentVM = NULL;
00604     lastVM = NULL;
00605 }
00606 
00607 void *VM_ArgPtr( int intValue ) {
00608     if ( !intValue ) {
00609         return NULL;
00610     }
00611     // bk001220 - currentVM is missing on reconnect
00612     if ( currentVM==NULL )
00613       return NULL;
00614 
00615     if ( currentVM->entryPoint ) {
00616         return (void *)(currentVM->dataBase + intValue);
00617     }
00618     else {
00619         return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask));
00620     }
00621 }
00622 
00623 void *VM_ExplicitArgPtr( vm_t *vm, int intValue ) {
00624     if ( !intValue ) {
00625         return NULL;
00626     }
00627 
00628     // bk010124 - currentVM is missing on reconnect here as well?
00629     if ( currentVM==NULL )
00630       return NULL;
00631 
00632     //
00633     if ( vm->entryPoint ) {
00634         return (void *)(vm->dataBase + intValue);
00635     }
00636     else {
00637         return (void *)(vm->dataBase + (intValue & vm->dataMask));
00638     }
00639 }
00640 
00641 
00642 /*
00643 ==============
00644 VM_Call
00645 
00646 
00647 Upon a system call, the stack will look like:
00648 
00649 sp+32   parm1
00650 sp+28   parm0
00651 sp+24   return value
00652 sp+20   return address
00653 sp+16   local1
00654 sp+14   local0
00655 sp+12   arg1
00656 sp+8    arg0
00657 sp+4    return stack
00658 sp      return address
00659 
00660 An interpreted function will immediately execute
00661 an OP_ENTER instruction, which will subtract space for
00662 locals from sp
00663 ==============
00664 */
00665 #define MAX_STACK   256
00666 #define STACK_MASK  (MAX_STACK-1)
00667 
00668 int QDECL VM_Call( vm_t *vm, int callnum, ... ) {
00669     vm_t    *oldVM;
00670     int     r;
00671     int i;
00672     int args[16];
00673     va_list ap;
00674 
00675 
00676     if ( !vm ) {
00677         Com_Error( ERR_FATAL, "VM_Call with NULL vm" );
00678     }
00679 
00680     oldVM = currentVM;
00681     currentVM = vm;
00682     lastVM = vm;
00683 
00684     if ( vm_debugLevel ) {
00685       Com_Printf( "VM_Call( %i )\n", callnum );
00686     }
00687 
00688     // if we have a dll loaded, call it directly
00689     if ( vm->entryPoint ) {
00690         //rcg010207 -  see dissertation at top of VM_DllSyscall() in this file.
00691         va_start(ap, callnum);
00692         for (i = 0; i < sizeof (args) / sizeof (args[i]); i++) {
00693             args[i] = va_arg(ap, int);
00694         }
00695         va_end(ap);
00696 
00697         r = vm->entryPoint( callnum,  args[0],  args[1],  args[2], args[3],
00698                             args[4],  args[5],  args[6], args[7],
00699                             args[8],  args[9], args[10], args[11],
00700                             args[12], args[13], args[14], args[15]);
00701     } else if ( vm->compiled ) {
00702         r = VM_CallCompiled( vm, &callnum );
00703     } else {
00704         r = VM_CallInterpreted( vm, &callnum );
00705     }
00706 
00707     if ( oldVM != NULL ) // bk001220 - assert(currentVM!=NULL) for oldVM==NULL
00708       currentVM = oldVM;
00709     return r;
00710 }
00711 
00712 //=================================================================
00713 
00714 static int QDECL VM_ProfileSort( const void *a, const void *b ) {
00715     vmSymbol_t  *sa, *sb;
00716 
00717     sa = *(vmSymbol_t **)a;
00718     sb = *(vmSymbol_t **)b;
00719 
00720     if ( sa->profileCount < sb->profileCount ) {
00721         return -1;
00722     }
00723     if ( sa->profileCount > sb->profileCount ) {
00724         return 1;
00725     }
00726     return 0;
00727 }
00728 
00729 /*
00730 ==============
00731 VM_VmProfile_f
00732 
00733 ==============
00734 */
00735 void VM_VmProfile_f( void ) {
00736     vm_t        *vm;
00737     vmSymbol_t  **sorted, *sym;
00738     int         i;
00739     double      total;
00740 
00741     if ( !lastVM ) {
00742         return;
00743     }
00744 
00745     vm = lastVM;
00746 
00747     if ( !vm->numSymbols ) {
00748         return;
00749     }
00750 
00751     sorted = Z_Malloc( vm->numSymbols * sizeof( *sorted ) );
00752     sorted[0] = vm->symbols;
00753     total = sorted[0]->profileCount;
00754     for ( i = 1 ; i < vm->numSymbols ; i++ ) {
00755         sorted[i] = sorted[i-1]->next;
00756         total += sorted[i]->profileCount;
00757     }
00758 
00759     qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort );
00760 
00761     for ( i = 0 ; i < vm->numSymbols ; i++ ) {
00762         int     perc;
00763 
00764         sym = sorted[i];
00765 
00766         perc = 100 * (float) sym->profileCount / total;
00767         Com_Printf( "%2i%% %9i %s\n", perc, sym->profileCount, sym->symName );
00768         sym->profileCount = 0;
00769     }
00770 
00771     Com_Printf("    %9.0f total\n", total );
00772 
00773     Z_Free( sorted );
00774 }
00775 
00776 /*
00777 ==============
00778 VM_VmInfo_f
00779 
00780 ==============
00781 */
00782 void VM_VmInfo_f( void ) {
00783     vm_t    *vm;
00784     int     i;
00785 
00786     Com_Printf( "Registered virtual machines:\n" );
00787     for ( i = 0 ; i < MAX_VM ; i++ ) {
00788         vm = &vmTable[i];
00789         if ( !vm->name[0] ) {
00790             break;
00791         }
00792         Com_Printf( "%s : ", vm->name );
00793         if ( vm->dllHandle ) {
00794             Com_Printf( "native\n" );
00795             continue;
00796         }
00797         if ( vm->compiled ) {
00798             Com_Printf( "compiled on load\n" );
00799         } else {
00800             Com_Printf( "interpreted\n" );
00801         }
00802         Com_Printf( "    code length : %7i\n", vm->codeLength );
00803         Com_Printf( "    table length: %7i\n", vm->instructionPointersLength );
00804         Com_Printf( "    data length : %7i\n", vm->dataMask + 1 );
00805     }
00806 }
00807 
00808 /*
00809 ===============
00810 VM_LogSyscalls
00811 
00812 Insert calls to this while debugging the vm compiler
00813 ===============
00814 */
00815 void VM_LogSyscalls( int *args ) {
00816     static  int     callnum;
00817     static  FILE    *f;
00818 
00819     if ( !f ) {
00820         f = fopen("syscalls.log", "w" );
00821     }
00822     callnum++;
00823     fprintf(f, "%i: %i (%i) = %i %i %i %i\n", callnum, args - (int *)currentVM->dataBase,
00824         args[0], args[1], args[2], args[3], args[4] );
00825 }
00826 
00827 
00828 
00829 #ifdef oDLL_ONLY // bk010215 - for DLL_ONLY dedicated servers/builds w/o VM
00830 int VM_CallCompiled( vm_t *vm, int *args ) {
00831   return(0); 
00832 }
00833 
00834 void VM_Compile( vm_t *vm, vmHeader_t *header ) {}
00835 #endif // DLL_ONLY

Generated on Thu Aug 25 12:37:49 2005 for Quake III Arena by  doxygen 1.3.9.1