1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/jprof/stub/libmalloc.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,781 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +// vim:cindent:sw=4:et:ts=8: 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// The linux glibc hides part of sigaction if _POSIX_SOURCE is defined 1.11 +#if defined(linux) 1.12 +#undef _POSIX_SOURCE 1.13 +#undef _SVID_SOURCE 1.14 +#ifndef _GNU_SOURCE 1.15 +#define _GNU_SOURCE 1.16 +#endif 1.17 +#endif 1.18 + 1.19 +#include <errno.h> 1.20 +#if defined(linux) 1.21 +#include <linux/rtc.h> 1.22 +#include <pthread.h> 1.23 +#endif 1.24 +#include <unistd.h> 1.25 +#include <fcntl.h> 1.26 +#include <stdio.h> 1.27 +#include <stdlib.h> 1.28 +#include <signal.h> 1.29 +#include <sys/time.h> 1.30 +#include <sys/types.h> 1.31 +#include <sys/ioctl.h> 1.32 +#include <sys/stat.h> 1.33 +#include <sys/syscall.h> 1.34 +#include <ucontext.h> 1.35 +#include <execinfo.h> 1.36 + 1.37 +#include "libmalloc.h" 1.38 +#include "jprof.h" 1.39 +#include <string.h> 1.40 +#include <errno.h> 1.41 +#include <dlfcn.h> 1.42 + 1.43 +// Must define before including jprof.h 1.44 +void *moz_xmalloc(size_t size) 1.45 +{ 1.46 + return malloc(size); 1.47 +} 1.48 + 1.49 +void moz_xfree(void *mem) 1.50 +{ 1.51 + free(mem); 1.52 +} 1.53 + 1.54 +#ifdef NTO 1.55 +#include <sys/link.h> 1.56 +extern r_debug _r_debug; 1.57 +#else 1.58 +#include <link.h> 1.59 +#endif 1.60 + 1.61 +#define USE_GLIBC_BACKTRACE 1 1.62 +// To debug, use #define JPROF_STATIC 1.63 +#define JPROF_STATIC //static 1.64 + 1.65 +static int gLogFD = -1; 1.66 +static pthread_t main_thread; 1.67 + 1.68 +static void startSignalCounter(unsigned long millisec); 1.69 +static int enableRTCSignals(bool enable); 1.70 + 1.71 + 1.72 +//---------------------------------------------------------------------- 1.73 +// replace use of atexit() 1.74 + 1.75 +static void DumpAddressMap(); 1.76 + 1.77 +struct JprofShutdown { 1.78 + JprofShutdown() {} 1.79 + ~JprofShutdown() { 1.80 + DumpAddressMap(); 1.81 + } 1.82 +}; 1.83 + 1.84 +static void RegisterJprofShutdown() { 1.85 + // This instanciates the dummy class above, and will trigger the class 1.86 + // destructor when libxul is unloaded. This is equivalent to atexit(), 1.87 + // but gracefully handles dlclose(). 1.88 + static JprofShutdown t; 1.89 +} 1.90 + 1.91 +#if defined(i386) || defined(_i386) || defined(__x86_64__) 1.92 +JPROF_STATIC void CrawlStack(malloc_log_entry* me, 1.93 + void* stack_top, void* top_instr_ptr) 1.94 +{ 1.95 +#if USE_GLIBC_BACKTRACE 1.96 + // This probably works on more than x86! But we need a way to get the 1.97 + // top instruction pointer, which is kindof arch-specific 1.98 + void *array[500]; 1.99 + int cnt, i; 1.100 + u_long numpcs = 0; 1.101 + bool tracing = false; 1.102 + 1.103 + // This is from glibc. A more generic version might use 1.104 + // libunwind and/or CaptureStackBackTrace() on Windows 1.105 + cnt = backtrace(&array[0],sizeof(array)/sizeof(array[0])); 1.106 + 1.107 + // StackHook->JprofLog->CrawlStack 1.108 + // Then we have sigaction, which replaced top_instr_ptr 1.109 + array[3] = top_instr_ptr; 1.110 + for (i = 3; i < cnt; i++) 1.111 + { 1.112 + me->pcs[numpcs++] = (char *) array[i]; 1.113 + } 1.114 + me->numpcs = numpcs; 1.115 + 1.116 +#else 1.117 + // original code - this breaks on many platforms 1.118 + void **bp; 1.119 +#if defined(__i386) 1.120 + __asm__( "movl %%ebp, %0" : "=g"(bp)); 1.121 +#elif defined(__x86_64__) 1.122 + __asm__( "movq %%rbp, %0" : "=g"(bp)); 1.123 +#else 1.124 + // It would be nice if this worked uniformly, but at least on i386 and 1.125 + // x86_64, it stopped working with gcc 4.1, because it points to the 1.126 + // end of the saved registers instead of the start. 1.127 + bp = __builtin_frame_address(0); 1.128 +#endif 1.129 + u_long numpcs = 0; 1.130 + bool tracing = false; 1.131 + 1.132 + me->pcs[numpcs++] = (char*) top_instr_ptr; 1.133 + 1.134 + while (numpcs < MAX_STACK_CRAWL) { 1.135 + void** nextbp = (void**) *bp++; 1.136 + void* pc = *bp; 1.137 + if (nextbp < bp) { 1.138 + break; 1.139 + } 1.140 + if (tracing) { 1.141 + // Skip the signal handling. 1.142 + me->pcs[numpcs++] = (char*) pc; 1.143 + } 1.144 + else if (pc == top_instr_ptr) { 1.145 + tracing = true; 1.146 + } 1.147 + bp = nextbp; 1.148 + } 1.149 + me->numpcs = numpcs; 1.150 +#endif 1.151 +} 1.152 +#endif 1.153 + 1.154 +//---------------------------------------------------------------------- 1.155 + 1.156 +static int rtcHz; 1.157 +static int rtcFD = -1; 1.158 +static bool circular = false; 1.159 + 1.160 +#if defined(linux) || defined(NTO) 1.161 +static void DumpAddressMap() 1.162 +{ 1.163 + // Turn off the timer so we don't get interrupts during shutdown 1.164 +#if defined(linux) 1.165 + if (rtcHz) { 1.166 + enableRTCSignals(false); 1.167 + } else 1.168 +#endif 1.169 + { 1.170 + startSignalCounter(0); 1.171 + } 1.172 + 1.173 + int mfd = open(M_MAPFILE, O_CREAT|O_WRONLY|O_TRUNC, 0666); 1.174 + if (mfd >= 0) { 1.175 + malloc_map_entry mme; 1.176 + link_map* map = _r_debug.r_map; 1.177 + while (nullptr != map) { 1.178 + if (map->l_name && *map->l_name) { 1.179 + mme.nameLen = strlen(map->l_name); 1.180 + mme.address = map->l_addr; 1.181 + write(mfd, &mme, sizeof(mme)); 1.182 + write(mfd, map->l_name, mme.nameLen); 1.183 +#if 0 1.184 + write(1, map->l_name, mme.nameLen); 1.185 + write(1, "\n", 1); 1.186 +#endif 1.187 + } 1.188 + map = map->l_next; 1.189 + } 1.190 + close(mfd); 1.191 + } 1.192 +} 1.193 +#endif 1.194 + 1.195 +static bool was_paused = true; 1.196 + 1.197 +JPROF_STATIC void JprofBufferDump(); 1.198 +JPROF_STATIC void JprofBufferClear(); 1.199 + 1.200 +static void ClearProfilingHook(int signum) 1.201 +{ 1.202 + if (circular) { 1.203 + JprofBufferClear(); 1.204 + puts("Jprof: cleared circular buffer."); 1.205 + } 1.206 +} 1.207 + 1.208 +static void EndProfilingHook(int signum) 1.209 +{ 1.210 + if (circular) 1.211 + JprofBufferDump(); 1.212 + 1.213 + DumpAddressMap(); 1.214 + was_paused = true; 1.215 + puts("Jprof: profiling paused."); 1.216 +} 1.217 + 1.218 + 1.219 + 1.220 +//---------------------------------------------------------------------- 1.221 +// proper usage would be a template, including the function to find the 1.222 +// size of an entry, or include a size header explicitly to each entry. 1.223 +#if defined(linux) 1.224 +#define DUMB_LOCK() pthread_mutex_lock(&mutex); 1.225 +#define DUMB_UNLOCK() pthread_mutex_unlock(&mutex); 1.226 +#else 1.227 +#define DUMB_LOCK() FIXME() 1.228 +#define DUMB_UNLOCK() FIXME() 1.229 +#endif 1.230 + 1.231 + 1.232 +class DumbCircularBuffer 1.233 +{ 1.234 +public: 1.235 + DumbCircularBuffer(size_t init_buffer_size) { 1.236 + used = 0; 1.237 + buffer_size = init_buffer_size; 1.238 + buffer = (unsigned char *) malloc(buffer_size); 1.239 + head = tail = buffer; 1.240 + 1.241 +#if defined(linux) 1.242 + pthread_mutexattr_t mAttr; 1.243 + pthread_mutexattr_settype(&mAttr, PTHREAD_MUTEX_RECURSIVE_NP); 1.244 + pthread_mutex_init(&mutex, &mAttr); 1.245 + pthread_mutexattr_destroy(&mAttr); 1.246 +#endif 1.247 + } 1.248 + ~DumbCircularBuffer() { 1.249 + free(buffer); 1.250 +#if defined(linux) 1.251 + pthread_mutex_destroy (&mutex); 1.252 +#endif 1.253 + } 1.254 + 1.255 + void clear() { 1.256 + DUMB_LOCK(); 1.257 + head = tail; 1.258 + used = 0; 1.259 + DUMB_UNLOCK(); 1.260 + } 1.261 + 1.262 + bool empty() { 1.263 + return head == tail; 1.264 + } 1.265 + 1.266 + size_t space_available() { 1.267 + size_t result; 1.268 + DUMB_LOCK(); 1.269 + if (tail > head) 1.270 + result = buffer_size - (tail-head) - 1; 1.271 + else 1.272 + result = head-tail - 1; 1.273 + DUMB_UNLOCK(); 1.274 + return result; 1.275 + } 1.276 + 1.277 + void drop(size_t size) { 1.278 + // assumes correctness! 1.279 + DUMB_LOCK(); 1.280 + head += size; 1.281 + if (head >= &buffer[buffer_size]) 1.282 + head -= buffer_size; 1.283 + used--; 1.284 + DUMB_UNLOCK(); 1.285 + } 1.286 + 1.287 + bool insert(void *data, size_t size) { 1.288 + // can fail if not enough space in the entire buffer 1.289 + DUMB_LOCK(); 1.290 + if (space_available() < size) 1.291 + return false; 1.292 + 1.293 + size_t max_without_wrap = &buffer[buffer_size] - tail; 1.294 + size_t initial = size > max_without_wrap ? max_without_wrap : size; 1.295 +#if DEBUG_CIRCULAR 1.296 + fprintf(stderr,"insert(%d): max_without_wrap %d, size %d, initial %d\n",used,max_without_wrap,size,initial); 1.297 +#endif 1.298 + memcpy(tail,data,initial); 1.299 + tail += initial; 1.300 + data = ((char *)data)+initial; 1.301 + size -= initial; 1.302 + if (size != 0) { 1.303 +#if DEBUG_CIRCULAR 1.304 + fprintf(stderr,"wrapping by %d bytes\n",size); 1.305 +#endif 1.306 + memcpy(buffer,data,size); 1.307 + tail = &(((unsigned char *)buffer)[size]); 1.308 + } 1.309 + 1.310 + used++; 1.311 + DUMB_UNLOCK(); 1.312 + 1.313 + return true; 1.314 + } 1.315 + 1.316 + // for external access to the buffer (saving) 1.317 + void lock() { 1.318 + DUMB_LOCK(); 1.319 + } 1.320 + 1.321 + void unlock() { 1.322 + DUMB_UNLOCK(); 1.323 + } 1.324 + 1.325 + // XXX These really shouldn't be public... 1.326 + unsigned char *head; 1.327 + unsigned char *tail; 1.328 + unsigned int used; 1.329 + unsigned char *buffer; 1.330 + size_t buffer_size; 1.331 + 1.332 +private: 1.333 + pthread_mutex_t mutex; 1.334 +}; 1.335 + 1.336 +class DumbCircularBuffer *JprofBuffer; 1.337 + 1.338 +JPROF_STATIC void 1.339 +JprofBufferInit(size_t size) 1.340 +{ 1.341 + JprofBuffer = new DumbCircularBuffer(size); 1.342 +} 1.343 + 1.344 +JPROF_STATIC void 1.345 +JprofBufferClear() 1.346 +{ 1.347 + fprintf(stderr,"Told to clear JPROF circular buffer\n"); 1.348 + JprofBuffer->clear(); 1.349 +} 1.350 + 1.351 +JPROF_STATIC size_t 1.352 +JprofEntrySizeof(malloc_log_entry *me) 1.353 +{ 1.354 + return offsetof(malloc_log_entry, pcs) + me->numpcs*sizeof(char*); 1.355 +} 1.356 + 1.357 +JPROF_STATIC void 1.358 +JprofBufferAppend(malloc_log_entry *me) 1.359 +{ 1.360 + size_t size = JprofEntrySizeof(me); 1.361 + 1.362 + do { 1.363 + while (JprofBuffer->space_available() < size && 1.364 + JprofBuffer->used > 0) { 1.365 +#if DEBUG_CIRCULAR 1.366 + fprintf(stderr,"dropping entry: %d in use, %d free, need %d, size_to_free = %d\n", 1.367 + JprofBuffer->used,JprofBuffer->space_available(),size,JprofEntrySizeof((malloc_log_entry *) JprofBuffer->head)); 1.368 +#endif 1.369 + JprofBuffer->drop(JprofEntrySizeof((malloc_log_entry *) JprofBuffer->head)); 1.370 + } 1.371 + if (JprofBuffer->space_available() < size) 1.372 + return; 1.373 + 1.374 + } while (!JprofBuffer->insert(me,size)); 1.375 +} 1.376 + 1.377 +JPROF_STATIC void 1.378 +JprofBufferDump() 1.379 +{ 1.380 + JprofBuffer->lock(); 1.381 +#if DEBUG_CIRCULAR 1.382 + fprintf(stderr,"dumping JP_CIRCULAR buffer, %d of %d bytes\n", 1.383 + JprofBuffer->tail > JprofBuffer->head ? 1.384 + JprofBuffer->tail - JprofBuffer->head : 1.385 + JprofBuffer->buffer_size + JprofBuffer->tail - JprofBuffer->head, 1.386 + JprofBuffer->buffer_size); 1.387 +#endif 1.388 + if (JprofBuffer->tail >= JprofBuffer->head) { 1.389 + write(gLogFD, JprofBuffer->head, JprofBuffer->tail - JprofBuffer->head); 1.390 + } else { 1.391 + write(gLogFD, JprofBuffer->head, &(JprofBuffer->buffer[JprofBuffer->buffer_size]) - JprofBuffer->head); 1.392 + write(gLogFD, JprofBuffer->buffer, JprofBuffer->tail - JprofBuffer->buffer); 1.393 + } 1.394 + JprofBuffer->clear(); 1.395 + JprofBuffer->unlock(); 1.396 +} 1.397 + 1.398 +//---------------------------------------------------------------------- 1.399 + 1.400 +JPROF_STATIC void 1.401 +JprofLog(u_long aTime, void* stack_top, void* top_instr_ptr) 1.402 +{ 1.403 + // Static is simply to make debugging tolerable 1.404 + static malloc_log_entry me; 1.405 + 1.406 + me.delTime = aTime; 1.407 + me.thread = syscall(SYS_gettid); //gettid(); 1.408 + if (was_paused) { 1.409 + me.flags = JP_FIRST_AFTER_PAUSE; 1.410 + was_paused = 0; 1.411 + } else { 1.412 + me.flags = 0; 1.413 + } 1.414 + 1.415 + CrawlStack(&me, stack_top, top_instr_ptr); 1.416 + 1.417 +#ifndef NTO 1.418 + if (circular) { 1.419 + JprofBufferAppend(&me); 1.420 + } else { 1.421 + write(gLogFD, &me, JprofEntrySizeof(&me)); 1.422 + } 1.423 +#else 1.424 + printf("Neutrino is missing the pcs member of malloc_log_entry!! \n"); 1.425 +#endif 1.426 +} 1.427 + 1.428 +static int realTime; 1.429 + 1.430 +/* Lets interrupt at 10 Hz. This is so my log files don't get too large. 1.431 + * This can be changed to a faster value latter. This timer is not 1.432 + * programmed to reset, even though it is capable of doing so. This is 1.433 + * to keep from getting interrupts from inside of the handler. 1.434 +*/ 1.435 +static void startSignalCounter(unsigned long millisec) 1.436 +{ 1.437 + struct itimerval tvalue; 1.438 + 1.439 + tvalue.it_interval.tv_sec = 0; 1.440 + tvalue.it_interval.tv_usec = 0; 1.441 + tvalue.it_value.tv_sec = millisec/1000; 1.442 + tvalue.it_value.tv_usec = (millisec%1000)*1000; 1.443 + 1.444 + if (realTime) { 1.445 + setitimer(ITIMER_REAL, &tvalue, nullptr); 1.446 + } else { 1.447 + setitimer(ITIMER_PROF, &tvalue, nullptr); 1.448 + } 1.449 +} 1.450 + 1.451 +static long timerMilliSec = 50; 1.452 + 1.453 +#if defined(linux) 1.454 +static int setupRTCSignals(int hz, struct sigaction *sap) 1.455 +{ 1.456 + /* global */ rtcFD = open("/dev/rtc", O_RDONLY); 1.457 + if (rtcFD < 0) { 1.458 + perror("JPROF_RTC setup: open(\"/dev/rtc\", O_RDONLY)"); 1.459 + return 0; 1.460 + } 1.461 + 1.462 + if (sigaction(SIGIO, sap, nullptr) == -1) { 1.463 + perror("JPROF_RTC setup: sigaction(SIGIO)"); 1.464 + return 0; 1.465 + } 1.466 + 1.467 + if (ioctl(rtcFD, RTC_IRQP_SET, hz) == -1) { 1.468 + perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_IRQP_SET, $JPROF_RTC_HZ)"); 1.469 + return 0; 1.470 + } 1.471 + 1.472 + if (ioctl(rtcFD, RTC_PIE_ON, 0) == -1) { 1.473 + perror("JPROF_RTC setup: ioctl(/dev/rtc, RTC_PIE_ON)"); 1.474 + return 0; 1.475 + } 1.476 + 1.477 + if (fcntl(rtcFD, F_SETSIG, 0) == -1) { 1.478 + perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETSIG, 0)"); 1.479 + return 0; 1.480 + } 1.481 + 1.482 + if (fcntl(rtcFD, F_SETOWN, getpid()) == -1) { 1.483 + perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETOWN, getpid())"); 1.484 + return 0; 1.485 + } 1.486 + 1.487 + return 1; 1.488 +} 1.489 + 1.490 +static int enableRTCSignals(bool enable) 1.491 +{ 1.492 + static bool enabled = false; 1.493 + if (enabled == enable) { 1.494 + return 0; 1.495 + } 1.496 + enabled = enable; 1.497 + 1.498 + int flags = fcntl(rtcFD, F_GETFL); 1.499 + if (flags < 0) { 1.500 + perror("JPROF_RTC setup: fcntl(/dev/rtc, F_GETFL)"); 1.501 + return 0; 1.502 + } 1.503 + 1.504 + if (enable) { 1.505 + flags |= FASYNC; 1.506 + } else { 1.507 + flags &= ~FASYNC; 1.508 + } 1.509 + 1.510 + if (fcntl(rtcFD, F_SETFL, flags) == -1) { 1.511 + if (enable) { 1.512 + perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETFL, flags | FASYNC)"); 1.513 + } else { 1.514 + perror("JPROF_RTC setup: fcntl(/dev/rtc, F_SETFL, flags & ~FASYNC)"); 1.515 + } 1.516 + return 0; 1.517 + } 1.518 + 1.519 + return 1; 1.520 +} 1.521 +#endif 1.522 + 1.523 +JPROF_STATIC void StackHook( 1.524 +int signum, 1.525 +siginfo_t *info, 1.526 +void *ucontext) 1.527 +{ 1.528 + static struct timeval tFirst; 1.529 + static int first=1; 1.530 + size_t millisec = 0; 1.531 + 1.532 +#if defined(linux) 1.533 + if (rtcHz && pthread_self() != main_thread) { 1.534 + // Only collect stack data on the main thread, for now. 1.535 + return; 1.536 + } 1.537 +#endif 1.538 + 1.539 + if(first && !(first=0)) { 1.540 + puts("Jprof: received first signal"); 1.541 +#if defined(linux) 1.542 + if (rtcHz) { 1.543 + enableRTCSignals(true); 1.544 + } else 1.545 +#endif 1.546 + { 1.547 + gettimeofday(&tFirst, 0); 1.548 + millisec = 0; 1.549 + } 1.550 + } else { 1.551 +#if defined(linux) 1.552 + if (rtcHz) { 1.553 + enableRTCSignals(true); 1.554 + } else 1.555 +#endif 1.556 + { 1.557 + struct timeval tNow; 1.558 + gettimeofday(&tNow, 0); 1.559 + double usec = 1e6*(tNow.tv_sec - tFirst.tv_sec); 1.560 + usec += (tNow.tv_usec - tFirst.tv_usec); 1.561 + millisec = static_cast<size_t>(usec*1e-3); 1.562 + } 1.563 + } 1.564 + 1.565 + gregset_t &gregs = ((ucontext_t*)ucontext)->uc_mcontext.gregs; 1.566 +#ifdef __x86_64__ 1.567 + JprofLog(millisec, (void*)gregs[REG_RSP], (void*)gregs[REG_RIP]); 1.568 +#else 1.569 + JprofLog(millisec, (void*)gregs[REG_ESP], (void*)gregs[REG_EIP]); 1.570 +#endif 1.571 + 1.572 + if (!rtcHz) 1.573 + startSignalCounter(timerMilliSec); 1.574 +} 1.575 + 1.576 +NS_EXPORT_(void) setupProfilingStuff(void) 1.577 +{ 1.578 + static int gFirstTime = 1; 1.579 + char filename[2048]; // XXX fix 1.580 + 1.581 + if(gFirstTime && !(gFirstTime=0)) { 1.582 + int startTimer = 1; 1.583 + int doNotStart = 1; 1.584 + int firstDelay = 0; 1.585 + int append = O_TRUNC; 1.586 + char *tst = getenv("JPROF_FLAGS"); 1.587 + 1.588 + /* Options from JPROF_FLAGS environment variable: 1.589 + * JP_DEFER -> Wait for a SIGPROF (or SIGALRM, if JP_REALTIME 1.590 + * is set) from userland before starting 1.591 + * to generate them internally 1.592 + * JP_START -> Install the signal handler 1.593 + * JP_PERIOD -> Time between profiler ticks 1.594 + * JP_FIRST -> Extra delay before starting 1.595 + * JP_REALTIME -> Take stack traces in intervals of real time 1.596 + * rather than time used by the process (and the 1.597 + * system for the process). This is useful for 1.598 + * finding time spent by the X server. 1.599 + * JP_APPEND -> Append to jprof-log rather than overwriting it. 1.600 + * This is somewhat risky since it depends on the 1.601 + * address map staying constant across multiple runs. 1.602 + * JP_FILENAME -> base filename to use when saving logs. Note that 1.603 + * this does not affect the mapfile. 1.604 + * JP_CIRCULAR -> use a circular buffer of size N, write/clear on SIGUSR1 1.605 + * 1.606 + * JPROF_SLAVE is set if this is not the first process. 1.607 + */ 1.608 + 1.609 + circular = false; 1.610 + 1.611 + if(tst) { 1.612 + if(strstr(tst, "JP_DEFER")) 1.613 + { 1.614 + doNotStart = 0; 1.615 + startTimer = 0; 1.616 + } 1.617 + if(strstr(tst, "JP_START")) doNotStart = 0; 1.618 + if(strstr(tst, "JP_REALTIME")) realTime = 1; 1.619 + if(strstr(tst, "JP_APPEND")) append = O_APPEND; 1.620 + 1.621 + char *delay = strstr(tst,"JP_PERIOD="); 1.622 + if(delay) { 1.623 + double tmp = strtod(delay+strlen("JP_PERIOD="), nullptr); 1.624 + if (tmp>=1e-3) { 1.625 + timerMilliSec = static_cast<unsigned long>(1000 * tmp); 1.626 + } else { 1.627 + fprintf(stderr, 1.628 + "JP_PERIOD of %g less than 0.001 (1ms), using 1ms\n", 1.629 + tmp); 1.630 + timerMilliSec = 1; 1.631 + } 1.632 + } 1.633 + 1.634 + char *circular_op = strstr(tst,"JP_CIRCULAR="); 1.635 + if(circular_op) { 1.636 + size_t size = atol(circular_op+strlen("JP_CIRCULAR=")); 1.637 + if (size < 1000) { 1.638 + fprintf(stderr, 1.639 + "JP_CIRCULAR of %d less than 1000, using 10000\n", 1.640 + size); 1.641 + size = 10000; 1.642 + } 1.643 + JprofBufferInit(size); 1.644 + fprintf(stderr,"JP_CIRCULAR buffer of %d bytes\n",size); 1.645 + circular = true; 1.646 + } 1.647 + 1.648 + char *first = strstr(tst, "JP_FIRST="); 1.649 + if(first) { 1.650 + firstDelay = atol(first+strlen("JP_FIRST=")); 1.651 + } 1.652 + 1.653 + char *rtc = strstr(tst, "JP_RTC_HZ="); 1.654 + if (rtc) { 1.655 +#if defined(linux) 1.656 + rtcHz = atol(rtc+strlen("JP_RTC_HZ=")); 1.657 + timerMilliSec = 0; /* This makes JP_FIRST work right. */ 1.658 + realTime = 1; /* It's the _R_TC and all. ;) */ 1.659 + 1.660 +#define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0) 1.661 + 1.662 + if (!IS_POWER_OF_TWO(rtcHz) || rtcHz < 2) { 1.663 + fprintf(stderr, "JP_RTC_HZ must be power of two and >= 2, " 1.664 + "but %d was provided; using default of 2048\n", 1.665 + rtcHz); 1.666 + rtcHz = 2048; 1.667 + } 1.668 +#else 1.669 + fputs("JP_RTC_HZ found, but RTC profiling only supported on " 1.670 + "Linux!\n", stderr); 1.671 + 1.672 +#endif 1.673 + } 1.674 + char *f = strstr(tst,"JP_FILENAME="); 1.675 + if (f) 1.676 + f = f + strlen("JP_FILENAME="); 1.677 + else 1.678 + f = M_LOGFILE; 1.679 + 1.680 + char *is_slave = getenv("JPROF_SLAVE"); 1.681 + if (!is_slave) 1.682 + setenv("JPROF_SLAVE","", 0); 1.683 + 1.684 + int pid = syscall(SYS_gettid); //gettid(); 1.685 + if (is_slave) 1.686 + snprintf(filename,sizeof(filename),"%s-%d",f,pid); 1.687 + else 1.688 + snprintf(filename,sizeof(filename),"%s",f); 1.689 + 1.690 + // XXX FIX! inherit current capture state! 1.691 + } 1.692 + 1.693 + if(!doNotStart) { 1.694 + 1.695 + if(gLogFD<0) { 1.696 + gLogFD = open(filename, O_CREAT | O_WRONLY | append, 0666); 1.697 + if(gLogFD<0) { 1.698 + fprintf(stderr, "Unable to create " M_LOGFILE); 1.699 + perror(":"); 1.700 + } else { 1.701 + struct sigaction action; 1.702 + sigset_t mset; 1.703 + 1.704 + // Dump out the address map when we terminate 1.705 + RegisterJprofShutdown(); 1.706 + 1.707 + main_thread = pthread_self(); 1.708 + //fprintf(stderr,"jprof: main_thread = %u\n", 1.709 + // (unsigned int)main_thread); 1.710 + 1.711 + // FIX! probably should block these against each other 1.712 + // Very unlikely. 1.713 + sigemptyset(&mset); 1.714 + action.sa_handler = nullptr; 1.715 + action.sa_sigaction = StackHook; 1.716 + action.sa_mask = mset; 1.717 + action.sa_flags = SA_RESTART | SA_SIGINFO; 1.718 +#if defined(linux) 1.719 + if (rtcHz) { 1.720 + if (!setupRTCSignals(rtcHz, &action)) { 1.721 + fputs("jprof: Error initializing RTC, NOT " 1.722 + "profiling\n", stderr); 1.723 + return; 1.724 + } 1.725 + } 1.726 + 1.727 + if (!rtcHz || firstDelay != 0) 1.728 +#endif 1.729 + { 1.730 + if (realTime) { 1.731 + sigaction(SIGALRM, &action, nullptr); 1.732 + } 1.733 + } 1.734 + // enable PROF in all cases to simplify JP_DEFER/pause/restart 1.735 + sigaction(SIGPROF, &action, nullptr); 1.736 + 1.737 + // make it so a SIGUSR1 will stop the profiling 1.738 + // Note: It currently does not close the logfile. 1.739 + // This could be configurable (so that it could 1.740 + // later be reopened). 1.741 + 1.742 + struct sigaction stop_action; 1.743 + stop_action.sa_handler = EndProfilingHook; 1.744 + stop_action.sa_mask = mset; 1.745 + stop_action.sa_flags = SA_RESTART; 1.746 + sigaction(SIGUSR1, &stop_action, nullptr); 1.747 + 1.748 + // make it so a SIGUSR2 will clear the circular buffer 1.749 + 1.750 + stop_action.sa_handler = ClearProfilingHook; 1.751 + stop_action.sa_mask = mset; 1.752 + stop_action.sa_flags = SA_RESTART; 1.753 + sigaction(SIGUSR2, &stop_action, nullptr); 1.754 + 1.755 + printf("Jprof: Initialized signal handler and set " 1.756 + "timer for %lu %s, %d s " 1.757 + "initial delay\n", 1.758 + rtcHz ? rtcHz : timerMilliSec, 1.759 + rtcHz ? "Hz" : "ms", 1.760 + firstDelay); 1.761 + 1.762 + if(startTimer) { 1.763 +#if defined(linux) 1.764 + /* If we have an initial delay we can just use 1.765 + startSignalCounter to set up a timer to fire the 1.766 + first stackHook after that delay. When that happens 1.767 + we'll go and switch to RTC profiling. */ 1.768 + if (rtcHz && firstDelay == 0) { 1.769 + puts("Jprof: enabled RTC signals"); 1.770 + enableRTCSignals(true); 1.771 + } else 1.772 +#endif 1.773 + { 1.774 + puts("Jprof: started timer"); 1.775 + startSignalCounter(firstDelay*1000 + timerMilliSec); 1.776 + } 1.777 + } 1.778 + } 1.779 + } 1.780 + } 1.781 + } else { 1.782 + printf("setupProfilingStuff() called multiple times\n"); 1.783 + } 1.784 +}