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