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