1 /* runtime.c  */
2 
3 #include "config.h"
4 
5 #include "runtime.h"
6 
7 #ifdef macintosh
8 
9 /* 23Nov93  e */
10 /* 16Mar94  e  -- revised to not abuse time manager (!) */
11 /* 31Aug95  e  -- revised to abuse time manager less (!) */
12 /* 05Sep95  e  -- added mosml required stuff: gettimeofday, e_getrusage */
13 
14 /* Keeping track of time spent
15  - in gc
16  - in multifinder
17  - overall since startup
18 */
19 
20 #include <Timer.h>
21 #include <stdlib.h>
22 
23 #include <time.h>
24 #include <utime.h>
25 
26 #include "alloc.h"
27 #include "fail.h"
28 #include "memory.h"
29 #include "debugger.h"
30 #include "mlvalues.h"
31 #include "freelist.h"
32 #include "gc.h"
33 #include "major_gc.h"
34 
35 /* */
36 
37 #define qRunTimes 8 /* must be at least three for: total, gc, mutlifinder */
38 
39 #define aMILLION (1000000L)
40 
41 #if GENERATINGPOWERPC
42 #pragma options align=mac68k
43 #endif
44 
45 typedef struct tinfuRec
46 {
47   TMTask TMTask;
48   short filler;
49   unsigned long  acc_secs;
50     signed long acc_usecs;
51 } tinfuRec, *tinfuPtr;
52 
53 #if GENERATINGPOWERPC
54 #pragma options align=reset
55 #endif
56 
57 static tinfuRec gTimeInfo[qRunTimes];
58 
59 #define TIMe(n) (gTimeInfo[n].TMTask)
60 
61 /* 09Jan95 e */
62 
63 static TimerUPP tmt_handlerUPP = NULL;
64 
65 #if powerc
tmt_handler(TMTaskPtr a1)66 static pascal void tmt_handler(TMTaskPtr a1)
67 {
68   ((tinfuPtr )a1)->acc_secs++;
69   PrimeTime((QElemPtr )a1, -aMILLION);
70 }
71 #else
72 #ifndef __MWERKS__
tmt_handler(void)73 static pascal void tmt_handler(void)
74 {
75   asm
76   {
77     ADDQ.L #1, tinfuRec.acc_secs(a1)
78     MOVE.L A1, A0
79     MOVE.L #-aMILLION, D0
80     DC.W 0xA05A ; PrimeTime
81   }
82 }
83 #else
tmt_handler(void)84 asm static pascal void tmt_handler(void)
85 {
86     ADDQ.L #1, 0x18(A1) // tinfuRec.acc_secs(a1)
87     MOVE.L A1, A0
88     MOVE.L #-aMILLION, D0
89     DC.W 0xA05A // PrimeTime
90     RTS
91 }
92 #endif
93 #endif
94 
cancel_timers(void)95 static void cancel_timers(void)
96 { int i;
97   for ( i = 0; i < qRunTimes; i++ )
98     // always! 31Aug95 e -- if (TIMe(i).tmAddr != NULL && TIMe(i).qType < 0 )
99     { RmvTime((QElemPtr )&TIMe(i));
100       TIMe(i).tmAddr = NULL;
101     }
102 }
103 
104 static time_t systime_init;
105 
init_timers(void)106 void init_timers(void)
107 { int i;
108   if (tmt_handlerUPP == NULL) tmt_handlerUPP = NewTimerProc(tmt_handler);
109   for ( i = 0; i < qRunTimes; i++ )
110     if (TIMe(i).tmAddr == NULL)
111     { TIMe(i).tmAddr = tmt_handlerUPP;
112       // only 0, 31Aug95 e -- InsXTime((QElemPtr )&TIMe(i));
113       if ( i == 0 )
114       { _atexit(cancel_timers);
115         InsXTime((QElemPtr )&TIMe(0));
116         systime_init = time(NULL);
117         PrimeTime((QElemPtr )&TIMe(0), -aMILLION);
118       }
119     }
120 }
121 
beg_runtime(int i)122 void beg_runtime(int i)
123 { tinfuPtr p;
124   if ( 0 >= i || i >= qRunTimes ) Debugger(); /* coding error */
125   p = &gTimeInfo[i];
126   p->TMTask.tmWakeUp = 0;
127   p->TMTask.tmReserved = 0;
128   InsXTime((QElemPtr )&(p->TMTask));
129   PrimeTime((QElemPtr )&(p->TMTask), -aMILLION);
130 }
131 
acc_runtime(int i)132 void acc_runtime(int i)
133 { tinfuPtr p;
134   if ( 0 > i || i >= qRunTimes ) Debugger(); /* coding error */
135   p = &gTimeInfo[i];
136   RmvTime((QElemPtr )&(p->TMTask));
137   p->acc_usecs += aMILLION + p->TMTask.tmCount;
138   if (p->acc_usecs >= aMILLION)
139   {
140     p->acc_secs += 1;
141     p->acc_usecs -= aMILLION;
142   }
143   else if (p->acc_usecs < 0)
144   {
145     p->acc_secs -= 1;
146     p->acc_usecs += aMILLION;
147   }
148 }
149 
150 static double double_zero = (double )0;
151 
get_timer(value x)152 value get_timer(value x)                          /* ML */
153 {
154   unsigned int i = Long_val(x);
155   tinfuPtr p;
156 
157   if ( 0 > i || i >= qRunTimes) return copy_double(double_zero);
158   p = &gTimeInfo[i];
159   if (i == 0)
160   {
161     RmvTime((QElemPtr )&(p->TMTask));
162     p->acc_usecs = aMILLION + p->TMTask.tmCount;
163     InsXTime((QElemPtr )&(p->TMTask));
164     PrimeTime((QElemPtr )&(p->TMTask), 0);
165   }
166   return copy_double(p->acc_secs + (p->acc_usecs / (double )aMILLION));
167 }
168 
beg_timer(value x)169 value beg_timer(value x)                          /* ML */
170 {
171   unsigned int i = Long_val(x);
172 
173   if (i >= qRunTimes || i <= 2) return Val_false;
174   beg_runtime(i);
175   return Val_true;
176 }
177 
end_timer(value x)178 value end_timer(value x)                          /* ML */
179 {
180   unsigned int i = Long_val(x);
181 
182   if (i >= qRunTimes || i <= 2) return Val_false;
183   acc_runtime(i);
184   return Val_true;
185 }
186 
clr_timer(value x)187 value clr_timer(value x)                          /* ML */
188 {
189   unsigned int i = Long_val(x);
190   tinfuPtr p;
191 
192   if (i >= qRunTimes || i <= 2) return Val_false;
193   p = &gTimeInfo[i];
194   p->acc_secs  = 0;
195   p->acc_usecs = 0;
196   return Val_true;
197 }
198 
199 /* runtime stats */
200 
e_getrusage(void)201 value e_getrusage( void )
202 {
203   tinfuPtr p;
204   unsigned long rts, sts, gts, uts;
205            long rtu, stu, gtu, utu;
206   value res = alloc (6, 0);
207   /* snapshot run timer */
208   p = &gTimeInfo[0];
209   RmvTime((QElemPtr )&(p->TMTask));
210   rts = p->acc_secs;            // elapsed time
211   rtu = p->acc_usecs + aMILLION + p->TMTask.tmCount;
212   InsXTime((QElemPtr )&(p->TMTask));
213   PrimeTime((QElemPtr )&(p->TMTask), 0);
214   /* compute "user" time */
215   gts = gTimeInfo[1].acc_secs;  // gc time
216   gtu = gTimeInfo[1].acc_usecs;
217   sts = gTimeInfo[2].acc_secs;  // multifinder time
218   stu = gTimeInfo[2].acc_usecs;
219   uts = (rts - sts) - gts;
220   utu = (rtu - stu) - gtu;
221   while (utu < 0)         { utu += aMILLION; uts -= 1; }
222   while (utu >= aMILLION) { utu -= aMILLION; uts += 1; }
223   /* pathological case at startup really fries mosml... */
224   if (uts > 0x3FFFFFFF)
225   {
226     uts = 0;
227     utu = 1;
228   }
229   /* fill in SML record */
230   Field (res, 0) = Val_long (gts);  // "gc"
231   Field (res, 1) = Val_long (gtu);
232   Field (res, 2) = Val_long (sts);  // "system"
233   Field (res, 3) = Val_long (stu);
234   Field (res, 4) = Val_long (uts);  // "user"
235   Field (res, 5) = Val_long (utu);
236   return res;
237 }
238 
239 /* end of MacOS specific code */
240 
241 #else
242 
243 /* DOS, Unix, Win32 */
244 
245 #ifdef WIN32
246 #include <sys/timeb.h>
247 #include <sys/utime.h>
248 #else
249 #include <sys/times.h>
250 #include <sys/time.h>
251 #include <sys/resource.h>
252 #include <unistd.h>
253 #endif
254 
255 struct mosml_timeval gc_time = { (long) 0, (long) 0 };
256 
beg_gc_time(void)257 void beg_gc_time(void)
258 {
259 #ifdef WIN32
260   /*
261   // Here I return sysTime = usrTime.
262   // Perhaps, win32 enables sysTime and usrTime to be mesured
263   // in an accurate way...
264   //  Sergei Romanenko
265   */
266   struct timeb t;
267   ftime(&t);
268   gc_time.tv_sec  -=  t.time;
269   gc_time.tv_usec -= ((long) t.millitm) * 1000;
270 #elif defined(hpux) || defined(__svr4__)
271   struct tms buffer;
272 
273   long persec = sysconf(_SC_CLK_TCK);
274   times(&buffer);
275   gc_time.tv_sec  -= buffer.tms_utime / persec;
276   gc_time.tv_usec -= (buffer.tms_utime % persec) * (1000000 / persec);
277 #else
278   struct rusage rusages;
279 
280   getrusage(RUSAGE_SELF, &rusages);
281   gc_time.tv_sec  -= rusages.ru_utime.tv_sec;
282   gc_time.tv_usec -= rusages.ru_utime.tv_usec;
283 #endif
284 
285   if (gc_time.tv_usec < 0) {
286     gc_time.tv_usec += 1000000;
287     gc_time.tv_sec  -= 1;
288   }
289 }
290 
end_gc_time(void)291 void end_gc_time(void)
292 {
293 #ifdef WIN32
294   /*
295   // Here I return sysTime = usrTime.
296   // Perhaps, win32 enables sysTime and usrTime to be mesured
297   // in an accurate way...
298   //  Sergei Romanenko
299   */
300   struct timeb t;
301   ftime(&t);
302   gc_time.tv_sec  +=  t.time;
303   gc_time.tv_usec += ((long) t.millitm) * 1000;
304 #elif defined(hpux) || defined(__svr4__)
305   struct tms buffer;
306 
307   long persec = sysconf(_SC_CLK_TCK);
308   times(&buffer);
309   gc_time.tv_sec  += buffer.tms_utime / persec;
310   gc_time.tv_usec += (buffer.tms_utime % persec) * (1000000 / persec);
311 #else
312   struct rusage rusages;
313 
314   getrusage(RUSAGE_SELF, &rusages);
315   gc_time.tv_sec  += rusages.ru_utime.tv_sec;
316   gc_time.tv_usec += rusages.ru_utime.tv_usec;
317 #endif
318 
319   if (gc_time.tv_usec >= 1000000) {
320     gc_time.tv_usec -= 1000000;
321     gc_time.tv_sec  += 1;
322   }
323 
324 }
325 
326 #endif
327