|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 // vim:cindent:sw=4:et:ts=8: |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 // The linux glibc hides part of sigaction if _POSIX_SOURCE is defined |
|
8 #if defined(linux) |
|
9 #undef _POSIX_SOURCE |
|
10 #undef _SVID_SOURCE |
|
11 #ifndef _GNU_SOURCE |
|
12 #define _GNU_SOURCE |
|
13 #endif |
|
14 #endif |
|
15 |
|
16 #include <errno.h> |
|
17 #if defined(linux) |
|
18 #include <linux/rtc.h> |
|
19 #include <pthread.h> |
|
20 #endif |
|
21 #include <unistd.h> |
|
22 #include <fcntl.h> |
|
23 #include <stdio.h> |
|
24 #include <stdlib.h> |
|
25 #include <signal.h> |
|
26 #include <sys/time.h> |
|
27 #include <sys/types.h> |
|
28 #include <sys/ioctl.h> |
|
29 #include <sys/stat.h> |
|
30 #include <sys/syscall.h> |
|
31 #include <ucontext.h> |
|
32 #include <execinfo.h> |
|
33 |
|
34 #include "libmalloc.h" |
|
35 #include "jprof.h" |
|
36 #include <string.h> |
|
37 #include <errno.h> |
|
38 #include <dlfcn.h> |
|
39 |
|
40 // Must define before including jprof.h |
|
41 void *moz_xmalloc(size_t size) |
|
42 { |
|
43 return malloc(size); |
|
44 } |
|
45 |
|
46 void moz_xfree(void *mem) |
|
47 { |
|
48 free(mem); |
|
49 } |
|
50 |
|
51 #ifdef NTO |
|
52 #include <sys/link.h> |
|
53 extern r_debug _r_debug; |
|
54 #else |
|
55 #include <link.h> |
|
56 #endif |
|
57 |
|
58 #define USE_GLIBC_BACKTRACE 1 |
|
59 // To debug, use #define JPROF_STATIC |
|
60 #define JPROF_STATIC //static |
|
61 |
|
62 static int gLogFD = -1; |
|
63 static pthread_t main_thread; |
|
64 |
|
65 static void startSignalCounter(unsigned long millisec); |
|
66 static int enableRTCSignals(bool enable); |
|
67 |
|
68 |
|
69 //---------------------------------------------------------------------- |
|
70 // replace use of atexit() |
|
71 |
|
72 static void DumpAddressMap(); |
|
73 |
|
74 struct JprofShutdown { |
|
75 JprofShutdown() {} |
|
76 ~JprofShutdown() { |
|
77 DumpAddressMap(); |
|
78 } |
|
79 }; |
|
80 |
|
81 static void RegisterJprofShutdown() { |
|
82 // This instanciates the dummy class above, and will trigger the class |
|
83 // destructor when libxul is unloaded. This is equivalent to atexit(), |
|
84 // but gracefully handles dlclose(). |
|
85 static JprofShutdown t; |
|
86 } |
|
87 |
|
88 #if defined(i386) || defined(_i386) || defined(__x86_64__) |
|
89 JPROF_STATIC void CrawlStack(malloc_log_entry* me, |
|
90 void* stack_top, void* top_instr_ptr) |
|
91 { |
|
92 #if USE_GLIBC_BACKTRACE |
|
93 // This probably works on more than x86! But we need a way to get the |
|
94 // top instruction pointer, which is kindof arch-specific |
|
95 void *array[500]; |
|
96 int cnt, i; |
|
97 u_long numpcs = 0; |
|
98 bool tracing = false; |
|
99 |
|
100 // This is from glibc. A more generic version might use |
|
101 // libunwind and/or CaptureStackBackTrace() on Windows |
|
102 cnt = backtrace(&array[0],sizeof(array)/sizeof(array[0])); |
|
103 |
|
104 // StackHook->JprofLog->CrawlStack |
|
105 // Then we have sigaction, which replaced top_instr_ptr |
|
106 array[3] = top_instr_ptr; |
|
107 for (i = 3; i < cnt; i++) |
|
108 { |
|
109 me->pcs[numpcs++] = (char *) array[i]; |
|
110 } |
|
111 me->numpcs = numpcs; |
|
112 |
|
113 #else |
|
114 // original code - this breaks on many platforms |
|
115 void **bp; |
|
116 #if defined(__i386) |
|
117 __asm__( "movl %%ebp, %0" : "=g"(bp)); |
|
118 #elif defined(__x86_64__) |
|
119 __asm__( "movq %%rbp, %0" : "=g"(bp)); |
|
120 #else |
|
121 // It would be nice if this worked uniformly, but at least on i386 and |
|
122 // x86_64, it stopped working with gcc 4.1, because it points to the |
|
123 // end of the saved registers instead of the start. |
|
124 bp = __builtin_frame_address(0); |
|
125 #endif |
|
126 u_long numpcs = 0; |
|
127 bool tracing = false; |
|
128 |
|
129 me->pcs[numpcs++] = (char*) top_instr_ptr; |
|
130 |
|
131 while (numpcs < MAX_STACK_CRAWL) { |
|
132 void** nextbp = (void**) *bp++; |
|
133 void* pc = *bp; |
|
134 if (nextbp < bp) { |
|
135 break; |
|
136 } |
|
137 if (tracing) { |
|
138 // Skip the signal handling. |
|
139 me->pcs[numpcs++] = (char*) pc; |
|
140 } |
|
141 else if (pc == top_instr_ptr) { |
|
142 tracing = true; |
|
143 } |
|
144 bp = nextbp; |
|
145 } |
|
146 me->numpcs = numpcs; |
|
147 #endif |
|
148 } |
|
149 #endif |
|
150 |
|
151 //---------------------------------------------------------------------- |
|
152 |
|
153 static int rtcHz; |
|
154 static int rtcFD = -1; |
|
155 static bool circular = false; |
|
156 |
|
157 #if defined(linux) || defined(NTO) |
|
158 static void DumpAddressMap() |
|
159 { |
|
160 // Turn off the timer so we don't get interrupts during shutdown |
|
161 #if defined(linux) |
|
162 if (rtcHz) { |
|
163 enableRTCSignals(false); |
|
164 } else |
|
165 #endif |
|
166 { |
|
167 startSignalCounter(0); |
|
168 } |
|
169 |
|
170 int mfd = open(M_MAPFILE, O_CREAT|O_WRONLY|O_TRUNC, 0666); |
|
171 if (mfd >= 0) { |
|
172 malloc_map_entry mme; |
|
173 link_map* map = _r_debug.r_map; |
|
174 while (nullptr != map) { |
|
175 if (map->l_name && *map->l_name) { |
|
176 mme.nameLen = strlen(map->l_name); |
|
177 mme.address = map->l_addr; |
|
178 write(mfd, &mme, sizeof(mme)); |
|
179 write(mfd, map->l_name, mme.nameLen); |
|
180 #if 0 |
|
181 write(1, map->l_name, mme.nameLen); |
|
182 write(1, "\n", 1); |
|
183 #endif |
|
184 } |
|
185 map = map->l_next; |
|
186 } |
|
187 close(mfd); |
|
188 } |
|
189 } |
|
190 #endif |
|
191 |
|
192 static bool was_paused = true; |
|
193 |
|
194 JPROF_STATIC void JprofBufferDump(); |
|
195 JPROF_STATIC void JprofBufferClear(); |
|
196 |
|
197 static void ClearProfilingHook(int signum) |
|
198 { |
|
199 if (circular) { |
|
200 JprofBufferClear(); |
|
201 puts("Jprof: cleared circular buffer."); |
|
202 } |
|
203 } |
|
204 |
|
205 static void EndProfilingHook(int signum) |
|
206 { |
|
207 if (circular) |
|
208 JprofBufferDump(); |
|
209 |
|
210 DumpAddressMap(); |
|
211 was_paused = true; |
|
212 puts("Jprof: profiling paused."); |
|
213 } |
|
214 |
|
215 |
|
216 |
|
217 //---------------------------------------------------------------------- |
|
218 // proper usage would be a template, including the function to find the |
|
219 // size of an entry, or include a size header explicitly to each entry. |
|
220 #if defined(linux) |
|
221 #define DUMB_LOCK() pthread_mutex_lock(&mutex); |
|
222 #define DUMB_UNLOCK() pthread_mutex_unlock(&mutex); |
|
223 #else |
|
224 #define DUMB_LOCK() FIXME() |
|
225 #define DUMB_UNLOCK() FIXME() |
|
226 #endif |
|
227 |
|
228 |
|
229 class DumbCircularBuffer |
|
230 { |
|
231 public: |
|
232 DumbCircularBuffer(size_t init_buffer_size) { |
|
233 used = 0; |
|
234 buffer_size = init_buffer_size; |
|
235 buffer = (unsigned char *) malloc(buffer_size); |
|
236 head = tail = buffer; |
|
237 |
|
238 #if defined(linux) |
|
239 pthread_mutexattr_t mAttr; |
|
240 pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE_NP); |
|
241 pthread_mutex_init(&mutex, &mAttr); |
|
242 pthread_mutexattr_destroy(&mAttr); |
|
243 #endif |
|
244 } |
|
245 ~DumbCircularBuffer() { |
|
246 free(buffer); |
|
247 #if defined(linux) |
|
248 pthread_mutex_destroy (&mutex); |
|
249 #endif |
|
250 } |
|
251 |
|
252 void clear() { |
|
253 DUMB_LOCK(); |
|
254 head = tail; |
|
255 used = 0; |
|
256 DUMB_UNLOCK(); |
|
257 } |
|
258 |
|
259 bool empty() { |
|
260 return head == tail; |
|
261 } |
|
262 |
|
263 size_t space_available() { |
|
264 size_t result; |
|
265 DUMB_LOCK(); |
|
266 if (tail > head) |
|
267 result = buffer_size - (tail-head) - 1; |
|
268 else |
|
269 result = head-tail - 1; |
|
270 DUMB_UNLOCK(); |
|
271 return result; |
|
272 } |
|
273 |
|
274 void drop(size_t size) { |
|
275 // assumes correctness! |
|
276 DUMB_LOCK(); |
|
277 head += size; |
|
278 if (head >= &buffer[buffer_size]) |
|
279 head -= buffer_size; |
|
280 used--; |
|
281 DUMB_UNLOCK(); |
|
282 } |
|
283 |
|
284 bool insert(void *data, size_t size) { |
|
285 // can fail if not enough space in the entire buffer |
|
286 DUMB_LOCK(); |
|
287 if (space_available() < size) |
|
288 return false; |
|
289 |
|
290 size_t max_without_wrap = &buffer[buffer_size] - tail; |
|
291 size_t initial = size > max_without_wrap ? max_without_wrap : size; |
|
292 #if DEBUG_CIRCULAR |
|
293 fprintf(stderr,"insert(%d): max_without_wrap %d, size %d, initial %d\n",used,max_without_wrap,size,initial); |
|
294 #endif |
|
295 memcpy(tail,data,initial); |
|
296 tail += initial; |
|
297 data = ((char *)data)+initial; |
|
298 size -= initial; |
|
299 if (size != 0) { |
|
300 #if DEBUG_CIRCULAR |
|
301 fprintf(stderr,"wrapping by %d bytes\n",size); |
|
302 #endif |
|
303 memcpy(buffer,data,size); |
|
304 tail = &(((unsigned char *)buffer)[size]); |
|
305 } |
|
306 |
|
307 used++; |
|
308 DUMB_UNLOCK(); |
|
309 |
|
310 return true; |
|
311 } |
|
312 |
|
313 // for external access to the buffer (saving) |
|
314 void lock() { |
|
315 DUMB_LOCK(); |
|
316 } |
|
317 |
|
318 void unlock() { |
|
319 DUMB_UNLOCK(); |
|
320 } |
|
321 |
|
322 // XXX These really shouldn't be public... |
|
323 unsigned char *head; |
|
324 unsigned char *tail; |
|
325 unsigned int used; |
|
326 unsigned char *buffer; |
|
327 size_t buffer_size; |
|
328 |
|
329 private: |
|
330 pthread_mutex_t mutex; |
|
331 }; |
|
332 |
|
333 class DumbCircularBuffer *JprofBuffer; |
|
334 |
|
335 JPROF_STATIC void |
|
336 JprofBufferInit(size_t size) |
|
337 { |
|
338 JprofBuffer = new DumbCircularBuffer(size); |
|
339 } |
|
340 |
|
341 JPROF_STATIC void |
|
342 JprofBufferClear() |
|
343 { |
|
344 fprintf(stderr,"Told to clear JPROF circular buffer\n"); |
|
345 JprofBuffer->clear(); |
|
346 } |
|
347 |
|
348 JPROF_STATIC size_t |
|
349 JprofEntrySizeof(malloc_log_entry *me) |
|
350 { |
|
351 return offsetof(malloc_log_entry, pcs) + me->numpcs*sizeof(char*); |
|
352 } |
|
353 |
|
354 JPROF_STATIC void |
|
355 JprofBufferAppend(malloc_log_entry *me) |
|
356 { |
|
357 size_t size = JprofEntrySizeof(me); |
|
358 |
|
359 do { |
|
360 while (JprofBuffer->space_available() < size && |
|
361 JprofBuffer->used > 0) { |
|
362 #if DEBUG_CIRCULAR |
|
363 fprintf(stderr,"dropping entry: %d in use, %d free, need %d, size_to_free = %d\n", |
|
364 JprofBuffer->used,JprofBuffer->space_available(),size,JprofEntrySizeof((malloc_log_entry *) JprofBuffer->head)); |
|
365 #endif |
|
366 JprofBuffer->drop(JprofEntrySizeof((malloc_log_entry *) JprofBuffer->head)); |
|
367 } |
|
368 if (JprofBuffer->space_available() < size) |
|
369 return; |
|
370 |
|
371 } while (!JprofBuffer->insert(me,size)); |
|
372 } |
|
373 |
|
374 JPROF_STATIC void |
|
375 JprofBufferDump() |
|
376 { |
|
377 JprofBuffer->lock(); |
|
378 #if DEBUG_CIRCULAR |
|
379 fprintf(stderr,"dumping JP_CIRCULAR buffer, %d of %d bytes\n", |
|
380 JprofBuffer->tail > JprofBuffer->head ? |
|
381 JprofBuffer->tail - JprofBuffer->head : |
|
382 JprofBuffer->buffer_size + JprofBuffer->tail - JprofBuffer->head, |
|
383 JprofBuffer->buffer_size); |
|
384 #endif |
|
385 if (JprofBuffer->tail >= JprofBuffer->head) { |
|
386 write(gLogFD, JprofBuffer->head, JprofBuffer->tail - JprofBuffer->head); |
|
387 } else { |
|
388 write(gLogFD, JprofBuffer->head, &(JprofBuffer->buffer[JprofBuffer->buffer_size]) - JprofBuffer->head); |
|
389 write(gLogFD, JprofBuffer->buffer, JprofBuffer->tail - JprofBuffer->buffer); |
|
390 } |
|
391 JprofBuffer->clear(); |
|
392 JprofBuffer->unlock(); |
|
393 } |
|
394 |
|
395 //---------------------------------------------------------------------- |
|
396 |
|
397 JPROF_STATIC void |
|
398 JprofLog(u_long aTime, void* stack_top, void* top_instr_ptr) |
|
399 { |
|
400 // Static is simply to make debugging tolerable |
|
401 static malloc_log_entry me; |
|
402 |
|
403 me.delTime = aTime; |
|
404 me.thread = syscall(SYS_gettid); //gettid(); |
|
405 if (was_paused) { |
|
406 me.flags = JP_FIRST_AFTER_PAUSE; |
|
407 was_paused = 0; |
|
408 } else { |
|
409 me.flags = 0; |
|
410 } |
|
411 |
|
412 CrawlStack(&me, stack_top, top_instr_ptr); |
|
413 |
|
414 #ifndef NTO |
|
415 if (circular) { |
|
416 JprofBufferAppend(&me); |
|
417 } else { |
|
418 write(gLogFD, &me, JprofEntrySizeof(&me)); |
|
419 } |
|
420 #else |
|
421 printf("Neutrino is missing the pcs member of malloc_log_entry!! \n"); |
|
422 #endif |
|
423 } |
|
424 |
|
425 static int realTime; |
|
426 |
|
427 /* Lets interrupt at 10 Hz. This is so my log files don't get too large. |
|
428 * This can be changed to a faster value latter. This timer is not |
|
429 * programmed to reset, even though it is capable of doing so. This is |
|
430 * to keep from getting interrupts from inside of the handler. |
|
431 */ |
|
432 static void startSignalCounter(unsigned long millisec) |
|
433 { |
|
434 struct itimerval tvalue; |
|
435 |
|
436 tvalue.it_interval.tv_sec = 0; |
|
437 tvalue.it_interval.tv_usec = 0; |
|
438 tvalue.it_value.tv_sec = millisec/1000; |
|
439 tvalue.it_value.tv_usec = (millisec%1000)*1000; |
|
440 |
|
441 if (realTime) { |
|
442 setitimer(ITIMER_REAL, &tvalue, nullptr); |
|
443 } else { |
|
444 setitimer(ITIMER_PROF, &tvalue, nullptr); |
|
445 } |
|
446 } |
|
447 |
|
448 static long timerMilliSec = 50; |
|
449 |
|
450 #if defined(linux) |
|
451 static int setupRTCSignals(int hz, struct sigaction *sap) |
|
452 { |
|
453 /* global */ rtcFD = open("/dev/rtc", O_RDONLY); |
|
454 if (rtcFD < 0) { |
|
455 perror("JPROF_RTC setup: open(\"/dev/rtc\", O_RDONLY)"); |
|
456 return 0; |
|
457 } |
|
458 |
|
459 if (sigaction(SIGIO, sap, nullptr) == -1) { |
|
460 perror("JPROF_RTC setup: sigaction(SIGIO)"); |
|
461 return 0; |
|
462 } |
|
463 |
|
464 if (ioctl(rtcFD, RTC_IRQP_SET, hz) == -1) { |
|
465 perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_IRQP_SET, $JPROF_RTC_HZ)"); |
|
466 return 0; |
|
467 } |
|
468 |
|
469 if (ioctl(rtcFD, RTC_PIE_ON, 0) == -1) { |
|
470 perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_PIE_ON)"); |
|
471 return 0; |
|
472 } |
|
473 |
|
474 if (fcntl(rtcFD, F_SETSIG, 0) == -1) { |
|
475 perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETSIG, 0)"); |
|
476 return 0; |
|
477 } |
|
478 |
|
479 if (fcntl(rtcFD, F_SETOWN, getpid()) == -1) { |
|
480 perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETOWN, getpid())"); |
|
481 return 0; |
|
482 } |
|
483 |
|
484 return 1; |
|
485 } |
|
486 |
|
487 static int enableRTCSignals(bool enable) |
|
488 { |
|
489 static bool enabled = false; |
|
490 if (enabled == enable) { |
|
491 return 0; |
|
492 } |
|
493 enabled = enable; |
|
494 |
|
495 int flags = fcntl(rtcFD, F_GETFL); |
|
496 if (flags < 0) { |
|
497 perror("JPROF_RTC setup: fcntl(/dev/rtc, F_GETFL)"); |
|
498 return 0; |
|
499 } |
|
500 |
|
501 if (enable) { |
|
502 flags |= FASYNC; |
|
503 } else { |
|
504 flags &= ~FASYNC; |
|
505 } |
|
506 |
|
507 if (fcntl(rtcFD, F_SETFL, flags) == -1) { |
|
508 if (enable) { |
|
509 perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETFL, flags | FASYNC)"); |
|
510 } else { |
|
511 perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETFL, flags & ~FASYNC)"); |
|
512 } |
|
513 return 0; |
|
514 } |
|
515 |
|
516 return 1; |
|
517 } |
|
518 #endif |
|
519 |
|
520 JPROF_STATIC void StackHook( |
|
521 int signum, |
|
522 siginfo_t *info, |
|
523 void *ucontext) |
|
524 { |
|
525 static struct timeval tFirst; |
|
526 static int first=1; |
|
527 size_t millisec = 0; |
|
528 |
|
529 #if defined(linux) |
|
530 if (rtcHz && pthread_self() != main_thread) { |
|
531 // Only collect stack data on the main thread, for now. |
|
532 return; |
|
533 } |
|
534 #endif |
|
535 |
|
536 if(first && !(first=0)) { |
|
537 puts("Jprof: received first signal"); |
|
538 #if defined(linux) |
|
539 if (rtcHz) { |
|
540 enableRTCSignals(true); |
|
541 } else |
|
542 #endif |
|
543 { |
|
544 gettimeofday(&tFirst, 0); |
|
545 millisec = 0; |
|
546 } |
|
547 } else { |
|
548 #if defined(linux) |
|
549 if (rtcHz) { |
|
550 enableRTCSignals(true); |
|
551 } else |
|
552 #endif |
|
553 { |
|
554 struct timeval tNow; |
|
555 gettimeofday(&tNow, 0); |
|
556 double usec = 1e6*(tNow.tv_sec - tFirst.tv_sec); |
|
557 usec += (tNow.tv_usec - tFirst.tv_usec); |
|
558 millisec = static_cast<size_t>(usec*1e-3); |
|
559 } |
|
560 } |
|
561 |
|
562 gregset_t &gregs = ((ucontext_t*)ucontext)->uc_mcontext.gregs; |
|
563 #ifdef __x86_64__ |
|
564 JprofLog(millisec, (void*)gregs[REG_RSP], (void*)gregs[REG_RIP]); |
|
565 #else |
|
566 JprofLog(millisec, (void*)gregs[REG_ESP], (void*)gregs[REG_EIP]); |
|
567 #endif |
|
568 |
|
569 if (!rtcHz) |
|
570 startSignalCounter(timerMilliSec); |
|
571 } |
|
572 |
|
573 NS_EXPORT_(void) setupProfilingStuff(void) |
|
574 { |
|
575 static int gFirstTime = 1; |
|
576 char filename[2048]; // XXX fix |
|
577 |
|
578 if(gFirstTime && !(gFirstTime=0)) { |
|
579 int startTimer = 1; |
|
580 int doNotStart = 1; |
|
581 int firstDelay = 0; |
|
582 int append = O_TRUNC; |
|
583 char *tst = getenv("JPROF_FLAGS"); |
|
584 |
|
585 /* Options from JPROF_FLAGS environment variable: |
|
586 * JP_DEFER -> Wait for a SIGPROF (or SIGALRM, if JP_REALTIME |
|
587 * is set) from userland before starting |
|
588 * to generate them internally |
|
589 * JP_START -> Install the signal handler |
|
590 * JP_PERIOD -> Time between profiler ticks |
|
591 * JP_FIRST -> Extra delay before starting |
|
592 * JP_REALTIME -> Take stack traces in intervals of real time |
|
593 * rather than time used by the process (and the |
|
594 * system for the process). This is useful for |
|
595 * finding time spent by the X server. |
|
596 * JP_APPEND -> Append to jprof-log rather than overwriting it. |
|
597 * This is somewhat risky since it depends on the |
|
598 * address map staying constant across multiple runs. |
|
599 * JP_FILENAME -> base filename to use when saving logs. Note that |
|
600 * this does not affect the mapfile. |
|
601 * JP_CIRCULAR -> use a circular buffer of size N, write/clear on SIGUSR1 |
|
602 * |
|
603 * JPROF_SLAVE is set if this is not the first process. |
|
604 */ |
|
605 |
|
606 circular = false; |
|
607 |
|
608 if(tst) { |
|
609 if(strstr(tst, "JP_DEFER")) |
|
610 { |
|
611 doNotStart = 0; |
|
612 startTimer = 0; |
|
613 } |
|
614 if(strstr(tst, "JP_START")) doNotStart = 0; |
|
615 if(strstr(tst, "JP_REALTIME")) realTime = 1; |
|
616 if(strstr(tst, "JP_APPEND")) append = O_APPEND; |
|
617 |
|
618 char *delay = strstr(tst,"JP_PERIOD="); |
|
619 if(delay) { |
|
620 double tmp = strtod(delay+strlen("JP_PERIOD="), nullptr); |
|
621 if (tmp>=1e-3) { |
|
622 timerMilliSec = static_cast<unsigned long>(1000 * tmp); |
|
623 } else { |
|
624 fprintf(stderr, |
|
625 "JP_PERIOD of %g less than 0.001 (1ms), using 1ms\n", |
|
626 tmp); |
|
627 timerMilliSec = 1; |
|
628 } |
|
629 } |
|
630 |
|
631 char *circular_op = strstr(tst,"JP_CIRCULAR="); |
|
632 if(circular_op) { |
|
633 size_t size = atol(circular_op+strlen("JP_CIRCULAR=")); |
|
634 if (size < 1000) { |
|
635 fprintf(stderr, |
|
636 "JP_CIRCULAR of %d less than 1000, using 10000\n", |
|
637 size); |
|
638 size = 10000; |
|
639 } |
|
640 JprofBufferInit(size); |
|
641 fprintf(stderr,"JP_CIRCULAR buffer of %d bytes\n",size); |
|
642 circular = true; |
|
643 } |
|
644 |
|
645 char *first = strstr(tst, "JP_FIRST="); |
|
646 if(first) { |
|
647 firstDelay = atol(first+strlen("JP_FIRST=")); |
|
648 } |
|
649 |
|
650 char *rtc = strstr(tst, "JP_RTC_HZ="); |
|
651 if (rtc) { |
|
652 #if defined(linux) |
|
653 rtcHz = atol(rtc+strlen("JP_RTC_HZ=")); |
|
654 timerMilliSec = 0; /* This makes JP_FIRST work right. */ |
|
655 realTime = 1; /* It's the _R_TC and all. ;) */ |
|
656 |
|
657 #define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0) |
|
658 |
|
659 if (!IS_POWER_OF_TWO(rtcHz) || rtcHz < 2) { |
|
660 fprintf(stderr, "JP_RTC_HZ must be power of two and >= 2, " |
|
661 "but %d was provided; using default of 2048\n", |
|
662 rtcHz); |
|
663 rtcHz = 2048; |
|
664 } |
|
665 #else |
|
666 fputs("JP_RTC_HZ found, but RTC profiling only supported on " |
|
667 "Linux!\n", stderr); |
|
668 |
|
669 #endif |
|
670 } |
|
671 char *f = strstr(tst,"JP_FILENAME="); |
|
672 if (f) |
|
673 f = f + strlen("JP_FILENAME="); |
|
674 else |
|
675 f = M_LOGFILE; |
|
676 |
|
677 char *is_slave = getenv("JPROF_SLAVE"); |
|
678 if (!is_slave) |
|
679 setenv("JPROF_SLAVE","", 0); |
|
680 |
|
681 int pid = syscall(SYS_gettid); //gettid(); |
|
682 if (is_slave) |
|
683 snprintf(filename,sizeof(filename),"%s-%d",f,pid); |
|
684 else |
|
685 snprintf(filename,sizeof(filename),"%s",f); |
|
686 |
|
687 // XXX FIX! inherit current capture state! |
|
688 } |
|
689 |
|
690 if(!doNotStart) { |
|
691 |
|
692 if(gLogFD<0) { |
|
693 gLogFD = open(filename, O_CREAT | O_WRONLY | append, 0666); |
|
694 if(gLogFD<0) { |
|
695 fprintf(stderr, "Unable to create " M_LOGFILE); |
|
696 perror(":"); |
|
697 } else { |
|
698 struct sigaction action; |
|
699 sigset_t mset; |
|
700 |
|
701 // Dump out the address map when we terminate |
|
702 RegisterJprofShutdown(); |
|
703 |
|
704 main_thread = pthread_self(); |
|
705 //fprintf(stderr,"jprof: main_thread = %u\n", |
|
706 // (unsigned int)main_thread); |
|
707 |
|
708 // FIX! probably should block these against each other |
|
709 // Very unlikely. |
|
710 sigemptyset(&mset); |
|
711 action.sa_handler = nullptr; |
|
712 action.sa_sigaction = StackHook; |
|
713 action.sa_mask = mset; |
|
714 action.sa_flags = SA_RESTART | SA_SIGINFO; |
|
715 #if defined(linux) |
|
716 if (rtcHz) { |
|
717 if (!setupRTCSignals(rtcHz, &action)) { |
|
718 fputs("jprof: Error initializing RTC, NOT " |
|
719 "profiling\n", stderr); |
|
720 return; |
|
721 } |
|
722 } |
|
723 |
|
724 if (!rtcHz || firstDelay != 0) |
|
725 #endif |
|
726 { |
|
727 if (realTime) { |
|
728 sigaction(SIGALRM, &action, nullptr); |
|
729 } |
|
730 } |
|
731 // enable PROF in all cases to simplify JP_DEFER/pause/restart |
|
732 sigaction(SIGPROF, &action, nullptr); |
|
733 |
|
734 // make it so a SIGUSR1 will stop the profiling |
|
735 // Note: It currently does not close the logfile. |
|
736 // This could be configurable (so that it could |
|
737 // later be reopened). |
|
738 |
|
739 struct sigaction stop_action; |
|
740 stop_action.sa_handler = EndProfilingHook; |
|
741 stop_action.sa_mask = mset; |
|
742 stop_action.sa_flags = SA_RESTART; |
|
743 sigaction(SIGUSR1, &stop_action, nullptr); |
|
744 |
|
745 // make it so a SIGUSR2 will clear the circular buffer |
|
746 |
|
747 stop_action.sa_handler = ClearProfilingHook; |
|
748 stop_action.sa_mask = mset; |
|
749 stop_action.sa_flags = SA_RESTART; |
|
750 sigaction(SIGUSR2, &stop_action, nullptr); |
|
751 |
|
752 printf("Jprof: Initialized signal handler and set " |
|
753 "timer for %lu %s, %d s " |
|
754 "initial delay\n", |
|
755 rtcHz ? rtcHz : timerMilliSec, |
|
756 rtcHz ? "Hz" : "ms", |
|
757 firstDelay); |
|
758 |
|
759 if(startTimer) { |
|
760 #if defined(linux) |
|
761 /* If we have an initial delay we can just use |
|
762 startSignalCounter to set up a timer to fire the |
|
763 first stackHook after that delay. When that happens |
|
764 we'll go and switch to RTC profiling. */ |
|
765 if (rtcHz && firstDelay == 0) { |
|
766 puts("Jprof: enabled RTC signals"); |
|
767 enableRTCSignals(true); |
|
768 } else |
|
769 #endif |
|
770 { |
|
771 puts("Jprof: started timer"); |
|
772 startSignalCounter(firstDelay*1000 + timerMilliSec); |
|
773 } |
|
774 } |
|
775 } |
|
776 } |
|
777 } |
|
778 } else { |
|
779 printf("setupProfilingStuff() called multiple times\n"); |
|
780 } |
|
781 } |