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