|
1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "VMPI.h" |
|
7 |
|
8 // Note, this is not supported in configurations with more than one AvmCore running |
|
9 // in the same process. |
|
10 |
|
11 #ifdef WIN32 |
|
12 #include "windows.h" |
|
13 #else |
|
14 #define __cdecl |
|
15 #include <stdarg.h> |
|
16 #include <string.h> |
|
17 #endif |
|
18 |
|
19 #include "vprof.h" |
|
20 |
|
21 #ifndef MIN |
|
22 #define MIN(x,y) ((x) <= (y) ? x : y) |
|
23 #endif |
|
24 #ifndef MAX |
|
25 #define MAX(x,y) ((x) >= (y) ? x : y) |
|
26 #endif |
|
27 |
|
28 #ifndef MAXINT |
|
29 #define MAXINT int(unsigned(-1)>>1) |
|
30 #endif |
|
31 |
|
32 #ifndef MAXINT64 |
|
33 #define MAXINT64 int64_t(uint64_t(-1)>>1) |
|
34 #endif |
|
35 |
|
36 #ifndef __STDC_WANT_SECURE_LIB__ |
|
37 #define sprintf_s(b,size,fmt,...) sprintf((b),(fmt),__VA_ARGS__) |
|
38 #endif |
|
39 |
|
40 #if THREADED |
|
41 #define DO_LOCK(lock) Lock(lock); { |
|
42 #define DO_UNLOCK(lock) }; Unlock(lock) |
|
43 #else |
|
44 #define DO_LOCK(lock) { (void)(lock); |
|
45 #define DO_UNLOCK(lock) } |
|
46 #endif |
|
47 |
|
48 #if THREAD_SAFE |
|
49 #define LOCK(lock) DO_LOCK(lock) |
|
50 #define UNLOCK(lock) DO_UNLOCK(lock) |
|
51 #else |
|
52 #define LOCK(lock) { (void)(lock); |
|
53 #define UNLOCK(lock) } |
|
54 #endif |
|
55 |
|
56 static entry* entries = nullptr; |
|
57 static bool notInitialized = true; |
|
58 static long glock = LOCK_IS_FREE; |
|
59 |
|
60 #define Lock(lock) while (_InterlockedCompareExchange(lock, LOCK_IS_TAKEN, LOCK_IS_FREE) == LOCK_IS_TAKEN){}; |
|
61 #define Unlock(lock) _InterlockedCompareExchange(lock, LOCK_IS_FREE, LOCK_IS_TAKEN); |
|
62 |
|
63 #if defined(WIN32) |
|
64 static void vprof_printf(const char* format, ...) |
|
65 { |
|
66 va_list args; |
|
67 va_start(args, format); |
|
68 |
|
69 char buf[1024]; |
|
70 vsnprintf(buf, sizeof(buf), format, args); |
|
71 |
|
72 va_end(args); |
|
73 |
|
74 printf(buf); |
|
75 ::OutputDebugStringA(buf); |
|
76 } |
|
77 #else |
|
78 #define vprof_printf printf |
|
79 #endif |
|
80 |
|
81 static inline entry* reverse (entry* s) |
|
82 { |
|
83 entry_t e, n, p; |
|
84 |
|
85 p = nullptr; |
|
86 for (e = s; e; e = n) { |
|
87 n = e->next; |
|
88 e->next = p; |
|
89 p = e; |
|
90 } |
|
91 |
|
92 return p; |
|
93 } |
|
94 |
|
95 static char* f (double d) |
|
96 { |
|
97 static char s[80]; |
|
98 char* p; |
|
99 sprintf_s (s, sizeof(s), "%lf", d); |
|
100 p = s+VMPI_strlen(s)-1; |
|
101 while (*p == '0') { |
|
102 *p = '\0'; |
|
103 p--; |
|
104 if (p == s) break; |
|
105 } |
|
106 if (*p == '.') *p = '\0'; |
|
107 return s; |
|
108 } |
|
109 |
|
110 static void dumpProfile (void) |
|
111 { |
|
112 entry_t e; |
|
113 |
|
114 entries = reverse(entries); |
|
115 vprof_printf ("event avg [min : max] total count\n"); |
|
116 for (e = entries; e; e = e->next) { |
|
117 if (e->count == 0) continue; // ignore entries with zero count. |
|
118 vprof_printf ("%s", e->file); |
|
119 if (e->line >= 0) { |
|
120 vprof_printf (":%d", e->line); |
|
121 } |
|
122 vprof_printf (" %s [%lld : %lld] %lld %lld ", |
|
123 f(((double)e->sum)/((double)e->count)), (long long int)e->min, (long long int)e->max, (long long int)e->sum, (long long int)e->count); |
|
124 if (e->h) { |
|
125 int j = MAXINT; |
|
126 for (j = 0; j < e->h->nbins; j ++) { |
|
127 vprof_printf ("(%lld < %lld) ", (long long int)e->h->count[j], (long long int)e->h->lb[j]); |
|
128 } |
|
129 vprof_printf ("(%lld >= %lld) ", (long long int)e->h->count[e->h->nbins], (long long int)e->h->lb[e->h->nbins-1]); |
|
130 } |
|
131 if (e->func) { |
|
132 int j; |
|
133 for (j = 0; j < NUM_EVARS; j++) { |
|
134 if (e->ivar[j] != 0) { |
|
135 vprof_printf ("IVAR%d %d ", j, e->ivar[j]); |
|
136 } |
|
137 } |
|
138 for (j = 0; j < NUM_EVARS; j++) { |
|
139 if (e->i64var[j] != 0) { |
|
140 vprof_printf ("I64VAR%d %lld ", j, (long long int)e->i64var[j]); |
|
141 } |
|
142 } |
|
143 for (j = 0; j < NUM_EVARS; j++) { |
|
144 if (e->dvar[j] != 0) { |
|
145 vprof_printf ("DVAR%d %lf ", j, e->dvar[j]); |
|
146 } |
|
147 } |
|
148 } |
|
149 vprof_printf ("\n"); |
|
150 } |
|
151 entries = reverse(entries); |
|
152 } |
|
153 |
|
154 static inline entry_t findEntry (char* file, int line) |
|
155 { |
|
156 for (entry_t e = entries; e; e = e->next) { |
|
157 if ((e->line == line) && (VMPI_strcmp (e->file, file) == 0)) { |
|
158 return e; |
|
159 } |
|
160 } |
|
161 return nullptr; |
|
162 } |
|
163 |
|
164 // Initialize the location pointed to by 'id' to a new value profile entry |
|
165 // associated with 'file' and 'line', or do nothing if already initialized. |
|
166 // An optional final argument provides a user-defined probe function. |
|
167 |
|
168 int initValueProfile(void** id, char* file, int line, ...) |
|
169 { |
|
170 DO_LOCK (&glock); |
|
171 entry_t e = (entry_t) *id; |
|
172 if (notInitialized) { |
|
173 atexit (dumpProfile); |
|
174 notInitialized = false; |
|
175 } |
|
176 |
|
177 if (e == nullptr) { |
|
178 e = findEntry (file, line); |
|
179 if (e) { |
|
180 *id = e; |
|
181 } |
|
182 } |
|
183 |
|
184 if (e == nullptr) { |
|
185 va_list va; |
|
186 e = (entry_t) malloc (sizeof(entry)); |
|
187 e->lock = LOCK_IS_FREE; |
|
188 e->file = file; |
|
189 e->line = line; |
|
190 e->value = 0; |
|
191 e->sum = 0; |
|
192 e->count = 0; |
|
193 e->min = 0; |
|
194 e->max = 0; |
|
195 // optional probe function argument |
|
196 va_start (va, line); |
|
197 e->func = (void (__cdecl*)(void*)) va_arg (va, void*); |
|
198 va_end (va); |
|
199 e->h = nullptr; |
|
200 e->genptr = nullptr; |
|
201 VMPI_memset (&e->ivar, 0, sizeof(e->ivar)); |
|
202 VMPI_memset (&e->i64var, 0, sizeof(e->i64var)); |
|
203 VMPI_memset (&e->dvar, 0, sizeof(e->dvar)); |
|
204 e->next = entries; |
|
205 entries = e; |
|
206 *id = e; |
|
207 } |
|
208 DO_UNLOCK (&glock); |
|
209 |
|
210 return 0; |
|
211 } |
|
212 |
|
213 // Record a value profile event. |
|
214 |
|
215 int profileValue(void* id, int64_t value) |
|
216 { |
|
217 entry_t e = (entry_t) id; |
|
218 long* lock = &(e->lock); |
|
219 LOCK (lock); |
|
220 e->value = value; |
|
221 if (e->count == 0) { |
|
222 e->sum = value; |
|
223 e->count = 1; |
|
224 e->min = value; |
|
225 e->max = value; |
|
226 } else { |
|
227 e->sum += value; |
|
228 e->count ++; |
|
229 e->min = MIN (e->min, value); |
|
230 e->max = MAX (e->max, value); |
|
231 } |
|
232 if (e->func) e->func (e); |
|
233 UNLOCK (lock); |
|
234 |
|
235 return 0; |
|
236 } |
|
237 |
|
238 // Initialize the location pointed to by 'id' to a new histogram profile entry |
|
239 // associated with 'file' and 'line', or do nothing if already initialized. |
|
240 |
|
241 int initHistProfile(void** id, char* file, int line, int nbins, ...) |
|
242 { |
|
243 DO_LOCK (&glock); |
|
244 entry_t e = (entry_t) *id; |
|
245 if (notInitialized) { |
|
246 atexit (dumpProfile); |
|
247 notInitialized = false; |
|
248 } |
|
249 |
|
250 if (e == nullptr) { |
|
251 e = findEntry (file, line); |
|
252 if (e) { |
|
253 *id = e; |
|
254 } |
|
255 } |
|
256 |
|
257 if (e == nullptr) { |
|
258 va_list va; |
|
259 hist_t h; |
|
260 int b, n, s; |
|
261 int64_t* lb; |
|
262 |
|
263 e = (entry_t) malloc (sizeof(entry)); |
|
264 e->lock = LOCK_IS_FREE; |
|
265 e->file = file; |
|
266 e->line = line; |
|
267 e->value = 0; |
|
268 e->sum = 0; |
|
269 e->count = 0; |
|
270 e->min = 0; |
|
271 e->max = 0; |
|
272 e->func = nullptr; |
|
273 e->h = h = (hist_t) malloc (sizeof(hist)); |
|
274 n = 1+MAX(nbins,0); |
|
275 h->nbins = n-1; |
|
276 s = n*sizeof(int64_t); |
|
277 lb = (int64_t*) malloc (s); |
|
278 h->lb = lb; |
|
279 VMPI_memset (h->lb, 0, s); |
|
280 h->count = (int64_t*) malloc (s); |
|
281 VMPI_memset (h->count, 0, s); |
|
282 |
|
283 va_start (va, nbins); |
|
284 for (b = 0; b < nbins; b++) { |
|
285 //lb[b] = va_arg (va, int64_t); |
|
286 lb[b] = va_arg (va, int); |
|
287 } |
|
288 lb[b] = MAXINT64; |
|
289 va_end (va); |
|
290 |
|
291 e->genptr = nullptr; |
|
292 VMPI_memset (&e->ivar, 0, sizeof(e->ivar)); |
|
293 VMPI_memset (&e->i64var, 0, sizeof(e->i64var)); |
|
294 VMPI_memset (&e->dvar, 0, sizeof(e->dvar)); |
|
295 e->next = entries; |
|
296 entries = e; |
|
297 *id = e; |
|
298 } |
|
299 DO_UNLOCK (&glock); |
|
300 |
|
301 return 0; |
|
302 } |
|
303 |
|
304 // Record a histogram profile event. |
|
305 |
|
306 int histValue(void* id, int64_t value) |
|
307 { |
|
308 entry_t e = (entry_t) id; |
|
309 long* lock = &(e->lock); |
|
310 hist_t h = e->h; |
|
311 int nbins = h->nbins; |
|
312 int64_t* lb = h->lb; |
|
313 int b; |
|
314 |
|
315 LOCK (lock); |
|
316 e->value = value; |
|
317 if (e->count == 0) { |
|
318 e->sum = value; |
|
319 e->count = 1; |
|
320 e->min = value; |
|
321 e->max = value; |
|
322 } else { |
|
323 e->sum += value; |
|
324 e->count ++; |
|
325 e->min = MIN (e->min, value); |
|
326 e->max = MAX (e->max, value); |
|
327 } |
|
328 for (b = 0; b < nbins; b ++) { |
|
329 if (value < lb[b]) break; |
|
330 } |
|
331 h->count[b] ++; |
|
332 UNLOCK (lock); |
|
333 |
|
334 return 0; |
|
335 } |
|
336 |
|
337 #if defined(_MSC_VER) && defined(_M_IX86) |
|
338 uint64_t readTimestampCounter() |
|
339 { |
|
340 // read the cpu cycle counter. 1 tick = 1 cycle on IA32 |
|
341 _asm rdtsc; |
|
342 } |
|
343 #elif defined(__GNUC__) && (__i386__ || __x86_64__) |
|
344 uint64_t readTimestampCounter() |
|
345 { |
|
346 uint32_t lo, hi; |
|
347 __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); |
|
348 return (uint64_t(hi) << 32) | lo; |
|
349 } |
|
350 #else |
|
351 // add stub for platforms without it, so fat builds don't fail |
|
352 uint64_t readTimestampCounter() { return 0; } |
|
353 #endif |
|
354 |