CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
SimpleProfiler.cc
Go to the documentation of this file.
1 
2 #ifndef _POSIX_C_SOURCE
3 #define _POSIX_C_SOURCE 199309
4 #endif
5 
6 #if __linux
7 # include <execinfo.h>
8 # include <ucontext.h>
9 # include <sys/syscall.h>
10 #endif
11 #if __APPLE__
12 #include <signal.h>
13 //the following is needed to use the deprecated ucontext method on OS X
14 #define _XOPEN_SOURCE
15 #endif
16 
17 
18 #include <pthread.h>
19 #include <unistd.h>
20 #include <dlfcn.h>
21 
22 #include <cassert>
23 #include <cstdio>
24 #include <cstring>
25 #include <iostream>
26 #include <sstream>
27 #include <stdexcept>
28 
29 #include <sys/time.h>
30 #include <sys/stat.h>
31 #include <sys/resource.h>
32 #include <fcntl.h>
33 #include <ucontext.h>
34 #include <execinfo.h>
35 
36 #include "SimpleProfiler.h"
37 #include "ProfParse.h"
38 
39 #ifdef __linux
40 #ifndef __USE_POSIX199309
41 #error "SimpleProfile requires the definition of __USE_POSIX199309"
42 #endif
43 #endif
44 
45 
46 namespace INSTR
47 {
48  typedef unsigned char byte;
49  const byte RET = 0xc3;
50 }
51 
52 namespace
53 {
54 
55  std::string makeFileName()
56  {
57  pid_t p = getpid();
58  std::ostringstream ost;
59  ost << "profdata_" << p;
60  return ost.str();
61  }
62 }
63 
64 // ---------------------------------------------------------------------
65 // Macros, for this compilation unit only
66 // ---------------------------------------------------------------------
67 
68 #define MUST_BE_ZERO(fun) if((fun)!=0) { perror("function failed"); abort(); }
69 #define getBP(X) asm ( "movl %%ebp,%0" : "=m" (X) )
70 #define getSP(X) asm ( "movl %%esp,%0" : "=m" (X) )
71 
72 #if 0
73 #define DODEBUG if(1) std::cerr
74 #else
75 #define DODEBUG if(0) std::cerr
76 #endif
77 
78 
79 
80 #ifndef __USE_GNU
81 const int REG_EIP = 14;
82 const int REG_EBP = 6;
83 const int REG_ESP = 7;
84 #endif
85 
86 #include "unistd.h"
87 #include <sstream>
88 #include <fstream>
89 
90 
91 namespace
92 {
93 
94  // Record the dynamic library mapping information from /proc/ for
95  // later use in discovering the names of functions recorded as
96  // 'unknown_*'.
97  void write_maps()
98  {
99  pid_t pid = getpid();
100  std::ostringstream strinput,stroutput;
101  strinput << "/proc/" << pid << "/maps";
102  stroutput << "profdata_" << pid << "_maps";
103  std::ifstream input(strinput.str().c_str());
104  std::ofstream output(stroutput.str().c_str());
105  std::string line;
106  while (getline(input, line)) output << line << '\n';
107  input.close();
108  output.close();
109  }
110 
111  FILE* frame_cond = 0;
112 
113  void openCondFile()
114  {
115  std::string filename(makeFileName());
116  filename += "_condfile";
117  frame_cond = fopen(filename.c_str(),"w");
118  if(frame_cond==0)
119  {
120  std::cerr << "bad open of profdata_condfile\n";
121  throw std::runtime_error("bad open");
122  }
123  }
124 
125  void writeCond(int code)
126  {
127  fwrite(&code,sizeof(int),1,frame_cond);
128  }
129 
130  void closeCondFile()
131  {
132  fclose(frame_cond);
133  }
134 
135  void dumpStack(const char* msg,
136  unsigned int* esp, unsigned int* ebp, unsigned char* eip,
137  ucontext_t* ucp)
138  {
139 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
140  throw std::logic_error("Cannot dumpStack on 64 bit build");
141 #else
142  fprintf(frame_cond, msg);
143  fflush(frame_cond);
144  return;
145  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",
146  eip, eip[0], (void *)ebp, (void *)esp, ebp[0], ebp[1], ebp[2]);
147  fflush(frame_cond);
148 
149 
150 #if 0
151  unsigned int* spp = esp;
152  for(int i=15;i>-5;--i)
153  {
154  fprintf(frame_cond, " %x esp[%d]= %x\n", (void*)(spp+i), i, (void*)*(spp+i));
155  fflush(frame_cond);
156  }
157 #else
158  while(ucp->uc_link)
159  {
160  fprintf(frame_cond, " %p\n", (void *)ucp->uc_link);
161  ucp = ucp->uc_link;
162  }
163 #endif
164 #endif
165  }
166 }
167 
168 namespace
169 {
170  // counters
171  int samples_total=0;
172  int samples_missing_framepointer=0;
173 }
174 
175 // ---------------------------------------------------------------------
176 // This is the stack trace discovery code from IgHookTrace.
177 // ---------------------------------------------------------------------
178 
179 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
180 # define HAVE_UNWIND_BACKTRACE 1
181 #endif
182 #if !defined MAP_ANONYMOUS && defined MAP_ANON
183 # define MAP_ANONYMOUS MAP_ANON
184 #endif
185 
186 #if HAVE_UNWIND_BACKTRACE
187 struct IgHookTraceArgs { void **array; int count; int size; };
188 extern "C" {
189  typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__)));
190  struct _Unwind_Context;
191  enum _Unwind_Reason_Code
192  {
193  _URC_NO_REASON = 0,
194  _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
195  _URC_FATAL_PHASE2_ERROR = 2,
196  _URC_FATAL_PHASE1_ERROR = 3,
197  _URC_NORMAL_STOP = 4,
198  _URC_END_OF_STACK = 5,
199  _URC_HANDLER_FOUND = 6,
200  _URC_INSTALL_CONTEXT = 7,
201  _URC_CONTINUE_UNWIND = 8
202  };
203 
204  typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn) (_Unwind_Context *, void *);
205  _Unwind_Reason_Code _Unwind_Backtrace (_Unwind_Trace_Fn, void *);
206  _Unwind_Ptr _Unwind_GetIP (_Unwind_Context *);
207 }
208 
209 #ifndef __linux
210 static _Unwind_Reason_Code
211 GCCBackTrace (_Unwind_Context *context, void *arg)
212 {
213  IgHookTraceArgs *args = (IgHookTraceArgs *) arg;
214  if (args->count >= 0 && args->count < args->size)
215  args->array [args->count++] = (void *) _Unwind_GetIP (context);
216  else
217  return _URC_END_OF_STACK;
218  return _URC_NO_REASON;
219 }
220 #endif
221 #endif
222 
223 
224 int stacktrace (void *addresses[], int nmax)
225 {
226  ++samples_total;
227 #if __linux && __i386
228 # if ! __x86_64__
229 # define PROBABLY_VSYSCALL_PAGE 0xffff0000
230 # else
231 # define PROBABLY_VSYSCALL_PAGE 0xffffffff00000000
232 # endif
233  struct frame
234  {
235  // Normal frame.
236  frame *ebp;
237  void *eip;
238  // Signal frame stuff, put in here by kernel.
239  int signo;
240  siginfo_t *info;
241  ucontext_t *ctx;
242  };
243  // register frame *ebp __asm__ ("ebp");
244  // register frame *esp __asm__ ("esp");
245  // use macros to avoid compiler warning.
246 #define getBP(X) asm ( "movl %%ebp,%0" : "=m" (X) )
247 #define getSP(X) asm ( "movl %%esp,%0" : "=m" (X) )
248  register frame* ebp = 0;
249  getBP(ebp);
250  register frame* esp = 0;
251  getSP(esp);
252 
253  frame *fp = ebp;
254  int depth = 0;
255 
256  // Add fake entry to be compatible with other methods
257  if (depth < nmax) addresses[depth++] = reinterpret_cast<void *>(reinterpret_cast<unsigned long>(&stacktrace));
258 
259  // Top-most frame ends with null pointer; check the rest is reasonable
260  while (depth < nmax && fp >= esp)
261  {
262  // Add this stack frame. The return address is the
263  // instruction immediately after the "call". The call
264  // instruction itself is 4 or 6 bytes; we guess 4.
265  addresses[depth++] = (char *) fp->eip - 4;
266 
267  // Recognise signal frames. We use two different methods
268  // depending on the linux kernel version.
269  //
270  // For the "old" kernels / systems we check the instructions
271  // at the caller's return address. We take it to be a signal
272  // frame if we find the signal return code sequence there
273  // and the thread register context structure pointer:
274  //
275  // mov $__NR_rt_sigreturn, %eax
276  // int 0x80
277  //
278  // For the "new" kernels / systems the operating system maps
279  // a "vsyscall" page at a high address, and it may contain
280  // either the above code, or use of the sysenter/sysexit
281  // instructions. We cannot poke at that page so we take the
282  // the high address as an indication this is a signal frame.
283  // (http://www.trilithium.com/johan/2005/08/linux-gate/)
284  // (http://manugarg.googlepages.com/systemcallinlinux2_6.html)
285  //
286  // If we don't recognise the signal frame correctly here, we
287  // lose one stack frame: signal delivery is not a call so
288  // when the signal handler is entered, ebp still points to
289  // what it was just before the signal.
290  unsigned char *insn = (unsigned char *) fp->eip;
291  if (insn
292  && insn[0] == 0xb8 && insn[1] == __NR_rt_sigreturn
293  && insn[5] == 0xcd && insn[6] == 0x80
294  && fp->ctx)
295  {
296  void *retip = (void *) fp->ctx->uc_mcontext.gregs [REG_EIP];
297  if (depth < nmax) addresses[depth++] = retip;
298 
299  fp = (frame *) fp->ctx->uc_mcontext.gregs [REG_EBP];
300  if (fp && (unsigned long) retip > PROBABLY_VSYSCALL_PAGE)
301  {
302  // __kernel_vsyscall stack on system call exit is
303  // [0] %ebp, [1] %edx, [2] %ecx, [3] return address.
304  if (depth < nmax) addresses[depth++] = ((void **) fp)[3];
305  fp = fp->ebp;
306 
307  // It seems the frame _above_ __kernel_syscall (the
308  // syscall implementation in libc, such as __mmap())
309  // is essentially frame-pointer-less, so we should
310  // find also the call above, but I don't know how
311  // to determine how many arguments the system call
312  // pushed on stack to call __kernel_syscall short
313  // of interpreting the DWARF unwind information :-(
314  // So we may lose one level of call stack here.
315  ++samples_missing_framepointer;
316  }
317  }
318 
319  // Otherwise it's a normal frame, process through frame pointer.
320  else
321  fp = fp->ebp;
322  }
323 
324  return depth;
325 #elif __linux
326  return backtrace (addresses, nmax);
327 #elif HAVE_UNWIND_BACKTRACE
328  if (nmax >= 1)
329  {
330  IgHookTraceArgs args = { addresses, 0, nmax };
331  _Unwind_Backtrace (&GCCBackTrace, &args);
332 
333  if (args.count > 1 && args.array [args.count-1] == 0)
334  args.count--;
335 
336  return args.count;
337  }
338  return 0;
339 #else
340  return 0;
341 #endif
342 }
343 
344 // ---------------------------------------------------------------------
345 // The signal handler. We register this to handle the SIGPROF signal.
346 // ---------------------------------------------------------------------
347 
348 extern "C"
349 {
350  void sigFunc(int sig, siginfo_t* info, void* context)
351  {
353  void** arr = prof->tempStack();
354  int nmax = prof->tempStackSize();
355  int stackdepth = stacktrace(arr, nmax);
356 
357  assert(stackdepth <= nmax);
358 
359  // We don't want the first three entries, because they always
360  // contain information about the how the signal handler was
361  // called, the signal handler, and the stacktrace function.
362  prof->commitFrame(arr+3, arr + stackdepth);
363  }
364 }
365 
366 namespace
367 {
368 
369 #if USE_SIGALTSTACK
370  stack_t ss_area;
371 #endif
372 
373  void setupTimer();
374 
375  void* setStacktop()
376  {
377 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
378  throw std::logic_error("setStacktop not callable on 64 bit platform");
379  return 0;
380 #else
381  const std::string target_name("__libc_start_main");
382  unsigned int* ebp_reg;
383  getBP(ebp_reg);
384  unsigned int* ebp = (unsigned int*)(ebp_reg);
385  unsigned int* top = (unsigned int*)(ebp);
386 
387  void* address_of_target = 0;
388  const int max_stackdepth=30;
389  void* sta[max_stackdepth];
390 
391  int depth = backtrace(sta, max_stackdepth);
392  int cnt = depth;
393  if (depth > 1) depth-=1;
394 
395  // find function one below main()
396  Dl_info look;
397  for (int i=0; i<cnt && !address_of_target; ++i)
398  {
399  if(dladdr(sta[i],&look)!=0)
400  {
401  if (look.dli_saddr && target_name==look.dli_sname)
402  {
403  address_of_target = sta[i];
404  }
405  }
406  else
407  {
408  // This isn't really an error; it just means the function
409  // was not found by the dynamic loader. The function might
410  // be one that is declared 'static', and is thus not
411  // visible outside of its compilation unit.
412  std::cerr << "setStacktop: no function information for "
413  << sta[i]
414  << "\n";
415  }
416  }
417 
418  if (address_of_target == 0)
419  throw std::runtime_error("no main function found in stack");
420 
421  //fprintf(stderr,"depth=%d top=%8.8x\n",depth,top);
422 
423  // Now we walk toward the beginning of the stack until we find the
424  // frame that is holding the return address of our target function.
425 
426  // depth is how many more frames there are to look at in the stack.
427  // top is the frame we are currently looking at.
428  // top+1 is the address to which the current frame will return.
429  while (depth>0 && (void*)*(top+1) != address_of_target)
430  {
431  //fprintf(stderr,"depth=%d top=%8.8x func=%8.8x\n",depth,top,*(top+1));
432  if (top<(unsigned int*)0x10) fprintf(stderr,"problem\n");
433  top=(unsigned int*)(*top);
434  --depth;
435  };
436 
437  if (depth==0)
438  throw std::runtime_error("setStacktop: could not find stack bottom");
439 
440  // Now we have to move one frame more, to the frame of the target
441  // function. We want the location in memory of this frame (not any
442  // address stored in the frame, but the address of the frame
443  // itself).
444  top=(unsigned int*)(*top);
445 
446  return top;
447 #endif
448  }
449 
450  void setupTimer()
451  {
452 #if USE_SIGALTSTACK
453  static vector<char> charbuffer(SIGSTKSZ);
454  //ss_area.ss_sp = new char[SIGSTKSZ];
455  ss_area.ss_sp = &charbuffer[0];
456  ss_area.ss_flags = 0;
457  ss_area.ss_size = SIGSTKSZ;
458 #endif
459  //static int is_set = 0;
460  //if(is_set!=1) { ++is_set; return; }
461  //else ++is_set;
462 
463  sigset_t myset,oldset;
464  // all blocked for now
465  MUST_BE_ZERO(sigfillset(&myset));
466  MUST_BE_ZERO(pthread_sigmask(SIG_SETMASK,&myset,&oldset));
467 
468 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
469 #if __linux
470  // ignore all the RT signals
471  struct sigaction tmpact;
472  memset(&tmpact,0,sizeof(tmpact));
473  tmpact.sa_handler = SIG_IGN;
474 
475  for(int num=SIGRTMIN;num<SIGRTMAX;++num)
476  {
477  MUST_BE_ZERO(sigaddset(&oldset,num));
478  MUST_BE_ZERO(sigaction(num,&tmpact,NULL));
479  }
480 #endif
481 #endif
482 
483 #if USE_SIGALTSTACK
484  if(sigaltstack(&ss_area,0)!=0)
485  {
486  perror("sigaltstack failed for profile timer interrupt");
487  abort();
488  }
489 #endif
490 
491  // set up my RT signal now
492  struct sigaction act;
493  memset(&act,0,sizeof(act));
494  act.sa_sigaction = sigFunc;
495  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
496 
497  // get my signal number
498  int mysig = SIGPROF;
499 
500  if(sigaction(mysig,&act,NULL)!=0)
501  {
502  perror("sigaction failed");
503  abort();
504  }
505 
506  // Turn off handling of SIGSEGV signal
507  memset(&act,0,sizeof(act));
508  act.sa_handler = SIG_DFL;
509 
510  if (sigaction(SIGSEGV, &act, NULL) != 0)
511  {
512  perror("sigaction failed");
513  abort();
514  }
515 
516  struct rlimit limits;
517  if (getrlimit(RLIMIT_CORE, &limits) != 0)
518  {
519  perror("getrlimit failed");
520  abort();
521  }
522  std::cerr << "core size limit (soft): " << limits.rlim_cur << '\n';
523  std::cerr << "core size limit (hard): " << limits.rlim_max << '\n';
524 
525  struct itimerval newval;
526  struct itimerval oldval;
527 
528  newval.it_interval.tv_sec = 0;
529  newval.it_interval.tv_usec = 10000;
530  newval.it_value.tv_sec = 0;
531  newval.it_value.tv_usec = 10000;
532 
533  if(setitimer(ITIMER_PROF,&newval,&oldval)!=0)
534  {
535  perror("setitimer failed");
536  abort();
537  }
538 
539  // reenable the signals, including my interval timer
540  MUST_BE_ZERO(sigdelset(&oldset,mysig));
541  MUST_BE_ZERO(pthread_sigmask(SIG_SETMASK,&oldset,0));
542  }
543 
544 
545  struct AdjustSigs
546  {
547  AdjustSigs()
548  {
549  sigset_t myset,oldset;
550  // all blocked for now
551  MUST_BE_ZERO(sigfillset(&myset));
552  MUST_BE_ZERO(pthread_sigmask(SIG_SETMASK,&myset,&oldset));
553 
554  // ignore all the RT signals
555  struct sigaction tmpact;
556  memset(&tmpact,0,sizeof(tmpact));
557  tmpact.sa_handler = SIG_IGN;
558 
559 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
560 #if __linux
561  for(int num=SIGRTMIN;num<SIGRTMAX;++num)
562  {
563  MUST_BE_ZERO(sigaddset(&oldset,num));
564  MUST_BE_ZERO(sigaction(num,&tmpact,NULL));
565  }
566 #endif
567 #endif
568 
569  MUST_BE_ZERO(sigaddset(&oldset,SIGPROF));
570  MUST_BE_ZERO(pthread_sigmask(SIG_SETMASK,&oldset,0));
571  }
572  };
573 
574  static AdjustSigs global_adjust;
575 
576 
577 }
578 
581 
582 
584 {
585  if(SimpleProfiler::inst_ == 0)
586  {
587  boost::mutex::scoped_lock sl(lock_);
588  if(SimpleProfiler::inst_ == 0)
589  {
590  static SimpleProfiler p;
591  SimpleProfiler::inst_ = &p;
592  }
593  }
594  return SimpleProfiler::inst_;
595 }
596 
598  frame_data_(10*1000*1000),
599  tmp_stack_(1000),
600  high_water_(&frame_data_[10*1000*1000-10*1000]),
601  curr_(&frame_data_[0]),
602  filename_(makeFileName()),
603  fd_(open(filename_.c_str(),
604  O_RDWR|O_CREAT,
605  S_IRWXU|S_IRGRP|S_IROTH|S_IWGRP|S_IWOTH)),
606  installed_(false),
607  running_(false),
608  owner_(),
609  stacktop_(setStacktop())
610 {
611  if (fd_ < 0) {
612  std::ostringstream ost;
613  ost << "failed to open profiler output file " << filename_;
614  throw std::runtime_error(ost.str().c_str());
615  }
616 
617  openCondFile();
618 }
619 
621 {
622  if (running_)
623  std::cerr << "Warning: the profile timer was not stopped,\n"
624  << "profiling data in " << filename_
625  << " is probably incomplete and will not be processed\n";
626 
627  closeCondFile();
628 }
629 
631 {
632  void** cnt_ptr = curr_;
633  *cnt_ptr = reinterpret_cast<void*>(std::distance(first,last));
634  ++curr_;
635  curr_ = std::copy(first,last,curr_);
636  if(curr_ > high_water_) doWrite();
637 }
638 
640 {
641  void** start = &frame_data_[0];
642  if(curr_ == start) return;
643  unsigned int cnt = std::distance(start,curr_) * sizeof(void*);
644  unsigned int totwr=0;
645 
646  while (cnt>0 && (totwr=write(fd_,start,cnt)) != cnt) {
647  if(totwr==0)
648  throw std::runtime_error("SimpleProfiler::doWrite wrote zero bytes");
649  start+=(totwr/sizeof(void*));
650  cnt-=totwr;
651  }
652 
653  curr_ = &frame_data_[0];
654 }
655 
657 {
658 #if defined(__x86_64__) || defined(__LP64__) || defined(_LP64)
659  throw std::logic_error("SimpleProfiler not available on 64 bit platform");
660 #endif
661  {
662  boost::mutex::scoped_lock sl(lock_);
663 
664  if (installed_) {
665  std::cerr << "Warning: second thread " << pthread_self()
666  << " requested the profiler timer and another thread\n"
667  << owner_ << "has already started it.\n"
668  << "Only one thread can do profiling at a time.\n"
669  << "This second thread will not be profiled.\n";
670  return;
671  }
672 
673  installed_ = true;
674  }
675 
676  owner_ = pthread_self();
677  setupTimer();
678  running_ = true;
679 }
680 
682 {
683  if(!installed_)
684  {
685  std::cerr << "SimpleProfiler::stop - no timer installed to stop\n";
686  return;
687  }
688 
689  if(owner_ != pthread_self())
690  {
691  std::cerr << "SimpleProfiler::stop - only owning thread can stop the timer\n";
692  return;
693  }
694 
695  if(!running_)
696  {
697  std::cerr << "SimpleProfiler::stop - no timer is running\n";
698  return;
699  }
700 
701  struct itimerval newval;
702 
703  newval.it_interval.tv_sec = 0;
704  newval.it_interval.tv_usec = 0;
705  newval.it_value.tv_sec = 0;
706  newval.it_value.tv_usec = 0;
707 
708  if(setitimer(ITIMER_PROF,&newval,0)!=0)
709  {
710  perror("setitimer call failed - could not stop the timer");
711  }
712 
713  running_=false;
714  complete();
715 }
716 
717 
718 
720 {
721  doWrite();
722 
723  if(lseek(fd_,0,SEEK_SET)<0)
724  {
725  std::cerr << "SimpleProfiler: could not seek to the start of the profile\n"
726  << " data file during completion. Data will be lost.\n";
727  return;
728  }
729 
731  write_maps();
732 
733  std::string totsname = makeFileName();
734  totsname += "_sample_info";
735  std::ofstream ost(totsname.c_str());
736 
737  ost << "samples_total " << samples_total << "\n"
738  << "samples_missing_framepointer " << samples_missing_framepointer << "\n" ;
739 }
std::string makeFileName(const std::string &base, int num)
Definition: TestConsumer.cc:18
pthread_t owner_
int i
Definition: DBlmapReader.cc:9
#define getBP(X)
VoidVec frame_data_
class Geom::OnePiRange __attribute__
static boost::mutex mutex
Definition: LHEProxy.cc:11
void commitFrame(void **first, void **last)
#define getSP(X)
const int REG_EIP
static SimpleProfiler * inst_
static boost::mutex lock_
#define NULL
Definition: scimark2.h:8
std::string filename_
A arg
Definition: Factorize.h:36
void ** high_water_
unsigned char byte
const int REG_EBP
bool first
Definition: L1TdeRCT.cc:79
tuple input
Definition: collect_tpl.py:10
static SimpleProfiler * instance()
const byte RET
const int REG_ESP
#define MUST_BE_ZERO(fun)
long long int num
Definition: procUtils.cc:71
void ** tempStack()
dictionary args
int stacktrace(void *addresses[], int nmax)
tuple filename
Definition: lut2db_cfg.py:20
perl if(1 lt scalar(@::datatypes))
Definition: edlooper.cc:31
void writeProfileData(int fd, const std::string &prefix)
Definition: ProfParse.cc:159
void sigFunc(int sig, siginfo_t *info, void *context)
size_type tempStackSize()
tuple size
Write out results.