Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
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
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>
34 #include "libmalloc.h"
35 #include "jprof.h"
36 #include <string.h>
37 #include <errno.h>
38 #include <dlfcn.h>
40 // Must define before including jprof.h
41 void *moz_xmalloc(size_t size)
42 {
43 return malloc(size);
44 }
46 void moz_xfree(void *mem)
47 {
48 free(mem);
49 }
51 #ifdef NTO
52 #include <sys/link.h>
53 extern r_debug _r_debug;
54 #else
55 #include <link.h>
56 #endif
58 #define USE_GLIBC_BACKTRACE 1
59 // To debug, use #define JPROF_STATIC
60 #define JPROF_STATIC //static
62 static int gLogFD = -1;
63 static pthread_t main_thread;
65 static void startSignalCounter(unsigned long millisec);
66 static int enableRTCSignals(bool enable);
69 //----------------------------------------------------------------------
70 // replace use of atexit()
72 static void DumpAddressMap();
74 struct JprofShutdown {
75 JprofShutdown() {}
76 ~JprofShutdown() {
77 DumpAddressMap();
78 }
79 };
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 }
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;
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]));
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;
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;
129 me->pcs[numpcs++] = (char*) top_instr_ptr;
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
151 //----------------------------------------------------------------------
153 static int rtcHz;
154 static int rtcFD = -1;
155 static bool circular = false;
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 }
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
192 static bool was_paused = true;
194 JPROF_STATIC void JprofBufferDump();
195 JPROF_STATIC void JprofBufferClear();
197 static void ClearProfilingHook(int signum)
198 {
199 if (circular) {
200 JprofBufferClear();
201 puts("Jprof: cleared circular buffer.");
202 }
203 }
205 static void EndProfilingHook(int signum)
206 {
207 if (circular)
208 JprofBufferDump();
210 DumpAddressMap();
211 was_paused = true;
212 puts("Jprof: profiling paused.");
213 }
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
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;
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 }
252 void clear() {
253 DUMB_LOCK();
254 head = tail;
255 used = 0;
256 DUMB_UNLOCK();
257 }
259 bool empty() {
260 return head == tail;
261 }
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 }
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 }
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;
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 }
307 used++;
308 DUMB_UNLOCK();
310 return true;
311 }
313 // for external access to the buffer (saving)
314 void lock() {
315 DUMB_LOCK();
316 }
318 void unlock() {
319 DUMB_UNLOCK();
320 }
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;
329 private:
330 pthread_mutex_t mutex;
331 };
333 class DumbCircularBuffer *JprofBuffer;
335 JPROF_STATIC void
336 JprofBufferInit(size_t size)
337 {
338 JprofBuffer = new DumbCircularBuffer(size);
339 }
341 JPROF_STATIC void
342 JprofBufferClear()
343 {
344 fprintf(stderr,"Told to clear JPROF circular buffer\n");
345 JprofBuffer->clear();
346 }
348 JPROF_STATIC size_t
349 JprofEntrySizeof(malloc_log_entry *me)
350 {
351 return offsetof(malloc_log_entry, pcs) + me->numpcs*sizeof(char*);
352 }
354 JPROF_STATIC void
355 JprofBufferAppend(malloc_log_entry *me)
356 {
357 size_t size = JprofEntrySizeof(me);
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;
371 } while (!JprofBuffer->insert(me,size));
372 }
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 }
395 //----------------------------------------------------------------------
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;
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 }
412 CrawlStack(&me, stack_top, top_instr_ptr);
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 }
425 static int realTime;
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;
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;
441 if (realTime) {
442 setitimer(ITIMER_REAL, &tvalue, nullptr);
443 } else {
444 setitimer(ITIMER_PROF, &tvalue, nullptr);
445 }
446 }
448 static long timerMilliSec = 50;
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 }
459 if (sigaction(SIGIO, sap, nullptr) == -1) {
460 perror("JPROF_RTC setup: sigaction(SIGIO)");
461 return 0;
462 }
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 }
469 if (ioctl(rtcFD, RTC_PIE_ON, 0) == -1) {
470 perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_PIE_ON)");
471 return 0;
472 }
474 if (fcntl(rtcFD, F_SETSIG, 0) == -1) {
475 perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETSIG, 0)");
476 return 0;
477 }
479 if (fcntl(rtcFD, F_SETOWN, getpid()) == -1) {
480 perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETOWN, getpid())");
481 return 0;
482 }
484 return 1;
485 }
487 static int enableRTCSignals(bool enable)
488 {
489 static bool enabled = false;
490 if (enabled == enable) {
491 return 0;
492 }
493 enabled = enable;
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 }
501 if (enable) {
502 flags |= FASYNC;
503 } else {
504 flags &= ~FASYNC;
505 }
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 }
516 return 1;
517 }
518 #endif
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;
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
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 }
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
569 if (!rtcHz)
570 startSignalCounter(timerMilliSec);
571 }
573 NS_EXPORT_(void) setupProfilingStuff(void)
574 {
575 static int gFirstTime = 1;
576 char filename[2048]; // XXX fix
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");
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 */
606 circular = false;
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;
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 }
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 }
645 char *first = strstr(tst, "JP_FIRST=");
646 if(first) {
647 firstDelay = atol(first+strlen("JP_FIRST="));
648 }
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. ;) */
657 #define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0)
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);
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;
677 char *is_slave = getenv("JPROF_SLAVE");
678 if (!is_slave)
679 setenv("JPROF_SLAVE","", 0);
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);
687 // XXX FIX! inherit current capture state!
688 }
690 if(!doNotStart) {
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;
701 // Dump out the address map when we terminate
702 RegisterJprofShutdown();
704 main_thread = pthread_self();
705 //fprintf(stderr,"jprof: main_thread = %u\n",
706 // (unsigned int)main_thread);
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 }
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);
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).
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);
745 // make it so a SIGUSR2 will clear the circular buffer
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);
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);
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 }