e36e82e7f67ee288278ab7088ed85fc86f41f817
[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_ANON
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 #else
77 static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
78 #endif
79
80 static int timer_overhead = 0;
81 static uint64_t time_inc = 0;
82 typedef uint64_t (*TimeFunc)(void);
83
84 static TimeFunc time_func;
85
86 static uint64_t
87 clock_time (void)
88 {
89 #if defined(__APPLE__)
90         uint64_t time = mach_absolute_time ();
91         
92         time *= info.numer;
93         time /= info.denom;
94
95         return time;
96 #else
97         struct timespec tspec;
98         clock_gettime (CLOCK_MONOTONIC, &tspec);
99         return ((uint64_t)tspec.tv_sec * TICKS_PER_SEC + tspec.tv_nsec);
100 #endif
101 }
102
103 /* must be power of two */
104 #define TIME_ADJ 8
105
106 static uint64_t
107 fast_current_time (void)
108 {
109         DECL_TLS_DATA;
110         if (tls->timer_count++ & (TIME_ADJ - 1)) {
111                 tls->last_time += time_inc;
112                 return tls->last_time;
113         }
114         tls->last_time = clock_time ();
115         return tls->last_time;
116 }
117
118 #if HAVE_RDTSC
119
120 #define rdtsc(low,high) \
121         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
122
123 static uint64_t
124 safe_rdtsc (int *cpu)
125 {
126         unsigned int low, high;
127         int c1 = sched_getcpu ();
128         int c2;
129         rdtsc (low, high);
130         c2 = sched_getcpu ();
131         if (c1 != c2) {
132                 *cpu = -1;
133                 return 0;
134         }
135         *cpu = c1;
136         return (((uint64_t) high) << 32) + (uint64_t)low;
137 }
138
139 static double cpu_freq;
140
141 static int 
142 have_rdtsc (void) {
143         char buf[256];
144         int have_freq = 0;
145         int have_flag = 0;
146         float val;
147         FILE *cpuinfo;
148         int cpu = sched_getcpu ();
149
150         if (cpu < 0)
151                 return 0;
152
153         if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
154                 return 0;
155         while (fgets (buf, sizeof(buf), cpuinfo)) {
156                 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
157                         /*printf ("got mh: %f\n", val);*/
158                         have_freq = 1;
159                         cpu_freq = val * 1000000;
160                 }
161                 if (strncmp (buf, "flags :", 5) == 0) {
162                         if (strstr (buf, "constant_tsc")) {
163                                 have_flag = 1;
164                                 /*printf ("have tsc\n");*/
165                         }
166                 }
167         }
168         fclose (cpuinfo);
169         return have_flag? have_freq: 0;
170 }
171
172 static uint64_t
173 rdtsc_current_time (void)
174 {
175         DECL_TLS_DATA;
176         if (tls->timer_count++ & (TIME_ADJ*8 - 1)) {
177                 int cpu;
178                 uint64_t tsc = safe_rdtsc (&cpu);
179                 if (cpu != -1 && cpu == tls->last_cpu) {
180                         int64_t diff = tsc - tls->last_rdtsc;
181                         uint64_t nsecs;
182                         if (diff > 0) {
183                                 nsecs = (double)diff/cpu_freq;
184                                 //printf ("%llu cycles: %llu nsecs\n", diff, nsecs);
185                                 return tls->last_time + nsecs;
186                         } else {
187                                 printf ("tsc went backwards\n");
188                         }
189                 } else {
190                         //printf ("wrong cpu: %d\n", cpu);
191                 }
192         }
193         tls->last_time = clock_time ();
194         tls->last_rdtsc = safe_rdtsc (&tls->last_cpu);
195         return tls->last_time;
196 }
197 #else
198 #define have_rdtsc() 0
199 #define rdtsc_current_time fast_current_time
200 #endif
201
202 static uint64_t
203 null_time (void)
204 {
205         static uint64_t timer = 0;
206         return timer++;
207 }
208
209 void
210 utils_init (int fast_time)
211 {
212         int i;
213         uint64_t time_start, time_end;
214         TLS_INIT (tls_data);
215 #ifdef HOST_WIN32
216         InitializeCriticalSection (&log_lock);
217 #endif
218 #if defined (__APPLE__)
219         mach_timebase_info (&timebase_info);
220 #endif
221
222         if (fast_time > 1) {
223                 time_func = null_time;
224         } else if (fast_time) {
225                 uint64_t timea;
226                 uint64_t timeb;
227                 clock_time ();
228                 timea = clock_time ();
229                 timeb = clock_time ();
230                 time_inc = (timeb - timea) / TIME_ADJ;
231                 /*printf ("time inc: %llu, timea: %llu, timeb: %llu, diff: %llu\n", time_inc, timea, timeb, timec-timeb);*/
232                 if (have_rdtsc ())
233                         time_func = rdtsc_current_time;
234                 else
235                         time_func = fast_current_time;
236         } else {
237                 time_func = clock_time;
238         }
239         time_start = time_func ();
240         for (i = 0; i < 256; ++i)
241                 time_func ();
242         time_end = time_func ();
243         timer_overhead = (time_end - time_start) / 256;
244 }
245
246 int
247 get_timer_overhead (void)
248 {
249         return timer_overhead;
250 }
251
252 uint64_t
253 current_time (void)
254 {
255         return time_func ();
256 }
257
258 void*
259 alloc_buffer (int size)
260 {
261         void *ptr;
262 #ifdef HOST_WIN32
263         ptr = VirtualAlloc (NULL, size, MEM_COMMIT, PAGE_READWRITE);
264         return ptr;
265 #else
266         ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
267         if (ptr == (void*)-1)
268                 return NULL;
269         return ptr;
270 #endif
271 }
272
273 void
274 free_buffer (void *buf, int size)
275 {
276 #ifdef HOST_WIN32
277         VirtualFree (buf, 0, MEM_RELEASE);
278 #else
279         munmap (buf, size);
280 #endif
281 }
282
283 void
284 take_lock (void)
285 {
286 #ifdef HOST_WIN32
287         EnterCriticalSection (&log_lock);
288 #else
289         pthread_mutex_lock (&log_lock);
290 #endif
291 }
292
293 void
294 release_lock (void)
295 {
296 #ifdef HOST_WIN32
297         LeaveCriticalSection (&log_lock);
298 #else
299         pthread_mutex_unlock (&log_lock);
300 #endif
301 }
302
303 void
304 encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
305 {
306         uint8_t *p = buf;
307
308         do {
309                 uint8_t b = value & 0x7f;
310                 value >>= 7;
311                 if (value != 0) /* more bytes to come */
312                         b |= 0x80;
313                 *p ++ = b;
314         } while (value);
315
316         *endbuf = p;
317 }
318
319 void
320 encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
321 {
322         int more = 1;
323         int negative = (value < 0);
324         unsigned int size = sizeof (intptr_t) * 8;
325         uint8_t byte;
326         uint8_t *p = buf;
327
328         while (more) {
329                 byte = value & 0x7f;
330                 value >>= 7;
331                 /* the following is unnecessary if the
332                  * implementation of >>= uses an arithmetic rather
333                  * than logical shift for a signed left operand
334                  */
335                 if (negative)
336                         /* sign extend */
337                         value |= - ((intptr_t)1 <<(size - 7));
338                 /* sign bit of byte is second high order bit (0x40) */
339                 if ((value == 0 && !(byte & 0x40)) ||
340                         (value == -1 && (byte & 0x40)))
341                         more = 0;
342                 else
343                         byte |= 0x80;
344                 *p ++= byte;
345         }
346
347         *endbuf = p;
348 }
349
350 uint64_t
351 decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
352 {
353         uint64_t res = 0;
354         int shift = 0;
355
356         while (1) {
357                 uint8_t b = *buf++;
358
359                 res |= (((uint64_t)(b & 0x7f)) << shift);
360                 if (!(b & 0x80))
361                         break;
362                 shift += 7;
363         }
364
365         *endbuf = buf;
366
367         return res;
368 }
369
370 intptr_t
371 decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
372 {
373         uint8_t *p = buf;
374         intptr_t res = 0;
375         int shift = 0;
376
377         while (1) {
378                 uint8_t b = *p;
379                 p ++;
380
381                 res = res | (((intptr_t)(b & 0x7f)) << shift);
382                 shift += 7;
383                 if (!(b & 0x80)) {
384                         if (shift < sizeof (intptr_t) * 8 && (b & 0x40))
385                                 res |= - ((intptr_t)1 << shift);
386                         break;
387                 }
388         }
389
390         *endbuf = p;
391
392         return res;
393 }
394
395 uintptr_t
396 thread_id (void)
397 {
398 #ifdef HOST_WIN32
399         return (uintptr_t)GetCurrentThreadId ();
400 #else
401         return (uintptr_t)pthread_self ();
402 #endif
403 }
404