Log profiler: added copyright header and comments.
[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 <stdlib.h>
17 #include <inttypes.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 #include "utils.h"
29
30 #ifdef HAVE_SYS_TIME_H
31 #include <sys/time.h>
32 #endif
33 #if HAVE_SYS_MMAN_H
34 #include <sys/mman.h>
35 #endif
36
37 #if defined(__APPLE__)
38 #include <mach/mach_time.h>  
39 #include <stdio.h> 
40
41 static mach_timebase_info_data_t timebase_info;
42 #endif
43
44 #ifndef MAP_ANON
45 #define MAP_ANONYMOUS MAP_ANON
46 #endif
47
48 #define TICKS_PER_SEC 1000000000LL
49
50 #if (defined(TARGET_X86) || defined(TARGET_AMD64)) && defined(__linux__)
51 #define HAVE_RDTSC 1
52 #endif
53
54 typedef struct {
55         unsigned int timer_count;
56         int last_cpu;
57         uint64_t last_rdtsc;
58         uint64_t last_time;
59 } TlsData;
60
61 #ifdef HOST_WIN32
62 static int tls_data;
63 #define DECL_TLS_DATA TlsData *tls; tls = (TlsData *) TlsGetValue (tls_data); if (tls == NULL) { tls = (TlsData *) calloc (sizeof (TlsData), 1); TlsSetValue (tls_data, tls); }
64 #define TLS_INIT(x) x = TlsAlloc()
65 #elif HAVE_KW_THREAD
66 static __thread TlsData tls_data;
67 #define DECL_TLS_DATA TlsData *tls = &tls_data
68 #define TLS_INIT(x)
69 #else
70 static pthread_key_t tls_data;
71 #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); }
72 #define TLS_INIT(x) pthread_key_create(&x, NULL)
73 #endif
74
75 #ifdef HOST_WIN32
76 static CRITICAL_SECTION log_lock;
77 #else
78 static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
79 #endif
80
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         TLS_INIT (tls_data);
213 #ifdef HOST_WIN32
214         InitializeCriticalSection (&log_lock);
215 #endif
216 #if defined (__APPLE__)
217         mach_timebase_info (&timebase_info);
218 #endif
219
220         if (fast_time > 1) {
221                 time_func = null_time;
222         } else if (fast_time) {
223                 uint64_t timea;
224                 uint64_t timeb;
225                 clock_time ();
226                 timea = clock_time ();
227                 timeb = clock_time ();
228                 time_inc = (timeb - timea) / TIME_ADJ;
229                 /*printf ("time inc: %llu, timea: %llu, timeb: %llu, diff: %llu\n", time_inc, timea, timeb, timec-timeb);*/
230                 if (have_rdtsc ())
231                         time_func = rdtsc_current_time;
232                 else
233                         time_func = fast_current_time;
234         } else {
235                 time_func = clock_time;
236         }
237 }
238
239 uint64_t
240 current_time (void)
241 {
242         return time_func ();
243 }
244
245 void*
246 alloc_buffer (int size)
247 {
248         void *ptr;
249 #ifdef HOST_WIN32
250         ptr = VirtualAlloc (NULL, size, MEM_COMMIT, PAGE_READWRITE);
251         return ptr;
252 #else
253         ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
254         if (ptr == (void*)-1)
255                 return NULL;
256         return ptr;
257 #endif
258 }
259
260 void
261 free_buffer (void *buf, int size)
262 {
263 #ifdef HOST_WIN32
264         VirtualFree (buf, 0, MEM_RELEASE);
265 #else
266         munmap (buf, size);
267 #endif
268 }
269
270 void
271 take_lock (void)
272 {
273 #ifdef HOST_WIN32
274         EnterCriticalSection (&log_lock);
275 #else
276         pthread_mutex_lock (&log_lock);
277 #endif
278 }
279
280 void
281 release_lock (void)
282 {
283 #ifdef HOST_WIN32
284         LeaveCriticalSection (&log_lock);
285 #else
286         pthread_mutex_unlock (&log_lock);
287 #endif
288 }
289
290 void
291 encode_uleb128 (uint64_t value, uint8_t *buf, uint8_t **endbuf)
292 {
293         uint8_t *p = buf;
294
295         do {
296                 uint8_t b = value & 0x7f;
297                 value >>= 7;
298                 if (value != 0) /* more bytes to come */
299                         b |= 0x80;
300                 *p ++ = b;
301         } while (value);
302
303         *endbuf = p;
304 }
305
306 void
307 encode_sleb128 (intptr_t value, uint8_t *buf, uint8_t **endbuf)
308 {
309         int more = 1;
310         int negative = (value < 0);
311         unsigned int size = sizeof (intptr_t) * 8;
312         uint8_t byte;
313         uint8_t *p = buf;
314
315         while (more) {
316                 byte = value & 0x7f;
317                 value >>= 7;
318                 /* the following is unnecessary if the
319                  * implementation of >>= uses an arithmetic rather
320                  * than logical shift for a signed left operand
321                  */
322                 if (negative)
323                         /* sign extend */
324                         value |= - ((intptr_t)1 <<(size - 7));
325                 /* sign bit of byte is second high order bit (0x40) */
326                 if ((value == 0 && !(byte & 0x40)) ||
327                         (value == -1 && (byte & 0x40)))
328                         more = 0;
329                 else
330                         byte |= 0x80;
331                 *p ++= byte;
332         }
333
334         *endbuf = p;
335 }
336
337 uint64_t
338 decode_uleb128 (uint8_t *buf, uint8_t **endbuf)
339 {
340         uint64_t res = 0;
341         int shift = 0;
342
343         while (1) {
344                 uint8_t b = *buf++;
345
346                 res |= (((uint64_t)(b & 0x7f)) << shift);
347                 if (!(b & 0x80))
348                         break;
349                 shift += 7;
350         }
351
352         *endbuf = buf;
353
354         return res;
355 }
356
357 intptr_t
358 decode_sleb128 (uint8_t *buf, uint8_t **endbuf)
359 {
360         uint8_t *p = buf;
361         intptr_t res = 0;
362         int shift = 0;
363
364         while (1) {
365                 uint8_t b = *p;
366                 p ++;
367
368                 res = res | (((intptr_t)(b & 0x7f)) << shift);
369                 shift += 7;
370                 if (!(b & 0x80)) {
371                         if (shift < sizeof (intptr_t) * 8 && (b & 0x40))
372                                 res |= - ((intptr_t)1 << shift);
373                         break;
374                 }
375         }
376
377         *endbuf = p;
378
379         return res;
380 }
381
382 uintptr_t
383 thread_id (void)
384 {
385 #ifdef HOST_WIN32
386         return (uintptr_t)GetCurrentThreadId ();
387 #else
388         return (uintptr_t)pthread_self ();
389 #endif
390 }
391