9be9dd00ae9b488842509067999182b9017ab38f
[libfirm] / ir / common / timing.c
1 /*
2  * This file is part of libFirm.
3  * Copyright (C) 2012 University of Karlsruhe.
4  */
5
6 /**
7  * @file
8  * @brief   platform neutral timing utilities
9  */
10 #include "config.h"
11
12 #include <stdio.h>
13 #include <string.h>
14
15 #include "timing.h"
16 #include "xmalloc.h"
17 #include "error.h"
18
19 #ifdef _WIN32
20 #define WIN32_LEAN_AND_MEAN
21 #include <windows.h>
22 #include <mmsystem.h>
23
24 /* Win32 timer value. */
25 typedef union {
26         unsigned lo_prec;       /**< 32bit low precision time in milli seconds */
27         LARGE_INTEGER hi_prec;  /**< 64bit high precision time in micro seconds */
28 } ir_timer_val_t;
29
30 #else
31
32 #include <unistd.h>
33 #define HAVE_GETTIMEOFDAY
34
35 /*
36  * Just, if we have gettimeofday()
37  * Someday, we will have a check here.
38  */
39 #ifndef __USE_BSD
40 #define __USE_BSD
41 #endif
42 #include <sys/time.h>
43
44 /* Linux timer value. */
45 typedef struct timeval ir_timer_val_t;
46
47 #endif /* _Win32 */
48
49 #include <stddef.h>
50
51 static inline void _time_reset(ir_timer_val_t *val);
52
53 /**
54  * A timer.
55  */
56 struct ir_timer_t {
57         ir_timer_val_t elapsed;     /**< the elapsed time so far */
58         ir_timer_val_t start;       /**< the start value of the timer */
59         ir_timer_t     *parent;     /**< parent of a timer */
60         ir_timer_t     *displaced;  /**< former timer in case of timer_push */
61         unsigned       running : 1; /**< set if this timer is running */
62 };
63
64 /** The top of the timer stack */
65 static ir_timer_t *timer_stack;
66
67 ir_timer_t *ir_timer_new(void)
68 {
69         ir_timer_t *timer = XMALLOCZ(ir_timer_t);
70         _time_reset(&timer->elapsed);
71         _time_reset(&timer->start);
72         return timer;
73 }
74
75 void ir_timer_free(ir_timer_t *timer)
76 {
77         xfree(timer);
78 }
79
80 #ifdef HAVE_GETTIMEOFDAY
81
82 static inline void _time_get(ir_timer_val_t *val)
83 {
84         gettimeofday(val, NULL);
85 }
86
87 static inline void _time_reset(ir_timer_val_t *val)
88 {
89         timerclear(val);
90 }
91
92 static inline unsigned long _time_to_msec(const ir_timer_val_t *elapsed)
93 {
94         return (unsigned long) elapsed->tv_sec * 1000UL
95                 + (unsigned long) elapsed->tv_usec / 1000UL;
96 }
97
98 static inline unsigned long _time_to_usec(const ir_timer_val_t *elapsed)
99 {
100         return (unsigned long) elapsed->tv_sec * 1000000UL
101                 + (unsigned long) elapsed->tv_usec;
102 }
103
104 static inline double _time_to_sec(const ir_timer_val_t *elapsed)
105 {
106         return (double)elapsed->tv_sec + (double)elapsed->tv_usec / 1000000.0;
107 }
108
109 static inline ir_timer_val_t *_time_add(ir_timer_val_t *res,
110                 const ir_timer_val_t *lhs, const ir_timer_val_t *rhs)
111 {
112         timeradd(lhs, rhs, res);
113                 return res;
114 }
115
116 static inline ir_timer_val_t *_time_sub(ir_timer_val_t *res,
117                 const ir_timer_val_t *lhs, const ir_timer_val_t *rhs)
118 {
119         timersub(lhs, rhs, res);
120         return res;
121 }
122
123 #elif defined(_WIN32)
124
125 static inline void _time_get(ir_timer_val_t *val)
126 {
127         if (!QueryPerformanceCounter(&val->hi_prec))
128                 val->lo_prec = timeGetTime();
129 }
130
131 static inline void _time_reset(ir_timer_val_t *val)
132 {
133         memset(val, 0, sizeof(val[0]));
134 }
135
136 static inline unsigned long _time_to_msec(const ir_timer_val_t *elapsed)
137 {
138         LARGE_INTEGER freq;
139
140         if (!QueryPerformanceFrequency(&freq))
141                 return (unsigned long) elapsed->lo_prec;
142
143         return (unsigned long) ((elapsed->hi_prec.QuadPart * 1000) / freq.QuadPart);
144 }
145
146 static inline unsigned long _time_to_usec(const ir_timer_val_t *elapsed)
147 {
148         LARGE_INTEGER freq;
149
150         if (!QueryPerformanceFrequency(&freq))
151                 return (unsigned long) elapsed->lo_prec * 1000;
152
153         return (unsigned long) ((elapsed->hi_prec.QuadPart * 1000000) / freq.QuadPart);
154 }
155
156 static inline double _time_to_sec(const ir_timer_val_t *elapsed)
157 {
158         LARGE_INTEGER freq;
159
160         if (!QueryPerformanceFrequency(&freq))
161                 return (double) elapsed->lo_prec / 1000.;
162
163         return (double)elapsed->hi_prec.QuadPart / (double)freq.QuadPart;
164 }
165
166 static inline ir_timer_val_t *_time_add(ir_timer_val_t *res, const ir_timer_val_t *lhs, const ir_timer_val_t *rhs)
167 {
168         LARGE_INTEGER dummy;
169         if (QueryPerformanceFrequency(&dummy))
170                 res->hi_prec.QuadPart = lhs->hi_prec.QuadPart + rhs->hi_prec.QuadPart;
171         else
172                 res->lo_prec = lhs->lo_prec + rhs->lo_prec;
173
174         return res;
175 }
176
177 static inline ir_timer_val_t *_time_sub(ir_timer_val_t *res, const ir_timer_val_t *lhs, const ir_timer_val_t *rhs)
178 {
179         LARGE_INTEGER dummy;
180         if (QueryPerformanceFrequency(&dummy))
181                 res->hi_prec.QuadPart = lhs->hi_prec.QuadPart - rhs->hi_prec.QuadPart;
182         else
183                 res->lo_prec = lhs->lo_prec - rhs->lo_prec;
184
185         return res;
186 }
187
188 #endif /* _WIN32 */
189
190 #if defined(_XOPEN_REALTIME) && _XOPEN_REALTIME != -1
191
192 #include <sys/types.h>
193 #include <sched.h>
194
195 static struct sched_param std_sched_param;
196 static int std_sched_param_init = 0;
197
198 int ir_timer_enter_high_priority(void)
199 {
200         pid_t pid = getpid();
201
202         struct sched_param p;
203         int res, max, algo;
204
205         if (!std_sched_param_init) {
206                 res = sched_getparam(pid, &std_sched_param);
207                 if (res != 0)
208                         return res;
209                 std_sched_param_init = 1;
210         }
211
212         algo = sched_getscheduler(pid);
213         max  = sched_get_priority_max(algo);
214
215         p = std_sched_param;
216         p.sched_priority = max;
217         res = sched_setparam(pid, &p);
218
219         return res;
220 }
221
222 int ir_timer_leave_high_priority(void)
223 {
224         int res   = 0;
225         pid_t pid = getpid();
226
227         if (std_sched_param_init)
228                 res = sched_setparam(pid, &std_sched_param);
229
230         return res;
231 }
232
233 #elif defined(_WIN32)
234
235 static int initial_priority = THREAD_PRIORITY_NORMAL;
236
237 int ir_timer_leave_high_priority(void)
238 {
239         int res = 0;
240         if (!SetThreadPriority(GetCurrentThread(), initial_priority)) {
241                 fprintf(stderr, "Failed to leave high priority (%d)\n", GetLastError());
242                 res = GetLastError();
243         }
244
245         return res;
246 }
247
248 int ir_timer_enter_high_priority(void)
249 {
250         int res = 0;
251         initial_priority = GetThreadPriority(GetCurrentThread());
252         if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) {
253                 fprintf(stderr, "Failed to enter high priority (%d)\n", GetLastError());
254                 res = GetLastError();
255         }
256
257         return res;
258 }
259
260
261 #else
262
263 int ir_timer_enter_high_priority(void)
264 {
265         fprintf(stderr, "POSIX scheduling API not present\n");
266         return 0;
267 }
268
269 int ir_timer_leave_high_priority(void)
270 {
271         return 0;
272 }
273
274 #endif
275
276
277 #ifdef __linux__
278
279 #include <malloc.h>
280 size_t ir_get_heap_used_bytes(void)
281 {
282         struct mallinfo mi = mallinfo();
283         return mi.uordblks;
284 }
285
286 #elif defined(_WIN32) /* __linux__ */
287
288 #include <malloc.h>
289
290 size_t ir_get_heap_used_bytes(void)
291 {
292         _HEAPINFO hinfo;
293         int heapstatus;
294         size_t res = 0;
295         hinfo._pentry = NULL;
296         while ((heapstatus = _heapwalk(&hinfo)) == _HEAPOK)
297                 res += hinfo._useflag == _USEDENTRY ? hinfo._size : 0;
298         return res;
299 }
300
301 #else
302
303 size_t ir_get_heap_used_bytes(void)
304 {
305         fprintf(stderr, "function not implemented\n");
306         return 0;
307 }
308
309 #endif
310
311 /* reset a timer */
312 void ir_timer_reset(ir_timer_t *timer)
313 {
314         _time_reset(&timer->elapsed);
315         _time_reset(&timer->start);
316         timer->running = 0;
317 }
318
319 /* start a timer */
320 void ir_timer_start(ir_timer_t *timer)
321 {
322         if (timer->running)
323                 panic("timer started twice");
324
325         _time_reset(&timer->start);
326         _time_get(&timer->start);
327         timer->running = 1;
328
329         if (timer->parent == NULL) {
330                 timer->parent = timer_stack;
331         } else if (timer->parent != timer_stack) {
332                 panic("timer used at different stack positions");
333         }
334         timer_stack = timer;
335 }
336
337 void ir_timer_reset_and_start(ir_timer_t *timer)
338 {
339   _time_reset(&timer->elapsed);
340   ir_timer_start(timer);
341 }
342
343 void ir_timer_stop(ir_timer_t *timer)
344 {
345         if (!timer->running)
346                 panic("attempting to stop stopped timer");
347         if (timer != timer_stack)
348                 panic("timer stack error");
349         timer_stack = timer->parent;
350
351         ir_timer_val_t val;
352         ir_timer_val_t tgt;
353
354         _time_get(&val);
355         timer->running = 0;
356         _time_add(&timer->elapsed, &timer->elapsed, _time_sub(&tgt, &val, &timer->start));
357 }
358
359 void ir_timer_init_parent(ir_timer_t *timer)
360 {
361         if (timer == NULL)
362                 return;
363         if (timer->parent != NULL && timer->parent != timer_stack)
364                 panic("timer parent mismatch");
365         timer->parent = timer_stack;
366 }
367
368 void ir_timer_push(ir_timer_t *timer)
369 {
370         if (timer->running)
371                 panic("timer started twice");
372
373         ir_timer_t *parent = timer->parent;
374         if (timer->parent == NULL)
375                 panic("pushing timer with unknown parent");
376
377         timer->displaced = timer_stack;
378         for (ir_timer_t *t = timer_stack; t != parent; t = t->parent) {
379                 if (t == NULL)
380                         panic("parent timer not on stack");
381                 ir_timer_stop(t);
382         }
383         timer_stack = parent;
384
385         ir_timer_start(timer);
386 }
387
388 static void start_stack(ir_timer_t *timer, ir_timer_t *stop)
389 {
390         if (timer == stop)
391                 return;
392         start_stack(timer->parent, stop);
393         ir_timer_start(timer);
394 }
395
396 void ir_timer_pop(ir_timer_t *timer)
397 {
398         if (!timer->running)
399                 panic("attempting to stop stopped timer");
400         ir_timer_t *displaced = timer->displaced;
401         if (displaced == NULL)
402                 panic("timer start/stop/push/pop mismatch");
403
404         ir_timer_t *parent = timer->parent;
405         timer->displaced = NULL;
406
407         ir_timer_stop(timer);
408         start_stack(displaced, parent);
409 }
410
411 unsigned long ir_timer_elapsed_msec(const ir_timer_t *timer)
412 {
413         ir_timer_val_t v;
414         const ir_timer_val_t *elapsed = &timer->elapsed;
415
416         if (timer->running) {
417                 elapsed = &v;
418                 _time_get(&v);
419                 _time_add(&v, &timer->elapsed, _time_sub(&v, &v, &timer->start));
420         }
421         return _time_to_msec(elapsed);
422 }
423
424 unsigned long ir_timer_elapsed_usec(const ir_timer_t *timer)
425 {
426         ir_timer_val_t v;
427         const ir_timer_val_t *elapsed = &timer->elapsed;
428
429         if (timer->running) {
430                 elapsed = &v;
431                 _time_get(&v);
432                 _time_add(&v, &timer->elapsed, _time_sub(&v, &v, &timer->start));
433         }
434         return _time_to_usec(elapsed);
435 }
436
437 double ir_timer_elapsed_sec(const ir_timer_t *timer)
438 {
439         ir_timer_val_t v;
440         const ir_timer_val_t *elapsed = &timer->elapsed;
441
442         if (timer->running) {
443                 elapsed = &v;
444                 _time_get(&v);
445                 _time_add(&v, &timer->elapsed, _time_sub(&v, &v, &timer->start));
446         }
447         return _time_to_sec(elapsed);
448 }