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