/* runtime.c */ #include "config.h" #include "runtime.h" #ifdef macintosh /* 23Nov93 e */ /* 16Mar94 e -- revised to not abuse time manager (!) */ /* 31Aug95 e -- revised to abuse time manager less (!) */ /* 05Sep95 e -- added mosml required stuff: gettimeofday, e_getrusage */ /* Keeping track of time spent - in gc - in multifinder - overall since startup */ #include #include #include #include #include "alloc.h" #include "fail.h" #include "memory.h" #include "debugger.h" #include "mlvalues.h" #include "freelist.h" #include "gc.h" #include "major_gc.h" /* */ #define qRunTimes 8 /* must be at least three for: total, gc, mutlifinder */ #define aMILLION (1000000L) #if GENERATINGPOWERPC #pragma options align=mac68k #endif typedef struct tinfuRec { TMTask TMTask; short filler; unsigned long acc_secs; signed long acc_usecs; } tinfuRec, *tinfuPtr; #if GENERATINGPOWERPC #pragma options align=reset #endif static tinfuRec gTimeInfo[qRunTimes]; #define TIMe(n) (gTimeInfo[n].TMTask) /* 09Jan95 e */ static TimerUPP tmt_handlerUPP = NULL; #if powerc static pascal void tmt_handler(TMTaskPtr a1) { ((tinfuPtr )a1)->acc_secs++; PrimeTime((QElemPtr )a1, -aMILLION); } #else #ifndef __MWERKS__ static pascal void tmt_handler(void) { asm { ADDQ.L #1, tinfuRec.acc_secs(a1) MOVE.L A1, A0 MOVE.L #-aMILLION, D0 DC.W 0xA05A ; PrimeTime } } #else asm static pascal void tmt_handler(void) { ADDQ.L #1, 0x18(A1) // tinfuRec.acc_secs(a1) MOVE.L A1, A0 MOVE.L #-aMILLION, D0 DC.W 0xA05A // PrimeTime RTS } #endif #endif static void cancel_timers(void) { int i; for ( i = 0; i < qRunTimes; i++ ) // always! 31Aug95 e -- if (TIMe(i).tmAddr != NULL && TIMe(i).qType < 0 ) { RmvTime((QElemPtr )&TIMe(i)); TIMe(i).tmAddr = NULL; } } static time_t systime_init; void init_timers(void) { int i; if (tmt_handlerUPP == NULL) tmt_handlerUPP = NewTimerProc(tmt_handler); for ( i = 0; i < qRunTimes; i++ ) if (TIMe(i).tmAddr == NULL) { TIMe(i).tmAddr = tmt_handlerUPP; // only 0, 31Aug95 e -- InsXTime((QElemPtr )&TIMe(i)); if ( i == 0 ) { _atexit(cancel_timers); InsXTime((QElemPtr )&TIMe(0)); systime_init = time(NULL); PrimeTime((QElemPtr )&TIMe(0), -aMILLION); } } } void beg_runtime(int i) { tinfuPtr p; if ( 0 >= i || i >= qRunTimes ) Debugger(); /* coding error */ p = &gTimeInfo[i]; p->TMTask.tmWakeUp = 0; p->TMTask.tmReserved = 0; InsXTime((QElemPtr )&(p->TMTask)); PrimeTime((QElemPtr )&(p->TMTask), -aMILLION); } void acc_runtime(int i) { tinfuPtr p; if ( 0 > i || i >= qRunTimes ) Debugger(); /* coding error */ p = &gTimeInfo[i]; RmvTime((QElemPtr )&(p->TMTask)); p->acc_usecs += aMILLION + p->TMTask.tmCount; if (p->acc_usecs >= aMILLION) { p->acc_secs += 1; p->acc_usecs -= aMILLION; } else if (p->acc_usecs < 0) { p->acc_secs -= 1; p->acc_usecs += aMILLION; } } static double double_zero = (double )0; value get_timer(value x) /* ML */ { unsigned int i = Long_val(x); tinfuPtr p; if ( 0 > i || i >= qRunTimes) return copy_double(double_zero); p = &gTimeInfo[i]; if (i == 0) { RmvTime((QElemPtr )&(p->TMTask)); p->acc_usecs = aMILLION + p->TMTask.tmCount; InsXTime((QElemPtr )&(p->TMTask)); PrimeTime((QElemPtr )&(p->TMTask), 0); } return copy_double(p->acc_secs + (p->acc_usecs / (double )aMILLION)); } value beg_timer(value x) /* ML */ { unsigned int i = Long_val(x); if (i >= qRunTimes || i <= 2) return Val_false; beg_runtime(i); return Val_true; } value end_timer(value x) /* ML */ { unsigned int i = Long_val(x); if (i >= qRunTimes || i <= 2) return Val_false; acc_runtime(i); return Val_true; } value clr_timer(value x) /* ML */ { unsigned int i = Long_val(x); tinfuPtr p; if (i >= qRunTimes || i <= 2) return Val_false; p = &gTimeInfo[i]; p->acc_secs = 0; p->acc_usecs = 0; return Val_true; } /* runtime stats */ value e_getrusage( void ) { tinfuPtr p; unsigned long rts, sts, gts, uts; long rtu, stu, gtu, utu; value res = alloc (6, 0); /* snapshot run timer */ p = &gTimeInfo[0]; RmvTime((QElemPtr )&(p->TMTask)); rts = p->acc_secs; // elapsed time rtu = p->acc_usecs + aMILLION + p->TMTask.tmCount; InsXTime((QElemPtr )&(p->TMTask)); PrimeTime((QElemPtr )&(p->TMTask), 0); /* compute "user" time */ gts = gTimeInfo[1].acc_secs; // gc time gtu = gTimeInfo[1].acc_usecs; sts = gTimeInfo[2].acc_secs; // multifinder time stu = gTimeInfo[2].acc_usecs; uts = (rts - sts) - gts; utu = (rtu - stu) - gtu; while (utu < 0) { utu += aMILLION; uts -= 1; } while (utu >= aMILLION) { utu -= aMILLION; uts += 1; } /* pathological case at startup really fries mosml... */ if (uts > 0x3FFFFFFF) { uts = 0; utu = 1; } /* fill in SML record */ Field (res, 0) = Val_long (gts); // "gc" Field (res, 1) = Val_long (gtu); Field (res, 2) = Val_long (sts); // "system" Field (res, 3) = Val_long (stu); Field (res, 4) = Val_long (uts); // "user" Field (res, 5) = Val_long (utu); return res; } /* end of MacOS specific code */ #else /* DOS, Unix, Win32 */ #ifdef WIN32 #include #include #else #include #include #include #include #endif struct mosml_timeval gc_time = { (long) 0, (long) 0 }; void beg_gc_time(void) { #ifdef WIN32 /* // Here I return sysTime = usrTime. // Perhaps, win32 enables sysTime and usrTime to be mesured // in an accurate way... // Sergei Romanenko */ struct timeb t; ftime(&t); gc_time.tv_sec -= t.time; gc_time.tv_usec -= ((long) t.millitm) * 1000; #elif defined(hpux) || defined(__svr4__) || defined(plan9) struct tms buffer; long persec = sysconf(_SC_CLK_TCK); times(&buffer); gc_time.tv_sec -= buffer.tms_utime / persec; gc_time.tv_usec -= (buffer.tms_utime % persec) * (1000000 / persec); #else struct rusage rusages; getrusage(RUSAGE_SELF, &rusages); gc_time.tv_sec -= rusages.ru_utime.tv_sec; gc_time.tv_usec -= rusages.ru_utime.tv_usec; #endif if (gc_time.tv_usec < 0) { gc_time.tv_usec += 1000000; gc_time.tv_sec -= 1; } } void end_gc_time(void) { #ifdef WIN32 /* // Here I return sysTime = usrTime. // Perhaps, win32 enables sysTime and usrTime to be mesured // in an accurate way... // Sergei Romanenko */ struct timeb t; ftime(&t); gc_time.tv_sec += t.time; gc_time.tv_usec += ((long) t.millitm) * 1000; #elif defined(hpux) || defined(__svr4__) || defined(plan9) struct tms buffer; long persec = sysconf(_SC_CLK_TCK); times(&buffer); gc_time.tv_sec += buffer.tms_utime / persec; gc_time.tv_usec += (buffer.tms_utime % persec) * (1000000 / persec); #else struct rusage rusages; getrusage(RUSAGE_SELF, &rusages); gc_time.tv_sec += rusages.ru_utime.tv_sec; gc_time.tv_usec += rusages.ru_utime.tv_usec; #endif if (gc_time.tv_usec >= 1000000) { gc_time.tv_usec -= 1000000; gc_time.tv_sec += 1; } } #endif