michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: // vim:cindent:sw=4:et:ts=8: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // The linux glibc hides part of sigaction if _POSIX_SOURCE is defined michael@0: #if defined(linux) michael@0: #undef _POSIX_SOURCE michael@0: #undef _SVID_SOURCE michael@0: #ifndef _GNU_SOURCE michael@0: #define _GNU_SOURCE michael@0: #endif michael@0: #endif michael@0: michael@0: #include michael@0: #if defined(linux) michael@0: #include michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "libmalloc.h" michael@0: #include "jprof.h" michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: // Must define before including jprof.h michael@0: void *moz_xmalloc(size_t size) michael@0: { michael@0: return malloc(size); michael@0: } michael@0: michael@0: void moz_xfree(void *mem) michael@0: { michael@0: free(mem); michael@0: } michael@0: michael@0: #ifdef NTO michael@0: #include michael@0: extern r_debug _r_debug; michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #define USE_GLIBC_BACKTRACE 1 michael@0: // To debug, use #define JPROF_STATIC michael@0: #define JPROF_STATIC //static michael@0: michael@0: static int gLogFD = -1; michael@0: static pthread_t main_thread; michael@0: michael@0: static void startSignalCounter(unsigned long millisec); michael@0: static int enableRTCSignals(bool enable); michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // replace use of atexit() michael@0: michael@0: static void DumpAddressMap(); michael@0: michael@0: struct JprofShutdown { michael@0: JprofShutdown() {} michael@0: ~JprofShutdown() { michael@0: DumpAddressMap(); michael@0: } michael@0: }; michael@0: michael@0: static void RegisterJprofShutdown() { michael@0: // This instanciates the dummy class above, and will trigger the class michael@0: // destructor when libxul is unloaded. This is equivalent to atexit(), michael@0: // but gracefully handles dlclose(). michael@0: static JprofShutdown t; michael@0: } michael@0: michael@0: #if defined(i386) || defined(_i386) || defined(__x86_64__) michael@0: JPROF_STATIC void CrawlStack(malloc_log_entry* me, michael@0: void* stack_top, void* top_instr_ptr) michael@0: { michael@0: #if USE_GLIBC_BACKTRACE michael@0: // This probably works on more than x86! But we need a way to get the michael@0: // top instruction pointer, which is kindof arch-specific michael@0: void *array[500]; michael@0: int cnt, i; michael@0: u_long numpcs = 0; michael@0: bool tracing = false; michael@0: michael@0: // This is from glibc. A more generic version might use michael@0: // libunwind and/or CaptureStackBackTrace() on Windows michael@0: cnt = backtrace(&array[0],sizeof(array)/sizeof(array[0])); michael@0: michael@0: // StackHook->JprofLog->CrawlStack michael@0: // Then we have sigaction, which replaced top_instr_ptr michael@0: array[3] = top_instr_ptr; michael@0: for (i = 3; i < cnt; i++) michael@0: { michael@0: me->pcs[numpcs++] = (char *) array[i]; michael@0: } michael@0: me->numpcs = numpcs; michael@0: michael@0: #else michael@0: // original code - this breaks on many platforms michael@0: void **bp; michael@0: #if defined(__i386) michael@0: __asm__( "movl %%ebp, %0" : "=g"(bp)); michael@0: #elif defined(__x86_64__) michael@0: __asm__( "movq %%rbp, %0" : "=g"(bp)); michael@0: #else michael@0: // It would be nice if this worked uniformly, but at least on i386 and michael@0: // x86_64, it stopped working with gcc 4.1, because it points to the michael@0: // end of the saved registers instead of the start. michael@0: bp = __builtin_frame_address(0); michael@0: #endif michael@0: u_long numpcs = 0; michael@0: bool tracing = false; michael@0: michael@0: me->pcs[numpcs++] = (char*) top_instr_ptr; michael@0: michael@0: while (numpcs < MAX_STACK_CRAWL) { michael@0: void** nextbp = (void**) *bp++; michael@0: void* pc = *bp; michael@0: if (nextbp < bp) { michael@0: break; michael@0: } michael@0: if (tracing) { michael@0: // Skip the signal handling. michael@0: me->pcs[numpcs++] = (char*) pc; michael@0: } michael@0: else if (pc == top_instr_ptr) { michael@0: tracing = true; michael@0: } michael@0: bp = nextbp; michael@0: } michael@0: me->numpcs = numpcs; michael@0: #endif michael@0: } michael@0: #endif michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: static int rtcHz; michael@0: static int rtcFD = -1; michael@0: static bool circular = false; michael@0: michael@0: #if defined(linux) || defined(NTO) michael@0: static void DumpAddressMap() michael@0: { michael@0: // Turn off the timer so we don't get interrupts during shutdown michael@0: #if defined(linux) michael@0: if (rtcHz) { michael@0: enableRTCSignals(false); michael@0: } else michael@0: #endif michael@0: { michael@0: startSignalCounter(0); michael@0: } michael@0: michael@0: int mfd = open(M_MAPFILE, O_CREAT|O_WRONLY|O_TRUNC, 0666); michael@0: if (mfd >= 0) { michael@0: malloc_map_entry mme; michael@0: link_map* map = _r_debug.r_map; michael@0: while (nullptr != map) { michael@0: if (map->l_name && *map->l_name) { michael@0: mme.nameLen = strlen(map->l_name); michael@0: mme.address = map->l_addr; michael@0: write(mfd, &mme, sizeof(mme)); michael@0: write(mfd, map->l_name, mme.nameLen); michael@0: #if 0 michael@0: write(1, map->l_name, mme.nameLen); michael@0: write(1, "\n", 1); michael@0: #endif michael@0: } michael@0: map = map->l_next; michael@0: } michael@0: close(mfd); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: static bool was_paused = true; michael@0: michael@0: JPROF_STATIC void JprofBufferDump(); michael@0: JPROF_STATIC void JprofBufferClear(); michael@0: michael@0: static void ClearProfilingHook(int signum) michael@0: { michael@0: if (circular) { michael@0: JprofBufferClear(); michael@0: puts("Jprof: cleared circular buffer."); michael@0: } michael@0: } michael@0: michael@0: static void EndProfilingHook(int signum) michael@0: { michael@0: if (circular) michael@0: JprofBufferDump(); michael@0: michael@0: DumpAddressMap(); michael@0: was_paused = true; michael@0: puts("Jprof: profiling paused."); michael@0: } michael@0: michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // proper usage would be a template, including the function to find the michael@0: // size of an entry, or include a size header explicitly to each entry. michael@0: #if defined(linux) michael@0: #define DUMB_LOCK() pthread_mutex_lock(&mutex); michael@0: #define DUMB_UNLOCK() pthread_mutex_unlock(&mutex); michael@0: #else michael@0: #define DUMB_LOCK() FIXME() michael@0: #define DUMB_UNLOCK() FIXME() michael@0: #endif michael@0: michael@0: michael@0: class DumbCircularBuffer michael@0: { michael@0: public: michael@0: DumbCircularBuffer(size_t init_buffer_size) { michael@0: used = 0; michael@0: buffer_size = init_buffer_size; michael@0: buffer = (unsigned char *) malloc(buffer_size); michael@0: head = tail = buffer; michael@0: michael@0: #if defined(linux) michael@0: pthread_mutexattr_t mAttr; michael@0: pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE_NP); michael@0: pthread_mutex_init(&mutex, &mAttr); michael@0: pthread_mutexattr_destroy(&mAttr); michael@0: #endif michael@0: } michael@0: ~DumbCircularBuffer() { michael@0: free(buffer); michael@0: #if defined(linux) michael@0: pthread_mutex_destroy (&mutex); michael@0: #endif michael@0: } michael@0: michael@0: void clear() { michael@0: DUMB_LOCK(); michael@0: head = tail; michael@0: used = 0; michael@0: DUMB_UNLOCK(); michael@0: } michael@0: michael@0: bool empty() { michael@0: return head == tail; michael@0: } michael@0: michael@0: size_t space_available() { michael@0: size_t result; michael@0: DUMB_LOCK(); michael@0: if (tail > head) michael@0: result = buffer_size - (tail-head) - 1; michael@0: else michael@0: result = head-tail - 1; michael@0: DUMB_UNLOCK(); michael@0: return result; michael@0: } michael@0: michael@0: void drop(size_t size) { michael@0: // assumes correctness! michael@0: DUMB_LOCK(); michael@0: head += size; michael@0: if (head >= &buffer[buffer_size]) michael@0: head -= buffer_size; michael@0: used--; michael@0: DUMB_UNLOCK(); michael@0: } michael@0: michael@0: bool insert(void *data, size_t size) { michael@0: // can fail if not enough space in the entire buffer michael@0: DUMB_LOCK(); michael@0: if (space_available() < size) michael@0: return false; michael@0: michael@0: size_t max_without_wrap = &buffer[buffer_size] - tail; michael@0: size_t initial = size > max_without_wrap ? max_without_wrap : size; michael@0: #if DEBUG_CIRCULAR michael@0: fprintf(stderr,"insert(%d): max_without_wrap %d, size %d, initial %d\n",used,max_without_wrap,size,initial); michael@0: #endif michael@0: memcpy(tail,data,initial); michael@0: tail += initial; michael@0: data = ((char *)data)+initial; michael@0: size -= initial; michael@0: if (size != 0) { michael@0: #if DEBUG_CIRCULAR michael@0: fprintf(stderr,"wrapping by %d bytes\n",size); michael@0: #endif michael@0: memcpy(buffer,data,size); michael@0: tail = &(((unsigned char *)buffer)[size]); michael@0: } michael@0: michael@0: used++; michael@0: DUMB_UNLOCK(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // for external access to the buffer (saving) michael@0: void lock() { michael@0: DUMB_LOCK(); michael@0: } michael@0: michael@0: void unlock() { michael@0: DUMB_UNLOCK(); michael@0: } michael@0: michael@0: // XXX These really shouldn't be public... michael@0: unsigned char *head; michael@0: unsigned char *tail; michael@0: unsigned int used; michael@0: unsigned char *buffer; michael@0: size_t buffer_size; michael@0: michael@0: private: michael@0: pthread_mutex_t mutex; michael@0: }; michael@0: michael@0: class DumbCircularBuffer *JprofBuffer; michael@0: michael@0: JPROF_STATIC void michael@0: JprofBufferInit(size_t size) michael@0: { michael@0: JprofBuffer = new DumbCircularBuffer(size); michael@0: } michael@0: michael@0: JPROF_STATIC void michael@0: JprofBufferClear() michael@0: { michael@0: fprintf(stderr,"Told to clear JPROF circular buffer\n"); michael@0: JprofBuffer->clear(); michael@0: } michael@0: michael@0: JPROF_STATIC size_t michael@0: JprofEntrySizeof(malloc_log_entry *me) michael@0: { michael@0: return offsetof(malloc_log_entry, pcs) + me->numpcs*sizeof(char*); michael@0: } michael@0: michael@0: JPROF_STATIC void michael@0: JprofBufferAppend(malloc_log_entry *me) michael@0: { michael@0: size_t size = JprofEntrySizeof(me); michael@0: michael@0: do { michael@0: while (JprofBuffer->space_available() < size && michael@0: JprofBuffer->used > 0) { michael@0: #if DEBUG_CIRCULAR michael@0: fprintf(stderr,"dropping entry: %d in use, %d free, need %d, size_to_free = %d\n", michael@0: JprofBuffer->used,JprofBuffer->space_available(),size,JprofEntrySizeof((malloc_log_entry *) JprofBuffer->head)); michael@0: #endif michael@0: JprofBuffer->drop(JprofEntrySizeof((malloc_log_entry *) JprofBuffer->head)); michael@0: } michael@0: if (JprofBuffer->space_available() < size) michael@0: return; michael@0: michael@0: } while (!JprofBuffer->insert(me,size)); michael@0: } michael@0: michael@0: JPROF_STATIC void michael@0: JprofBufferDump() michael@0: { michael@0: JprofBuffer->lock(); michael@0: #if DEBUG_CIRCULAR michael@0: fprintf(stderr,"dumping JP_CIRCULAR buffer, %d of %d bytes\n", michael@0: JprofBuffer->tail > JprofBuffer->head ? michael@0: JprofBuffer->tail - JprofBuffer->head : michael@0: JprofBuffer->buffer_size + JprofBuffer->tail - JprofBuffer->head, michael@0: JprofBuffer->buffer_size); michael@0: #endif michael@0: if (JprofBuffer->tail >= JprofBuffer->head) { michael@0: write(gLogFD, JprofBuffer->head, JprofBuffer->tail - JprofBuffer->head); michael@0: } else { michael@0: write(gLogFD, JprofBuffer->head, &(JprofBuffer->buffer[JprofBuffer->buffer_size]) - JprofBuffer->head); michael@0: write(gLogFD, JprofBuffer->buffer, JprofBuffer->tail - JprofBuffer->buffer); michael@0: } michael@0: JprofBuffer->clear(); michael@0: JprofBuffer->unlock(); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: JPROF_STATIC void michael@0: JprofLog(u_long aTime, void* stack_top, void* top_instr_ptr) michael@0: { michael@0: // Static is simply to make debugging tolerable michael@0: static malloc_log_entry me; michael@0: michael@0: me.delTime = aTime; michael@0: me.thread = syscall(SYS_gettid); //gettid(); michael@0: if (was_paused) { michael@0: me.flags = JP_FIRST_AFTER_PAUSE; michael@0: was_paused = 0; michael@0: } else { michael@0: me.flags = 0; michael@0: } michael@0: michael@0: CrawlStack(&me, stack_top, top_instr_ptr); michael@0: michael@0: #ifndef NTO michael@0: if (circular) { michael@0: JprofBufferAppend(&me); michael@0: } else { michael@0: write(gLogFD, &me, JprofEntrySizeof(&me)); michael@0: } michael@0: #else michael@0: printf("Neutrino is missing the pcs member of malloc_log_entry!! \n"); michael@0: #endif michael@0: } michael@0: michael@0: static int realTime; michael@0: michael@0: /* Lets interrupt at 10 Hz. This is so my log files don't get too large. michael@0: * This can be changed to a faster value latter. This timer is not michael@0: * programmed to reset, even though it is capable of doing so. This is michael@0: * to keep from getting interrupts from inside of the handler. michael@0: */ michael@0: static void startSignalCounter(unsigned long millisec) michael@0: { michael@0: struct itimerval tvalue; michael@0: michael@0: tvalue.it_interval.tv_sec = 0; michael@0: tvalue.it_interval.tv_usec = 0; michael@0: tvalue.it_value.tv_sec = millisec/1000; michael@0: tvalue.it_value.tv_usec = (millisec%1000)*1000; michael@0: michael@0: if (realTime) { michael@0: setitimer(ITIMER_REAL, &tvalue, nullptr); michael@0: } else { michael@0: setitimer(ITIMER_PROF, &tvalue, nullptr); michael@0: } michael@0: } michael@0: michael@0: static long timerMilliSec = 50; michael@0: michael@0: #if defined(linux) michael@0: static int setupRTCSignals(int hz, struct sigaction *sap) michael@0: { michael@0: /* global */ rtcFD = open("/dev/rtc", O_RDONLY); michael@0: if (rtcFD < 0) { michael@0: perror("JPROF_RTC setup: open(\"/dev/rtc\", O_RDONLY)"); michael@0: return 0; michael@0: } michael@0: michael@0: if (sigaction(SIGIO, sap, nullptr) == -1) { michael@0: perror("JPROF_RTC setup: sigaction(SIGIO)"); michael@0: return 0; michael@0: } michael@0: michael@0: if (ioctl(rtcFD, RTC_IRQP_SET, hz) == -1) { michael@0: perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_IRQP_SET, $JPROF_RTC_HZ)"); michael@0: return 0; michael@0: } michael@0: michael@0: if (ioctl(rtcFD, RTC_PIE_ON, 0) == -1) { michael@0: perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_PIE_ON)"); michael@0: return 0; michael@0: } michael@0: michael@0: if (fcntl(rtcFD, F_SETSIG, 0) == -1) { michael@0: perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETSIG, 0)"); michael@0: return 0; michael@0: } michael@0: michael@0: if (fcntl(rtcFD, F_SETOWN, getpid()) == -1) { michael@0: perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETOWN, getpid())"); michael@0: return 0; michael@0: } michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: static int enableRTCSignals(bool enable) michael@0: { michael@0: static bool enabled = false; michael@0: if (enabled == enable) { michael@0: return 0; michael@0: } michael@0: enabled = enable; michael@0: michael@0: int flags = fcntl(rtcFD, F_GETFL); michael@0: if (flags < 0) { michael@0: perror("JPROF_RTC setup: fcntl(/dev/rtc, F_GETFL)"); michael@0: return 0; michael@0: } michael@0: michael@0: if (enable) { michael@0: flags |= FASYNC; michael@0: } else { michael@0: flags &= ~FASYNC; michael@0: } michael@0: michael@0: if (fcntl(rtcFD, F_SETFL, flags) == -1) { michael@0: if (enable) { michael@0: perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETFL, flags | FASYNC)"); michael@0: } else { michael@0: perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETFL, flags & ~FASYNC)"); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: return 1; michael@0: } michael@0: #endif michael@0: michael@0: JPROF_STATIC void StackHook( michael@0: int signum, michael@0: siginfo_t *info, michael@0: void *ucontext) michael@0: { michael@0: static struct timeval tFirst; michael@0: static int first=1; michael@0: size_t millisec = 0; michael@0: michael@0: #if defined(linux) michael@0: if (rtcHz && pthread_self() != main_thread) { michael@0: // Only collect stack data on the main thread, for now. michael@0: return; michael@0: } michael@0: #endif michael@0: michael@0: if(first && !(first=0)) { michael@0: puts("Jprof: received first signal"); michael@0: #if defined(linux) michael@0: if (rtcHz) { michael@0: enableRTCSignals(true); michael@0: } else michael@0: #endif michael@0: { michael@0: gettimeofday(&tFirst, 0); michael@0: millisec = 0; michael@0: } michael@0: } else { michael@0: #if defined(linux) michael@0: if (rtcHz) { michael@0: enableRTCSignals(true); michael@0: } else michael@0: #endif michael@0: { michael@0: struct timeval tNow; michael@0: gettimeofday(&tNow, 0); michael@0: double usec = 1e6*(tNow.tv_sec - tFirst.tv_sec); michael@0: usec += (tNow.tv_usec - tFirst.tv_usec); michael@0: millisec = static_cast(usec*1e-3); michael@0: } michael@0: } michael@0: michael@0: gregset_t &gregs = ((ucontext_t*)ucontext)->uc_mcontext.gregs; michael@0: #ifdef __x86_64__ michael@0: JprofLog(millisec, (void*)gregs[REG_RSP], (void*)gregs[REG_RIP]); michael@0: #else michael@0: JprofLog(millisec, (void*)gregs[REG_ESP], (void*)gregs[REG_EIP]); michael@0: #endif michael@0: michael@0: if (!rtcHz) michael@0: startSignalCounter(timerMilliSec); michael@0: } michael@0: michael@0: NS_EXPORT_(void) setupProfilingStuff(void) michael@0: { michael@0: static int gFirstTime = 1; michael@0: char filename[2048]; // XXX fix michael@0: michael@0: if(gFirstTime && !(gFirstTime=0)) { michael@0: int startTimer = 1; michael@0: int doNotStart = 1; michael@0: int firstDelay = 0; michael@0: int append = O_TRUNC; michael@0: char *tst = getenv("JPROF_FLAGS"); michael@0: michael@0: /* Options from JPROF_FLAGS environment variable: michael@0: * JP_DEFER -> Wait for a SIGPROF (or SIGALRM, if JP_REALTIME michael@0: * is set) from userland before starting michael@0: * to generate them internally michael@0: * JP_START -> Install the signal handler michael@0: * JP_PERIOD -> Time between profiler ticks michael@0: * JP_FIRST -> Extra delay before starting michael@0: * JP_REALTIME -> Take stack traces in intervals of real time michael@0: * rather than time used by the process (and the michael@0: * system for the process). This is useful for michael@0: * finding time spent by the X server. michael@0: * JP_APPEND -> Append to jprof-log rather than overwriting it. michael@0: * This is somewhat risky since it depends on the michael@0: * address map staying constant across multiple runs. michael@0: * JP_FILENAME -> base filename to use when saving logs. Note that michael@0: * this does not affect the mapfile. michael@0: * JP_CIRCULAR -> use a circular buffer of size N, write/clear on SIGUSR1 michael@0: * michael@0: * JPROF_SLAVE is set if this is not the first process. michael@0: */ michael@0: michael@0: circular = false; michael@0: michael@0: if(tst) { michael@0: if(strstr(tst, "JP_DEFER")) michael@0: { michael@0: doNotStart = 0; michael@0: startTimer = 0; michael@0: } michael@0: if(strstr(tst, "JP_START")) doNotStart = 0; michael@0: if(strstr(tst, "JP_REALTIME")) realTime = 1; michael@0: if(strstr(tst, "JP_APPEND")) append = O_APPEND; michael@0: michael@0: char *delay = strstr(tst,"JP_PERIOD="); michael@0: if(delay) { michael@0: double tmp = strtod(delay+strlen("JP_PERIOD="), nullptr); michael@0: if (tmp>=1e-3) { michael@0: timerMilliSec = static_cast(1000 * tmp); michael@0: } else { michael@0: fprintf(stderr, michael@0: "JP_PERIOD of %g less than 0.001 (1ms), using 1ms\n", michael@0: tmp); michael@0: timerMilliSec = 1; michael@0: } michael@0: } michael@0: michael@0: char *circular_op = strstr(tst,"JP_CIRCULAR="); michael@0: if(circular_op) { michael@0: size_t size = atol(circular_op+strlen("JP_CIRCULAR=")); michael@0: if (size < 1000) { michael@0: fprintf(stderr, michael@0: "JP_CIRCULAR of %d less than 1000, using 10000\n", michael@0: size); michael@0: size = 10000; michael@0: } michael@0: JprofBufferInit(size); michael@0: fprintf(stderr,"JP_CIRCULAR buffer of %d bytes\n",size); michael@0: circular = true; michael@0: } michael@0: michael@0: char *first = strstr(tst, "JP_FIRST="); michael@0: if(first) { michael@0: firstDelay = atol(first+strlen("JP_FIRST=")); michael@0: } michael@0: michael@0: char *rtc = strstr(tst, "JP_RTC_HZ="); michael@0: if (rtc) { michael@0: #if defined(linux) michael@0: rtcHz = atol(rtc+strlen("JP_RTC_HZ=")); michael@0: timerMilliSec = 0; /* This makes JP_FIRST work right. */ michael@0: realTime = 1; /* It's the _R_TC and all. ;) */ michael@0: michael@0: #define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0) michael@0: michael@0: if (!IS_POWER_OF_TWO(rtcHz) || rtcHz < 2) { michael@0: fprintf(stderr, "JP_RTC_HZ must be power of two and >= 2, " michael@0: "but %d was provided; using default of 2048\n", michael@0: rtcHz); michael@0: rtcHz = 2048; michael@0: } michael@0: #else michael@0: fputs("JP_RTC_HZ found, but RTC profiling only supported on " michael@0: "Linux!\n", stderr); michael@0: michael@0: #endif michael@0: } michael@0: char *f = strstr(tst,"JP_FILENAME="); michael@0: if (f) michael@0: f = f + strlen("JP_FILENAME="); michael@0: else michael@0: f = M_LOGFILE; michael@0: michael@0: char *is_slave = getenv("JPROF_SLAVE"); michael@0: if (!is_slave) michael@0: setenv("JPROF_SLAVE","", 0); michael@0: michael@0: int pid = syscall(SYS_gettid); //gettid(); michael@0: if (is_slave) michael@0: snprintf(filename,sizeof(filename),"%s-%d",f,pid); michael@0: else michael@0: snprintf(filename,sizeof(filename),"%s",f); michael@0: michael@0: // XXX FIX! inherit current capture state! michael@0: } michael@0: michael@0: if(!doNotStart) { michael@0: michael@0: if(gLogFD<0) { michael@0: gLogFD = open(filename, O_CREAT | O_WRONLY | append, 0666); michael@0: if(gLogFD<0) { michael@0: fprintf(stderr, "Unable to create " M_LOGFILE); michael@0: perror(":"); michael@0: } else { michael@0: struct sigaction action; michael@0: sigset_t mset; michael@0: michael@0: // Dump out the address map when we terminate michael@0: RegisterJprofShutdown(); michael@0: michael@0: main_thread = pthread_self(); michael@0: //fprintf(stderr,"jprof: main_thread = %u\n", michael@0: // (unsigned int)main_thread); michael@0: michael@0: // FIX! probably should block these against each other michael@0: // Very unlikely. michael@0: sigemptyset(&mset); michael@0: action.sa_handler = nullptr; michael@0: action.sa_sigaction = StackHook; michael@0: action.sa_mask = mset; michael@0: action.sa_flags = SA_RESTART | SA_SIGINFO; michael@0: #if defined(linux) michael@0: if (rtcHz) { michael@0: if (!setupRTCSignals(rtcHz, &action)) { michael@0: fputs("jprof: Error initializing RTC, NOT " michael@0: "profiling\n", stderr); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if (!rtcHz || firstDelay != 0) michael@0: #endif michael@0: { michael@0: if (realTime) { michael@0: sigaction(SIGALRM, &action, nullptr); michael@0: } michael@0: } michael@0: // enable PROF in all cases to simplify JP_DEFER/pause/restart michael@0: sigaction(SIGPROF, &action, nullptr); michael@0: michael@0: // make it so a SIGUSR1 will stop the profiling michael@0: // Note: It currently does not close the logfile. michael@0: // This could be configurable (so that it could michael@0: // later be reopened). michael@0: michael@0: struct sigaction stop_action; michael@0: stop_action.sa_handler = EndProfilingHook; michael@0: stop_action.sa_mask = mset; michael@0: stop_action.sa_flags = SA_RESTART; michael@0: sigaction(SIGUSR1, &stop_action, nullptr); michael@0: michael@0: // make it so a SIGUSR2 will clear the circular buffer michael@0: michael@0: stop_action.sa_handler = ClearProfilingHook; michael@0: stop_action.sa_mask = mset; michael@0: stop_action.sa_flags = SA_RESTART; michael@0: sigaction(SIGUSR2, &stop_action, nullptr); michael@0: michael@0: printf("Jprof: Initialized signal handler and set " michael@0: "timer for %lu %s, %d s " michael@0: "initial delay\n", michael@0: rtcHz ? rtcHz : timerMilliSec, michael@0: rtcHz ? "Hz" : "ms", michael@0: firstDelay); michael@0: michael@0: if(startTimer) { michael@0: #if defined(linux) michael@0: /* If we have an initial delay we can just use michael@0: startSignalCounter to set up a timer to fire the michael@0: first stackHook after that delay. When that happens michael@0: we'll go and switch to RTC profiling. */ michael@0: if (rtcHz && firstDelay == 0) { michael@0: puts("Jprof: enabled RTC signals"); michael@0: enableRTCSignals(true); michael@0: } else michael@0: #endif michael@0: { michael@0: puts("Jprof: started timer"); michael@0: startSignalCounter(firstDelay*1000 + timerMilliSec); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: printf("setupProfilingStuff() called multiple times\n"); michael@0: } michael@0: }