Merge branch 'marek'
[mono.git] / mono / profiler / utils.c
1 #include <stdlib.h>
2 #include <inttypes.h>
3 #include <time.h>
4 #include <pthread.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <sched.h>
8
9 #include "utils.h"
10
11 //#ifdef HAVE_SYS_TIME_H
12 #include <sys/time.h>
13 //#endif
14 //#if HAVE_SYS_MMAN_H
15 #include <sys/mman.h>
16 //#endif
17
18 #if defined(__APPLE__)
19 #include <mach/mach_time.h>  
20 #include <stdio.h> 
21
22 static mach_timebase_info_data_t timebase_info;
23 #endif
24
25 #ifndef MAP_ANON
26 #define MAP_ANONYMOUS MAP_ANON
27 #endif
28
29 #define TICKS_PER_SEC 1000000000LL
30
31 typedef struct {
32         unsigned int timer_count;
33         int last_cpu;
34         uint64_t last_rdtsc;
35         uint64_t last_time;
36 } TlsData;
37
38 #if HAVE_KW_THREAD
39 static __thread TlsData tls_data;
40 #define DECL_TLS_DATA TlsData *tls = &tls_data
41 #define TLS_INIT(x)
42 #else
43 static pthread_key_t tls_data;
44 #define DECL_TLS_DATA TlsData *tls; tls = (TlsData *) pthread_getspecific (tls_data); if (tls == NULL) { tls = (TlsData *) malloc (sizeof (TlsData)); pthread_setspecific (tls_data, tls); }
45 #define TLS_INIT(x) pthread_key_create(&x, NULL)
46 #endif
47
48 static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
49
50 static uint64_t time_inc = 0;
51 typedef uint64_t (*TimeFunc)(void);
52
53 static TimeFunc time_func;
54
55 static uint64_t
56 clock_time (void)
57 {
58 #if defined(__APPLE__)
59         uint64_t time = mach_absolute_time ();
60         
61         time *= info.numer;
62         time /= info.denom;
63
64         return time;
65 #else
66         struct timespec tspec;
67         clock_gettime (CLOCK_MONOTONIC, &tspec);
68         return ((uint64_t)tspec.tv_sec * TICKS_PER_SEC + tspec.tv_nsec);
69 #endif
70 }
71
72 /* must be power of two */
73 #define TIME_ADJ 8
74
75 static uint64_t
76 fast_current_time (void)
77 {
78         DECL_TLS_DATA;
79         if (tls->timer_count++ & (TIME_ADJ - 1)) {
80                 tls->last_time += time_inc;
81                 return tls->last_time;
82         }
83         tls->last_time = clock_time ();
84         return tls->last_time;
85 }
86
87 #define rdtsc(low,high) \
88         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
89
90 #if !defined(__APPLE__)
91 static uint64_t
92 safe_rdtsc (int *cpu)
93 {
94         unsigned int low, high;
95         int c1 = sched_getcpu ();
96         int c2;
97         rdtsc (low, high);
98         c2 = sched_getcpu ();
99         if (c1 != c2) {
100                 *cpu = -1;
101                 return 0;
102         }
103         *cpu = c1;
104         return (((uint64_t) high) << 32) + (uint64_t)low;
105 }
106
107 static double cpu_freq;
108
109 static int 
110 have_rdtsc (void) {
111         char buf[256];
112         int have_freq = 0;
113         int have_flag = 0;
114         float val;
115         FILE *cpuinfo;
116
117         if (!(cpuinfo = fopen ("/proc/cpuinfo", "r")))
118                 return 0;
119         while (fgets (buf, sizeof(buf), cpuinfo)) {
120                 if (sscanf (buf, "cpu MHz : %f", &val) == 1) {
121                         /*printf ("got mh: %f\n", val);*/
122                         have_freq = 1;
123                         cpu_freq = val * 1000000;
124                 }
125                 if (strncmp (buf, "flags :", 5) == 0) {
126                         if (strstr (buf, "constant_tsc")) {
127                                 have_flag = 1;
128                                 /*printf ("have tsc\n");*/
129                         }
130                 }
131         }
132         fclose (cpuinfo);
133         return have_flag? have_freq: 0;
134 }
135
136 static uint64_t
137 rdtsc_current_time (void)
138 {
139         DECL_TLS_DATA;
140         if (tls->timer_count++ & (TIME_ADJ*8 - 1)) {
141                 int cpu;
142                 uint64_t tsc = safe_rdtsc (&cpu);
143                 if (cpu != -1 && cpu == tls->last_cpu) {
144                         int64_t diff = tsc - tls->last_rdtsc;
145                         uint64_t nsecs;
146                         if (diff > 0) {
147                                 nsecs = (double)diff/cpu_freq;
148                                 //printf ("%llu cycles: %llu nsecs\n", diff, nsecs);
149                                 return tls->last_time + nsecs;
150                         } else {
151                                 printf ("tsc went backwards\n");
152                         }
153                 } else {
154                         //printf ("wrong cpu: %d\n", cpu);
155                 }
156         }
157         tls->last_time = clock_time ();
158         tls->last_rdtsc = safe_rdtsc (&tls->last_cpu);
159         return tls->last_time;
160 }
161 #endif
162
163 static uint64_t
164 null_time (void)
165 {
166         static uint64_t timer = 0;
167         return timer++;
168 }
169
170 void
171 utils_init (int fast_time)
172 {
173         TLS_INIT (tls_data);
174
175         if (fast_time > 1) {
176                 time_func = null_time;
177         } else if (fast_time) {
178 #if defined (__APPLE__)
179                 mach_timebase_info (&timebase_info);
180                 time_func = fast_current_time;
181 #else
182                 uint64_t timea;
183                 uint64_t timeb;
184                 int cpu = sched_getcpu ();
185                 clock_time ();
186                 timea = clock_time ();
187                 timeb = clock_time ();
188                 time_inc = (timeb - timea) / TIME_ADJ;
189                 /*printf ("time inc: %llu, timea: %llu, timeb: %llu, diff: %llu\n", time_inc, timea, timeb, timec-timeb);*/
190                 if (cpu != -1 && have_rdtsc ())
191                         time_func = rdtsc_current_time;
192                 else
193                         time_func = fast_current_time;
194 #endif
195         } else {
196                 time_func = clock_time;
197         }
198 }
199
200 uint64_t
201 current_time (void)
202 {
203         return time_func ();
204 }
205
206 void*
207 alloc_buffer (int size)
208 {
209         void *ptr;
210         ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
211         if (ptr == (void*)-1)
212                 return NULL;
213         return ptr;
214 }
215
216 void
217 free_buffer (void *buf, int size)
218 {
219         munmap (buf, size);
220 }
221
222 void
223 take_lock (void)
224 {
225         pthread_mutex_lock (&log_lock);
226 }
227
228 void
229 release_lock (void)
230 {
231         pthread_mutex_unlock (&log_lock);
232 }
233
234 void
235 encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
236 {
237         uint8_t *p = buf;
238
239         do {
240                 uint8_t b = value & 0x7f;
241                 value >>= 7;
242                 if (value != 0) /* more bytes to come */
243                         b |= 0x80;
244                 *p ++ = b;
245         } while (value);
246
247         *endbuf = p;
248 }
249
250 /* FIXME: make sure this works for 64 bit systems/values */
251 void
252 encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
253 {
254         int more = 1;
255         int negative = (value < 0);
256         unsigned int size = 32;
257         uint8_t byte;
258         uint8_t *p = buf;
259
260         while (more) {
261                 byte = value & 0x7f;
262                 value >>= 7;
263                 /* the following is unnecessary if the
264                  * implementation of >>= uses an arithmetic rather
265                  * than logical shift for a signed left operand
266                  */
267                 if (negative)
268                         /* sign extend */
269                         value |= - (1 <<(size - 7));
270                 /* sign bit of byte is second high order bit (0x40) */
271                 if ((value == 0 && !(byte & 0x40)) ||
272                         (value == -1 && (byte & 0x40)))
273                         more = 0;
274                 else
275                         byte |= 0x80;
276                 *p ++= byte;
277         }
278
279         *endbuf = p;
280 }
281
282 uint64_t
283 decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
284 {
285         uint64_t res = 0;
286         int shift = 0;
287
288         while (1) {
289                 uint8_t b = *buf++;
290
291                 res |= (((uint64_t)(b & 0x7f)) << shift);
292                 if (!(b & 0x80))
293                         break;
294                 shift += 7;
295         }
296
297         *endbuf = buf;
298
299         return res;
300 }
301
302 intptr_t
303 decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
304 {
305         uint8_t *p = buf;
306         intptr_t res = 0;
307         int shift = 0;
308
309         while (1) {
310                 uint8_t b = *p;
311                 p ++;
312
313                 res = res | (((int)(b & 0x7f)) << shift);
314                 shift += 7;
315                 if (!(b & 0x80)) {
316                         if (shift < 32 && (b & 0x40))
317                                 res |= - (1 << shift);
318                         break;
319                 }
320         }
321
322         *endbuf = p;
323
324         return res;
325 }
326
327 uintptr_t
328 thread_id (void)
329 {
330         return (uintptr_t)pthread_self ();
331 }
332