Log profiler: osx/win32 portability fixes.
[mono.git] / mono / profiler / utils.c
1 /*
2  * utils.c: log profiler and reporter utils
3  *
4  * We have here the minimal needed portability functions: we can't depend
5  * on the ones provided by the runtime, since they are internal and,
6  * especially mprof-report is an external program.
7  * Note also that we don't take a glib/eglib dependency here for mostly
8  * the same reason (but also because we need tight control in the profiler
9  * over memory allocation, which needs to work with the world stopped).
10  *
11  * Author:
12  *   Paolo Molaro (lupus@ximian.com)
13  *
14  * Copyright 2010 Novell, Inc (http://www.novell.com)
15  */
16 #include "utils.h"
17 #include <stdlib.h>
18 #include <time.h>
19 #include <stdio.h>
20 #include <string.h>
21 #ifdef HOST_WIN32
22 #include <windows.h>
23 #else
24 #include <pthread.h>
25 #include <sched.h>
26 #endif
27
28
29 #ifdef HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32 #if HAVE_SYS_MMAN_H
33 #include <sys/mman.h>
34 #endif
35
36 #if defined(__APPLE__)
37 #include <mach/mach_time.h>  
38 #include <stdio.h> 
39
40 static mach_timebase_info_data_t timebase_info;
41 #endif
42
43 #ifndef MAP_ANONYMOUS
44 #define MAP_ANONYMOUS MAP_ANON
45 #endif
46
47 #define TICKS_PER_SEC 1000000000LL
48
49 #if (defined(TARGET_X86) || defined(TARGET_AMD64)) && defined(__linux__)
50 #define HAVE_RDTSC 1
51 #endif
52
53 typedef struct {
54         unsigned int timer_count;
55         int last_cpu;
56         uint64_t last_rdtsc;
57         uint64_t last_time;
58 } TlsData;
59
60 #ifdef HOST_WIN32
61 static int tls_data;
62 #define DECL_TLS_DATA TlsData *tls; tls = (TlsData *) TlsGetValue (tls_data); if (tls == NULL) { tls = (TlsData *) calloc (sizeof (TlsData), 1); TlsSetValue (tls_data, tls); }
63 #define TLS_INIT(x) x = TlsAlloc()
64 #elif HAVE_KW_THREAD
65 static __thread TlsData tls_data;
66 #define DECL_TLS_DATA TlsData *tls = &tls_data
67 #define TLS_INIT(x)
68 #else
69 static pthread_key_t tls_data;
70 #define DECL_TLS_DATA TlsData *tls; tls = (TlsData *) pthread_getspecific (tls_data); if (tls == NULL) { tls = (TlsData *) calloc (sizeof (TlsData), 1); pthread_setspecific (tls_data, tls); }
71 #define TLS_INIT(x) pthread_key_create(&x, NULL)
72 #endif
73
74 #ifdef HOST_WIN32
75 static CRITICAL_SECTION log_lock;
76 static LARGE_INTEGER pcounter_freq;
77 #else
78 static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
79 #endif
80
81 static int timer_overhead = 0;
82 static uint64_t time_inc = 0;
83 typedef uint64_t (*TimeFunc)(void);
84
85 static TimeFunc time_func;
86
87 static uint64_t
88 clock_time (void)
89 {
90 #if defined(__APPLE__)
91         uint64_t time = mach_absolute_time ();
92         
93         time *= timebase_info.numer;
94         time /= timebase_info.denom;
95
96         return time;
97 #elif defined(HOST_WIN32)
98         LARGE_INTEGER value;
99         QueryPerformanceCounter (&value);
100         return value.QuadPart * TICKS_PER_SEC / pcounter_freq.QuadPart;
101 #elif defined(CLOCK_MONOTONIC)
102         struct timespec tspec;
103         clock_gettime (CLOCK_MONOTONIC, &tspec);
104         return ((uint64_t)tspec.tv_sec * TICKS_PER_SEC + tspec.tv_nsec);
105 #else
106         struct timeval tv;
107         gettimeofday (&tv, NULL);
108         return ((uint64_t)tv.tv_sec * TICKS_PER_SEC + tv.tv_usec * 1000);
109 #endif
110 }
111
112 /* must be power of two */
113 #define TIME_ADJ 8
114
115 static uint64_t
116 fast_current_time (void)
117 {
118         DECL_TLS_DATA;
119         if (tls->timer_count++ & (TIME_ADJ - 1)) {
120                 tls->last_time += time_inc;
121                 return tls->last_time;
122         }
123         tls->last_time = clock_time ();
124         return tls->last_time;
125 }
126
127 #if HAVE_RDTSC
128
129 #define rdtsc(low,high) \
130         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
131
132 static uint64_t
133 safe_rdtsc (int *cpu)
134 {
135         unsigned int low, high;
136         int c1 = sched_getcpu ();
137         int c2;
138         rdtsc (low, high);
139         c2 = sched_getcpu ();
140         if (c1 != c2) {
141                 *cpu = -1;
142                 return 0;
143         }
144         *cpu = c1;
145         return (((uint64_t) high) << 32) + (uint64_t)low;
146 }
147
148 static double cpu_freq;
149
150 static int 
151 have_rdtsc (void) {
152         char buf[256];
153         int have_freq = 0;
154         int have_flag = 0;
155         float val;
156         FILE *cpuinfo;
157         int cpu = sched_getcpu ();
158
159         if (cpu < 0)
160                 return 0;
161
162         if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
163                 return 0;
164         while (fgets (buf, sizeof(buf), cpuinfo)) {
165                 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
166                         /*printf ("got mh: %f\n", val);*/
167                         have_freq = 1;
168                         cpu_freq = val * 1000000;
169                 }
170                 if (strncmp (buf, "flags :", 5) == 0) {
171                         if (strstr (buf, "constant_tsc")) {
172                                 have_flag = 1;
173                                 /*printf ("have tsc\n");*/
174                         }
175                 }
176         }
177         fclose (cpuinfo);
178         return have_flag? have_freq: 0;
179 }
180
181 static uint64_t
182 rdtsc_current_time (void)
183 {
184         DECL_TLS_DATA;
185         if (tls->timer_count++ & (TIME_ADJ*8 - 1)) {
186                 int cpu;
187                 uint64_t tsc = safe_rdtsc (&cpu);
188                 if (cpu != -1 && cpu == tls->last_cpu) {
189                         int64_t diff = tsc - tls->last_rdtsc;
190                         uint64_t nsecs;
191                         if (diff > 0) {
192                                 nsecs = (double)diff/cpu_freq;
193                                 //printf ("%llu cycles: %llu nsecs\n", diff, nsecs);
194                                 return tls->last_time + nsecs;
195                         } else {
196                                 printf ("tsc went backwards\n");
197                         }
198                 } else {
199                         //printf ("wrong cpu: %d\n", cpu);
200                 }
201         }
202         tls->last_time = clock_time ();
203         tls->last_rdtsc = safe_rdtsc (&tls->last_cpu);
204         return tls->last_time;
205 }
206 #else
207 #define have_rdtsc() 0
208 #define rdtsc_current_time fast_current_time
209 #endif
210
211 static uint64_t
212 null_time (void)
213 {
214         static uint64_t timer = 0;
215         return timer++;
216 }
217
218 void
219 utils_init (int fast_time)
220 {
221         int i;
222         uint64_t time_start, time_end;
223         TLS_INIT (tls_data);
224 #ifdef HOST_WIN32
225         InitializeCriticalSection (&log_lock);
226         QueryPerformanceFrequency (&pcounter_freq);
227 #endif
228 #if defined (__APPLE__)
229         mach_timebase_info (&timebase_info);
230 #endif
231
232         if (fast_time > 1) {
233                 time_func = null_time;
234         } else if (fast_time) {
235                 uint64_t timea;
236                 uint64_t timeb;
237                 clock_time ();
238                 timea = clock_time ();
239                 timeb = clock_time ();
240                 time_inc = (timeb - timea) / TIME_ADJ;
241                 /*printf ("time inc: %llu, timea: %llu, timeb: %llu, diff: %llu\n", time_inc, timea, timeb, timec-timeb);*/
242                 if (have_rdtsc ())
243                         time_func = rdtsc_current_time;
244                 else
245                         time_func = fast_current_time;
246         } else {
247                 time_func = clock_time;
248         }
249         time_start = time_func ();
250         for (i = 0; i < 256; ++i)
251                 time_func ();
252         time_end = time_func ();
253         timer_overhead = (time_end - time_start) / 256;
254 }
255
256 int
257 get_timer_overhead (void)
258 {
259         return timer_overhead;
260 }
261
262 uint64_t
263 current_time (void)
264 {
265         return time_func ();
266 }
267
268 void*
269 alloc_buffer (int size)
270 {
271         void *ptr;
272 #ifdef HOST_WIN32
273         ptr = VirtualAlloc (NULL, size, MEM_COMMIT, PAGE_READWRITE);
274         return ptr;
275 #else
276         ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
277         if (ptr == (void*)-1)
278                 return NULL;
279         return ptr;
280 #endif
281 }
282
283 void
284 free_buffer (void *buf, int size)
285 {
286 #ifdef HOST_WIN32
287         VirtualFree (buf, 0, MEM_RELEASE);
288 #else
289         munmap (buf, size);
290 #endif
291 }
292
293 void
294 take_lock (void)
295 {
296 #ifdef HOST_WIN32
297         EnterCriticalSection (&log_lock);
298 #else
299         pthread_mutex_lock (&log_lock);
300 #endif
301 }
302
303 void
304 release_lock (void)
305 {
306 #ifdef HOST_WIN32
307         LeaveCriticalSection (&log_lock);
308 #else
309         pthread_mutex_unlock (&log_lock);
310 #endif
311 }
312
313 void
314 encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
315 {
316         uint8_t *p = buf;
317
318         do {
319                 uint8_t b = value & 0x7f;
320                 value >>= 7;
321                 if (value != 0) /* more bytes to come */
322                         b |= 0x80;
323                 *p ++ = b;
324         } while (value);
325
326         *endbuf = p;
327 }
328
329 void
330 encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
331 {
332         int more = 1;
333         int negative = (value < 0);
334         unsigned int size = sizeof (intptr_t) * 8;
335         uint8_t byte;
336         uint8_t *p = buf;
337
338         while (more) {
339                 byte = value & 0x7f;
340                 value >>= 7;
341                 /* the following is unnecessary if the
342                  * implementation of >>= uses an arithmetic rather
343                  * than logical shift for a signed left operand
344                  */
345                 if (negative)
346                         /* sign extend */
347                         value |= - ((intptr_t)1 <<(size - 7));
348                 /* sign bit of byte is second high order bit (0x40) */
349                 if ((value == 0 && !(byte & 0x40)) ||
350                         (value == -1 && (byte & 0x40)))
351                         more = 0;
352                 else
353                         byte |= 0x80;
354                 *p ++= byte;
355         }
356
357         *endbuf = p;
358 }
359
360 uint64_t
361 decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
362 {
363         uint64_t res = 0;
364         int shift = 0;
365
366         while (1) {
367                 uint8_t b = *buf++;
368
369                 res |= (((uint64_t)(b & 0x7f)) << shift);
370                 if (!(b & 0x80))
371                         break;
372                 shift += 7;
373         }
374
375         *endbuf = buf;
376
377         return res;
378 }
379
380 intptr_t
381 decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
382 {
383         uint8_t *p = buf;
384         intptr_t res = 0;
385         int shift = 0;
386
387         while (1) {
388                 uint8_t b = *p;
389                 p ++;
390
391                 res = res | (((intptr_t)(b & 0x7f)) << shift);
392                 shift += 7;
393                 if (!(b & 0x80)) {
394                         if (shift < sizeof (intptr_t) * 8 && (b & 0x40))
395                                 res |= - ((intptr_t)1 << shift);
396                         break;
397                 }
398         }
399
400         *endbuf = p;
401
402         return res;
403 }
404
405 uintptr_t
406 thread_id (void)
407 {
408 #ifdef HOST_WIN32
409         return (uintptr_t)GetCurrentThreadId ();
410 #else
411         return (uintptr_t)pthread_self ();
412 #endif
413 }
414