tools/jprof/stub/libmalloc.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial