00001
00002 #ifndef _POSIX_C_SOURCE
00003 #define _POSIX_C_SOURCE 199309
00004 #endif
00005
00006 #if __linux
00007 # include <execinfo.h>
00008 # include <ucontext.h>
00009 # include <sys/syscall.h>
00010 #endif
00011 #if __APPLE__
00012 #include <signal.h>
00013
00014 #define _XOPEN_SOURCE
00015 #endif
00016
00017
00018 #include <pthread.h>
00019 #include <unistd.h>
00020 #include <dlfcn.h>
00021
00022 #include <cassert>
00023 #include <cstdio>
00024 #include <cstring>
00025 #include <iostream>
00026 #include <sstream>
00027 #include <stdexcept>
00028
00029 #include <sys/time.h>
00030 #include <sys/stat.h>
00031 #include <sys/resource.h>
00032 #include <fcntl.h>
00033 #include <ucontext.h>
00034 #include <execinfo.h>
00035
00036 #include "SimpleProfiler.h"
00037 #include "ProfParse.h"
00038
00039 #ifdef __linux
00040 #ifndef __USE_POSIX199309
00041 #error "SimpleProfile requires the definition of __USE_POSIX199309"
00042 #endif
00043 #endif
00044
00045 namespace INSTR {
00046 typedef unsigned char byte;
00047 byte const RET = 0xc3;
00048 }
00049
00050 namespace {
00051
00052 std::string makeFileName() {
00053 pid_t p = getpid();
00054 std::ostringstream ost;
00055 ost << "profdata_" << p;
00056 return ost.str();
00057 }
00058 }
00059
00060
00061
00062
00063
00064 #define MUST_BE_ZERO(fun) if((fun) != 0) { perror("function failed"); abort(); }
00065 #define getBP(X) asm ( "movl %%ebp,%0" : "=m" (X) )
00066 #define getSP(X) asm ( "movl %%esp,%0" : "=m" (X) )
00067
00068 #if 0
00069 #define DODEBUG if(1) std::cerr
00070 #else
00071 #define DODEBUG if(0) std::cerr
00072 #endif
00073
00074 #ifndef __USE_GNU
00075 int const REG_EIP = 14;
00076 int const REG_EBP = 6;
00077 int const REG_ESP = 7;
00078 #endif
00079
00080 #include "unistd.h"
00081 #include <sstream>
00082 #include <fstream>
00083
00084
00085 namespace {
00086
00087
00088
00089
00090 void write_maps() {
00091 pid_t pid = getpid();
00092 std::ostringstream strinput, stroutput;
00093 strinput << "/proc/" << pid << "/maps";
00094 stroutput << "profdata_" << pid << "_maps";
00095 std::ifstream input(strinput.str().c_str());
00096 std::ofstream output(stroutput.str().c_str());
00097 std::string line;
00098 while(getline(input, line)) output << line << '\n';
00099 input.close();
00100 output.close();
00101 }
00102
00103 FILE* frame_cond = 0;
00104
00105 void openCondFile() {
00106 std::string filename(makeFileName());
00107 filename += "_condfile";
00108 frame_cond = fopen(filename.c_str(), "w");
00109 if(frame_cond == 0) {
00110 std::cerr << "bad open of profdata_condfile\n";
00111 throw std::runtime_error("bad open");
00112 }
00113 }
00114
00115 void writeCond(int code) {
00116 fwrite(&code, sizeof(int), 1, frame_cond);
00117 }
00118
00119 void closeCondFile() {
00120 fclose(frame_cond);
00121 }
00122
00123 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
00124 void dumpStack(char const*, unsigned int*, unsigned int*, unsigned char*, ucontext_t*) {
00125 throw std::logic_error("Cannot dumpStack on 64 bit build");
00126 }
00127 #else
00128 void dumpStack(char const* msg,
00129 unsigned int* esp, unsigned int* ebp, unsigned char* eip,
00130 ucontext_t* ucp) {
00131 fprintf(frame_cond, msg);
00132 fflush(frame_cond);
00133 return;
00134 fprintf(frame_cond, "dumpStack:\n i= %p\n eip[0]= %2.2x\nb= %p\n s= %p\n b[0]= %x\n b[1]= %x\n b[2]= %x\n",
00135 eip, eip[0], (void*)ebp, (void*)esp, ebp[0], ebp[1], ebp[2]);
00136 fflush(frame_cond);
00137
00138 #if 0
00139 unsigned int* spp = esp;
00140 for(int i = 15; i > -5; --i) {
00141 fprintf(frame_cond, " %x esp[%d]= %x\n", (void*)(spp + i), i, (void*)*(spp + i));
00142 fflush(frame_cond);
00143 }
00144 #else
00145 while(ucp->uc_link) {
00146 fprintf(frame_cond, " %p\n", (void*)ucp->uc_link);
00147 ucp = ucp->uc_link;
00148 }
00149 #endif
00150 }
00151 #endif
00152 }
00153
00154 namespace {
00155
00156 int samples_total = 0;
00157 int samples_missing_framepointer = 0;
00158 }
00159
00160
00161
00162
00163
00164 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
00165 # define HAVE_UNWIND_BACKTRACE 1
00166 #endif
00167 #if !defined MAP_ANONYMOUS && defined MAP_ANON
00168 # define MAP_ANONYMOUS MAP_ANON
00169 #endif
00170
00171 #if HAVE_UNWIND_BACKTRACE
00172 struct IgHookTraceArgs { void** array; int count; int size; };
00173 extern "C" {
00174 typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__)));
00175 struct _Unwind_Context;
00176 enum _Unwind_Reason_Code
00177 {
00178 _URC_NO_REASON = 0,
00179 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
00180 _URC_FATAL_PHASE2_ERROR = 2,
00181 _URC_FATAL_PHASE1_ERROR = 3,
00182 _URC_NORMAL_STOP = 4,
00183 _URC_END_OF_STACK = 5,
00184 _URC_HANDLER_FOUND = 6,
00185 _URC_INSTALL_CONTEXT = 7,
00186 _URC_CONTINUE_UNWIND = 8
00187 };
00188
00189 typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn) (_Unwind_Context*, void*);
00190 _Unwind_Reason_Code _Unwind_Backtrace (_Unwind_Trace_Fn, void*);
00191 _Unwind_Ptr _Unwind_GetIP (_Unwind_Context*);
00192 }
00193
00194 #ifndef __linux
00195 static _Unwind_Reason_Code
00196 GCCBackTrace (_Unwind_Context* context, void* arg) {
00197 IgHookTraceArgs* args = (IgHookTraceArgs*)arg;
00198 if(args->count >= 0 && args->count < args->size)
00199 args->array [args->count++] = (void*)_Unwind_GetIP (context);
00200 else
00201 return _URC_END_OF_STACK;
00202 return _URC_NO_REASON;
00203 }
00204 #endif
00205 #endif
00206
00207 int stacktrace (void* addresses[], int nmax) {
00208 ++samples_total;
00209 #if __linux && __i386
00210 # if ! __x86_64__
00211 # define PROBABLY_VSYSCALL_PAGE 0xffff0000
00212 # else
00213 # define PROBABLY_VSYSCALL_PAGE 0xffffffff00000000
00214 # endif
00215 struct frame {
00216
00217 frame* ebp;
00218 void* eip;
00219
00220 int* signo;
00221 siginfo_t* info;
00222 ucontext_t* ctx;
00223 };
00224
00225
00226
00227 #define getBP(X) asm ( "movl %%ebp,%0" : "=m" (X) )
00228 #define getSP(X) asm ( "movl %%esp,%0" : "=m" (X) )
00229 register frame* ebp = 0;
00230 getBP(ebp);
00231 register frame* esp = 0;
00232 getSP(esp);
00233
00234 frame* fp = ebp;
00235 int depth = 0;
00236
00237
00238 if(depth < nmax) addresses[depth++] = reinterpret_cast<void*>(reinterpret_cast<unsigned long>(&stacktrace));
00239
00240
00241 while(depth < nmax && fp >= esp) {
00242
00243
00244
00245 addresses[depth++] = (char*)fp->eip - 4;
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270 unsigned char* insn = (unsigned char*)fp->eip;
00271 if(insn
00272 && insn[0] == 0xb8 && insn[1] == __NR_rt_sigreturn
00273 && insn[5] == 0xcd && insn[6] == 0x80
00274 && fp->ctx) {
00275 void* retip = (void*)fp->ctx->uc_mcontext.gregs [REG_EIP];
00276 if(depth < nmax) addresses[depth++] = retip;
00277
00278 fp = (frame*)fp->ctx->uc_mcontext.gregs [REG_EBP];
00279 if(fp && (unsigned long) retip > PROBABLY_VSYSCALL_PAGE) {
00280
00281
00282 if(depth < nmax) addresses[depth++] = ((void**)fp)[3];
00283 fp = fp->ebp;
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293 ++samples_missing_framepointer;
00294 }
00295 }
00296
00297
00298 else
00299 fp = fp->ebp;
00300 }
00301
00302 return depth;
00303 #elif __linux
00304 return backtrace (addresses, nmax);
00305 #elif HAVE_UNWIND_BACKTRACE
00306 if(nmax >= 1) {
00307 IgHookTraceArgs args = { addresses, 0, nmax };
00308 _Unwind_Backtrace (&GCCBackTrace, &args);
00309
00310 if(args.count > 1 && args.array [args.count-1] == 0)
00311 args.count--;
00312
00313 return args.count;
00314 }
00315 return 0;
00316 #else
00317 return 0;
00318 #endif
00319 }
00320
00321
00322
00323
00324
00325 extern "C" {
00326 void sigFunc(int , siginfo_t* , void* ) {
00327 SimpleProfiler* prof = SimpleProfiler::instance();
00328 void** arr = prof->tempStack();
00329 int nmax = prof->tempStackSize();
00330 int stackdepth = stacktrace(arr, nmax);
00331
00332 assert(stackdepth <= nmax);
00333
00334
00335
00336
00337 prof->commitFrame(arr + 3, arr + stackdepth);
00338 }
00339 }
00340
00341 namespace {
00342
00343 #if USE_SIGALTSTACK
00344 stack_t ss_area;
00345 #endif
00346
00347 void setupTimer();
00348
00349 void* setStacktop() {
00350 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
00351 throw std::logic_error("setStacktop not callable on 64 bit platform");
00352 return 0;
00353 #else
00354 std::string consttarget_name("__libc_start_main");
00355 unsigned int* ebp_reg;
00356 getBP(ebp_reg);
00357 unsigned int* ebp = (unsigned int*)(ebp_reg);
00358 unsigned int* top = (unsigned int*)(ebp);
00359
00360 void* address_of_target = 0;
00361 int const max_stackdepth = 30;
00362 void* sta[max_stackdepth];
00363
00364 int depth = backtrace(sta, max_stackdepth);
00365 int cnt = depth;
00366 if(depth > 1) depth -= 1;
00367
00368
00369 Dl_info look;
00370 for(int i = 0; i < cnt && !address_of_target; ++i) {
00371 if(dladdr(sta[i], &look) != 0) {
00372 if(look.dli_saddr && target_name == look.dli_sname) {
00373 address_of_target = sta[i];
00374 }
00375 } else {
00376
00377
00378
00379
00380 std::cerr << "setStacktop: no function information for "
00381 << sta[i]
00382 << "\n";
00383 }
00384 }
00385
00386 if(address_of_target == 0) {
00387 throw std::runtime_error("no main function found in stack");
00388 }
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398 while(depth > 0 && (void*)*(top + 1) != address_of_target) {
00399
00400 if(top < (unsigned int*)0x10) fprintf(stderr, "problem\n");
00401 top = (unsigned int*)(*top);
00402 --depth;
00403 };
00404
00405 if(depth == 0) {
00406 throw std::runtime_error("setStacktop: could not find stack bottom");
00407 }
00408
00409
00410
00411
00412
00413 top = (unsigned int*)(*top);
00414
00415 return top;
00416 #endif
00417 }
00418
00419 void setupTimer() {
00420 #if USE_SIGALTSTACK
00421 static vector<char> charbuffer(SIGSTKSZ);
00422
00423 ss_area.ss_sp = &charbuffer[0];
00424 ss_area.ss_flags = 0;
00425 ss_area.ss_size = SIGSTKSZ;
00426 #endif
00427
00428
00429
00430
00431 sigset_t myset, oldset;
00432
00433 MUST_BE_ZERO(sigfillset(&myset));
00434 MUST_BE_ZERO(pthread_sigmask(SIG_SETMASK, &myset, &oldset));
00435
00436 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
00437 #if __linux
00438
00439 struct sigaction tmpact;
00440 memset(&tmpact, 0, sizeof(tmpact));
00441 tmpact.sa_handler = SIG_IGN;
00442
00443 for(int num=SIGRTMIN; num < SIGRTMAX; ++num) {
00444 MUST_BE_ZERO(sigaddset(&oldset, num));
00445 MUST_BE_ZERO(sigaction(num, &tmpact, NULL));
00446 }
00447 #endif
00448 #endif
00449
00450 #if USE_SIGALTSTACK
00451 if(sigaltstack(&ss_area, 0) != 0) {
00452 perror("sigaltstack failed for profile timer interrupt");
00453 abort();
00454 }
00455 #endif
00456
00457
00458 struct sigaction act;
00459 memset(&act, 0, sizeof(act));
00460 act.sa_sigaction = sigFunc;
00461 act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
00462
00463
00464 int mysig = SIGPROF;
00465
00466 if(sigaction(mysig, &act, NULL) != 0) {
00467 perror("sigaction failed");
00468 abort();
00469 }
00470
00471
00472 memset(&act, 0, sizeof(act));
00473 act.sa_handler = SIG_DFL;
00474
00475 if(sigaction(SIGSEGV, &act, NULL) != 0) {
00476 perror("sigaction failed");
00477 abort();
00478 }
00479
00480 struct rlimit limits;
00481 if(getrlimit(RLIMIT_CORE, &limits) != 0) {
00482 perror("getrlimit failed");
00483 abort();
00484 }
00485 std::cerr << "core size limit (soft): " << limits.rlim_cur << '\n';
00486 std::cerr << "core size limit (hard): " << limits.rlim_max << '\n';
00487
00488 struct itimerval newval;
00489 struct itimerval oldval;
00490
00491 newval.it_interval.tv_sec = 0;
00492 newval.it_interval.tv_usec = 10000;
00493 newval.it_value.tv_sec = 0;
00494 newval.it_value.tv_usec = 10000;
00495
00496 if(setitimer(ITIMER_PROF, &newval, &oldval) != 0) {
00497 perror("setitimer failed");
00498 abort();
00499 }
00500
00501
00502 MUST_BE_ZERO(sigdelset(&oldset, mysig));
00503 MUST_BE_ZERO(pthread_sigmask(SIG_SETMASK, &oldset, 0));
00504 }
00505
00506 struct AdjustSigs {
00507 AdjustSigs()
00508 {
00509 sigset_t myset, oldset;
00510
00511 MUST_BE_ZERO(sigfillset(&myset));
00512 MUST_BE_ZERO(pthread_sigmask(SIG_SETMASK, &myset, &oldset));
00513
00514
00515 struct sigaction tmpact;
00516 memset(&tmpact, 0, sizeof(tmpact));
00517 tmpact.sa_handler = SIG_IGN;
00518
00519 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
00520 #if __linux
00521 for(int num=SIGRTMIN; num<SIGRTMAX; ++num) {
00522 MUST_BE_ZERO(sigaddset(&oldset, num));
00523 MUST_BE_ZERO(sigaction(num, &tmpact, NULL));
00524 }
00525 #endif
00526 #endif
00527
00528 MUST_BE_ZERO(sigaddset(&oldset, SIGPROF));
00529 MUST_BE_ZERO(pthread_sigmask(SIG_SETMASK, &oldset, 0));
00530 }
00531 };
00532
00533 static AdjustSigs global_adjust;
00534 }
00535
00536 SimpleProfiler* SimpleProfiler::inst_ = 0;
00537 boost::mutex SimpleProfiler::lock_;
00538
00539
00540 SimpleProfiler* SimpleProfiler::instance() {
00541 if(SimpleProfiler::inst_ == 0) {
00542 boost::mutex::scoped_lock sl(lock_);
00543 if(SimpleProfiler::inst_ == 0) {
00544 static SimpleProfiler p;
00545 SimpleProfiler::inst_ = &p;
00546 }
00547 }
00548 return SimpleProfiler::inst_;
00549 }
00550
00551 SimpleProfiler::SimpleProfiler():
00552 frame_data_(10*1000*1000),
00553 tmp_stack_(1000),
00554 high_water_(&frame_data_[10*1000*1000-10*1000]),
00555 curr_(&frame_data_[0]),
00556 filename_(makeFileName()),
00557 fd_(open(filename_.c_str(),
00558 O_RDWR|O_CREAT,
00559 S_IRWXU|S_IRGRP|S_IROTH|S_IWGRP|S_IWOTH)),
00560 installed_(false),
00561 running_(false),
00562 owner_(),
00563 stacktop_(setStacktop()) {
00564 if(fd_ < 0) {
00565 std::ostringstream ost;
00566 ost << "failed to open profiler output file " << filename_;
00567 throw std::runtime_error(ost.str().c_str());
00568 }
00569 openCondFile();
00570 }
00571
00572 SimpleProfiler::~SimpleProfiler() {
00573 if(running_) {
00574 std::cerr << "Warning: the profile timer was not stopped,\n"
00575 << "profiling data in " << filename_
00576 << " is probably incomplete and will not be processed\n";
00577 }
00578 closeCondFile();
00579 }
00580
00581 void SimpleProfiler::commitFrame(void** first, void** last) {
00582 void** cnt_ptr = curr_;
00583 *cnt_ptr = reinterpret_cast<void*>(std::distance(first, last));
00584 ++curr_;
00585 curr_ = std::copy(first, last, curr_);
00586 if(curr_ > high_water_) doWrite();
00587 }
00588
00589 void SimpleProfiler::doWrite() {
00590 void** start = &frame_data_[0];
00591 if(curr_ == start) return;
00592 unsigned int cnt = std::distance(start, curr_) * sizeof(void*);
00593 unsigned int totwr = 0;
00594
00595 while(cnt>0 && (totwr = write(fd_, start, cnt)) != cnt) {
00596 if(totwr == 0)
00597 throw std::runtime_error("SimpleProfiler::doWrite wrote zero bytes");
00598 start += (totwr/sizeof(void*));
00599 cnt -= totwr;
00600 }
00601
00602 curr_ = &frame_data_[0];
00603 }
00604
00605 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
00606 void SimpleProfiler::start() {
00607 throw std::logic_error("SimpleProfiler not available on 64 bit platform");
00608 }
00609 #else
00610 void SimpleProfiler::start() {
00611 {
00612 boost::mutex::scoped_lock sl(lock_);
00613
00614 if(installed_) {
00615 std::cerr << "Warning: second thread " << pthread_self()
00616 << " requested the profiler timer and another thread\n"
00617 << owner_ << "has already started it.\n"
00618 << "Only one thread can do profiling at a time.\n"
00619 << "This second thread will not be profiled.\n";
00620 return;
00621 }
00622
00623 installed_ = true;
00624 }
00625
00626 owner_ = pthread_self();
00627 setupTimer();
00628 running_ = true;
00629 }
00630 #endif
00631
00632 void SimpleProfiler::stop() {
00633 if(!installed_) {
00634 std::cerr << "SimpleProfiler::stop - no timer installed to stop\n";
00635 return;
00636 }
00637
00638 if(owner_ != pthread_self()) {
00639 std::cerr << "SimpleProfiler::stop - only owning thread can stop the timer\n";
00640 return;
00641 }
00642
00643 if(!running_) {
00644 std::cerr << "SimpleProfiler::stop - no timer is running\n";
00645 return;
00646 }
00647
00648 struct itimerval newval;
00649
00650 newval.it_interval.tv_sec = 0;
00651 newval.it_interval.tv_usec = 0;
00652 newval.it_value.tv_sec = 0;
00653 newval.it_value.tv_usec = 0;
00654
00655 if(setitimer(ITIMER_PROF, &newval, 0) != 0) {
00656 perror("setitimer call failed - could not stop the timer");
00657 }
00658
00659 running_ = false;
00660 complete();
00661 }
00662
00663 void SimpleProfiler::complete() {
00664 doWrite();
00665
00666 if(lseek(fd_, 0, SEEK_SET) < 0) {
00667 std::cerr << "SimpleProfiler: could not seek to the start of the profile\n"
00668 << " data file during completion. Data will be lost.\n";
00669 return;
00670 }
00671
00672 writeProfileData(fd_, filename_);
00673 write_maps();
00674
00675 std::string totsname = makeFileName();
00676 totsname += "_sample_info";
00677 std::ofstream ost(totsname.c_str());
00678
00679 ost << "samples_total " << samples_total << "\n"
00680 << "samples_missing_framepointer " << samples_missing_framepointer << "\n" ;
00681 }