add prof2 profiler
[goodguy/cinelerra.git] / cinelerra-5.1 / prof2 / profile.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <signal.h>
4 #include <ucontext.h>
5 #include <pthread.h>
6 #include <link.h>
7
8 #define smCommand "smap 2> /dev/null < "
9 #ifdef __i386__
10 #define nmAddr 0
11 #define nmType 9
12 #define nmName 11
13 #define _LX "%08lx"
14 #ifndef LIB
15 #define LIB "/lib"
16 #endif
17 #endif
18 #ifdef __x86_64__
19 #define nmAddr 0
20 #define nmType 17
21 #define nmName 19
22 #define _LX "%016lx"
23 #ifndef LIB
24 #define LIB "/lib64"
25 #endif
26 #endif
27
28 #define unlikely(x) __builtin_expect(!!(x), 0)
29 #define dprintf(s...) do { if( unlikely(prof_debug!=0) ) fprintf(s); } while(0)
30
31 int profileMain(int ac,char **av,char **ev);
32 void profileStart(void);
33 void profileStop(void);
34 void profileClear(void);
35 void profileShow(void);
36 void profileDone(void);
37
38 #include <string.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <stdarg.h>
42 #include <fcntl.h>
43 #include <time.h>
44 #include <limits.h>
45 #include <sys/time.h>
46 #include <sys/times.h>
47
48 #define MAP2MAX 256
49 #define MAPINCR 64
50 #define MOD2MAX 256
51 #define MODINCR 64
52 #define SYM2MAX 1024*256
53 #define SYMINCR 1024*16
54 #if 0
55 static volatile long prof_debug = 1;
56 static volatile long prof_type = 0;
57 static volatile long prof_tmrval = 10000;
58 static volatile unsigned long prof_stkptr = INT_MAX-0x1000;
59 static volatile char prof_output[256] = { "-" };
60 #else
61 static volatile long prof_debug; /* enable debug output */
62 static volatile long prof_type; /* 0=usr+sys, 1=usr only, 2=real time */
63 static volatile long prof_tmrval; /* profile timer interval in usecs */
64 static volatile unsigned long prof_stkptr; /* top of stack at profileStart */
65 static volatile char prof_output[256]; /* profile output file name */
66 #endif
67
68 static FILE *prof_fp = NULL;
69
70 typedef struct {
71    char *name;
72    unsigned long offs;
73 } tMap;
74
75 typedef struct {
76    int n, m;
77    tMap map[MAPINCR];
78 } tMapTbl;
79
80 typedef struct {
81    char *name;
82    unsigned long offs;
83    int count;
84    int calls;
85 } tMod;
86
87 typedef struct {
88    int n, m;
89    tMod mod[MODINCR];
90 } tModTbl;
91
92 typedef struct {
93    char *name;
94    unsigned long adr;
95    int count;
96    int calls;
97    int tick;
98    int tcalls;
99    int mod;
100 } tSym;
101
102 typedef struct {
103    int n, m;
104    tSym sym[SYMINCR];
105 } tSymTbl;
106
107 static tSymTbl *tsym = NULL;
108 static tModTbl *tmod = NULL;
109 static tMapTbl *tmap = NULL;
110 static int nTicks;               /* profile timer tick count */
111 static struct tms start_cpu;     /* cpu time at profile_start */
112 static struct tms stop_cpu;      /* cpu time at profile_stop */
113 static struct timeval start_clk; /* real time at profile_start */
114 static struct timeval stop_clk;  /* real time at profile_stop */
115
116 int profileActive = 0;           /* flag, true if profile handler active */
117 static struct sigaction oldSig;  /* orignal profile handler data */
118 static struct sigaction oldSegv; /* orignal segv handler data */
119
120 static tMapTbl *
121 newMapTbl(void)
122 {
123    tMapTbl *tp;
124    if( (tp=malloc(sizeof(*tp))) != NULL ) {
125       tp->n = 0;
126       tp->m = sizeof(tp->map)/sizeof(tp->map[0]);
127    }
128    return tp;
129 }
130
131 static tModTbl *
132 newModTbl(void)
133 {
134    tModTbl *tp;
135    if( (tp=malloc(sizeof(*tp))) != NULL ) {
136       tp->n = 0;
137       tp->m = sizeof(tp->mod)/sizeof(tp->mod[0]);
138    }
139    return tp;
140 }
141
142 static tSymTbl *
143 newSymTbl(void)
144 {
145    tSymTbl *tp;
146    if( (tp=malloc(sizeof(*tp))) != NULL ) {
147       tp->n = 0;
148       tp->m = sizeof(tp->sym)/sizeof(tp->sym[0]);
149    }
150    return tp;
151 }
152
153 static tSym *
154 newSym(tSymTbl **ptp,const char *name,unsigned long adr,int mod)
155 {
156    tSym *sp;
157    tSymTbl *tp = *ptp;
158    int n = tp->n;
159    int m = tp->m;
160    if( n >= m ) {
161       tSymTbl *rp;  int l;
162       m = m < SYM2MAX ? m*2 : m+SYMINCR;
163       l = sizeof(*tp)-sizeof(tp->sym)+m*sizeof(tp->sym[0]);
164       rp = (tSymTbl*) realloc(tp,l);
165       if( rp == NULL ) return NULL;
166       tp = rp;
167       tp->m = m;
168       *ptp = tp;
169    }
170    sp = &tp->sym[n++];
171    tp->n = n;
172    sp->name = strdup(name);
173    sp->adr = adr;
174    sp->mod = mod;
175    sp->count = sp->calls = 0;
176    sp->tick = sp->tcalls = 0;
177    return sp;
178 }
179
180 static int
181 newMod(tModTbl **ptp, const char *name, unsigned long offs)
182 {
183    tMod *mp;
184    tModTbl *tp = *ptp;
185    int n = tp->n;
186    int m = tp->m;
187    if( n >= m ) {
188       tModTbl *rp;  int l;
189       m = m < MOD2MAX ? m*2 : m+MODINCR;
190       l = sizeof(*tp)-sizeof(tp->mod)+m*sizeof(tp->mod[0]);
191       rp = (tModTbl*) realloc(tp,l);
192       if( !rp ) return -1;
193       tp = rp;
194       *ptp = tp;
195    }
196    int ret = n++;
197    mp = &tp->mod[ret];
198    tp->n = n;
199    mp->name = strdup(name);
200    mp->offs = offs;
201    mp->count = mp->calls = 0;
202    return ret;
203 }
204
205 int
206 findMod(tModTbl *tp, const char *nm)
207 {
208    int n = tp->n;
209    while( --n >= 0 && strcmp(nm,tp->mod[n].name) );
210    return n;
211 }
212
213 static int
214 newMap(tMapTbl **ptp, const char *name, unsigned long offs)
215 {
216    tMap *mp;
217    tMapTbl *tp = *ptp;
218    int n = tp->n;
219    int m = tp->m;
220    if( n >= m ) {
221       tMapTbl *rp;  int l;
222       m = m < MAP2MAX ? m*2 : m+MAPINCR;
223       l = sizeof(*tp)-sizeof(tp->map)+m*sizeof(tp->map[0]);
224       rp = (tMapTbl*) realloc(tp,l);
225       if( !rp ) return -1;
226       tp = rp;
227       *ptp = tp;
228    }
229    int ret = n++;
230    mp = &tp->map[ret];
231    tp->n = n;
232    mp->name = strdup(name);
233    mp->offs = offs;
234    return ret;
235 }
236
237 static void
238 delMapTbl(tMapTbl **ptp)
239 {
240    int i;  char *name;
241    tMapTbl *tp;  tMap *mp;
242    if( (tp=*ptp) == NULL ) return;
243    mp = &tp->map[0];
244    for( i=tp->n; --i>=0; ++mp ) {
245       if( (name=mp->name) != NULL )
246          free(name);
247    }
248    free(tp);
249    *ptp = NULL;
250 }
251
252 static void
253 delModTbl(tModTbl **ptp)
254 {
255    int i;  char *name;
256    tModTbl *tp;  tMod *mp;
257    if( (tp=*ptp) == NULL ) return;
258    mp = &tp->mod[0];
259    for( i=tp->n; --i>=0; ++mp ) {
260       if( (name=mp->name) != NULL )
261          free(name);
262    }
263    free(tp);
264    *ptp = NULL;
265 }
266
267 static void
268 delSymTbl(tSymTbl **ptp)
269 {
270    int i;  char *name;
271    tSymTbl *tp;  tSym *sp;
272    if( (tp=*ptp) == NULL ) return;
273    sp = &tp->sym[0];
274    for( i=tp->n; --i>=0; ++sp ) {
275       if( (name=sp->name) != NULL )
276          free(name);
277    }
278    free(tp);
279    *ptp = NULL;
280 }
281
282 static void
283 delAllTbls(void)
284 {
285    delMapTbl(&tmap);
286    delModTbl(&tmod);
287    delSymTbl(&tsym);
288 }
289
290 static int
291 mapLkup(tMapTbl *tp,const char *nm)
292 {
293    tMap *mp = &tp->map[0];
294    int n = tp->n;
295    while( --n >= 0 && strcmp(nm,mp->name)!=0 )
296      ++mp;
297    return n;
298 }
299
300 static int
301 findMap(tMapTbl *tp,char *nm)
302 {
303    int ret = mapLkup(tp,nm);
304    if( ret < 0 )
305       dprintf(stderr,"cant find map '%s'\n",nm);
306    return ret;
307 }
308
309 static int
310 findSymAdr(tSymTbl *tp,const char *nm,unsigned long *adr)
311 {
312   int n = tp->n;
313   while( --n >= 0 && strcmp(nm,tp->sym[n].name) );
314   if( n < 0 ) return 1;
315   *adr = tp->sym[n].adr;
316   return 0;
317 }
318
319 static int
320 isSymNm(char *cp, char *bp)
321 {
322    int ch;
323    while( (ch=*cp++) != 0 ) {
324       if( ch == '\n' ) break;
325       *bp++ = ch;
326       if( ch == '#' || ch == '$' || ch == '.' ) return 0;
327    }
328    *bp = 0;
329    return 1;
330 }
331
332 static int
333 hex(char *cp, unsigned long *val)
334 {
335    char *bp = cp;
336    *val = strtoul(cp,&bp,16);
337    return bp - cp;
338 }
339
340 static int
341 mapMod(char *fn,tSymTbl **ptp, int mod)
342 {
343    char nmLine[512], nmCmd[512], *lp;
344    unsigned long adr, ofs;  int typ;
345    FILE *fp;  tMod *mp;
346    if( access(fn, R_OK) < 0 ) return -1;
347    strcpy(&nmCmd[0],smCommand);
348    strcat(&nmCmd[0],fn);
349    lp = NULL;
350    if( (fp=popen(&nmCmd[0],"r")) == NULL ) return -1;
351    mp = &tmod->mod[mod];  ofs = mp->offs;
352    while((lp=fgets(&nmLine[0],sizeof nmLine,fp)) != NULL ) {
353       typ = nmLine[nmType];
354       if( typ != 'T' && typ != 't' ) continue;
355       if( isSymNm(&nmLine[nmName],&nmCmd[0]) == 0 ) continue;
356       if( hex(&nmLine[nmAddr],&adr) != nmType-1 ) continue;
357       adr += ofs;
358       if( newSym(ptp,&nmCmd[0],adr,mod) == NULL ) break;
359    } while((lp=fgets(&nmLine[0],sizeof nmLine,fp)) != NULL );
360    pclose(fp);
361    return lp != NULL ? -1 : 0;
362 }
363
364 static int
365 adrCmpr(const void *a, const void *b)
366 {
367    unsigned long aadr = ((tSym*)a)->adr;
368    unsigned long badr = ((tSym*)b)->adr;
369    if( aadr > badr ) return 1;
370    if( aadr < badr ) return -1;
371    return 0;
372 }
373
374 static int
375 cntCmpr(const void *a, const void *b)
376 {
377    int acnt = ((tSym*)a)->count;
378    int bcnt = ((tSym*)b)->count;
379    return acnt - bcnt;
380 }
381
382 static int
383 tclCmpr(const void *a, const void *b)
384 {
385    int atcl = ((tSym*)a)->tcalls;
386    int btcl = ((tSym*)b)->tcalls;
387    return atcl - btcl;
388 }
389
390 static int
391 modCmpr(const void *a, const void *b)
392 {
393    int acnt = ((tMod*)a)->count;
394    int bcnt = ((tMod*)b)->count;
395    return acnt - bcnt;
396 }
397
398 static int
399 profile_tally(unsigned long pc, int cnt)
400 {
401    tSym *sp, *tp;
402    int l, m, r;
403    if( tsym == NULL )
404       return -1;
405    sp = tp = &tsym->sym[0];
406    l = -1;  r = tsym->n;
407  /* find function containing pc */
408    while( (r-l) > 1 ) {
409       m = (r+l) >> 1;
410       sp = &tp[m];
411       if( sp->adr == pc ) break;
412       if( sp->adr < pc ) l = m;
413       else r = m;
414    }
415    if( sp->adr != pc ) {
416       if( l < 0 )
417          return -1;
418       sp = &tp[l];
419    }
420    tMod *mp = &tmod->mod[sp->mod];
421 /* incr function/module calls/counts */
422    ++sp->calls;
423    ++mp->calls;
424 /* only 1 call per unwinding */
425    if( sp->tick < nTicks ) {
426       sp->tick = nTicks;
427       ++sp->tcalls;
428    }
429    if( cnt != 0 ) {
430       ++sp->count;
431       ++mp->count;
432    }
433    return 0;
434 }
435
436 struct reg_frame {
437    unsigned long rf_fp;        /* frame pointer */
438    unsigned long rf_rtn;       /* return addr */
439 };
440
441 static int readProcMaps(unsigned long *fwa)
442 {
443    int ret, n, m, fd, len;
444    unsigned long vm_start, vm_end, ino;
445    char mr, mw, mx, ms, *bp;
446    off_t pgoff;
447    unsigned int maj, min;
448    char mfn[PATH_MAX], ifn[PATH_MAX];
449    char bfr[65536];
450    pid_t pid = getpid();
451    ret = 1;
452    snprintf(&mfn[0],sizeof mfn,"/proc/%d/maps",pid);
453    fd = open(&mfn[0],O_RDONLY);
454    if( fd >= 0 ) {
455       len = read(fd,bp=&bfr[0],sizeof bfr);
456       close(fd);
457       while( len > 0 ) {
458          m = sscanf(bp, "%lx-%lx %c%c%c%c %lx %x:%x %lu%n",
459             &vm_start,&vm_end,&mr,&mw,&mx,&ms,&pgoff,&maj,&min,&ino,&n);
460          if( m == 10 && fwa ) { *fwa = vm_start;  fwa = 0; }
461          if( m == 10 && mx == 'x' && pgoff == 0 ) {
462             for( bp+=n,len-=n ; len>0 && *bp!='\n' && *bp==' '; ++bp,--len );
463             for( m=0 ; len>0 && *bp!='\n' && m<PATH_MAX-1; ++bp,--len )
464                ifn[m++] = *bp;
465             ifn[m] = 0;
466             if( maj !=0 || min != 0 ) {
467                dprintf(stderr," map "_LX" %s\n",vm_start,&ifn[0]);
468                newMap(&tmap,&ifn[0],vm_start);
469             }
470          }
471          while( --len>=0 && *bp++!='\n' );
472       }
473       ret = 0;
474    }
475    return ret;
476 }
477
478 static int readNmMaps(unsigned long fwa)
479 {
480    unsigned long adr;
481    int i, k;
482    char dfn[PATH_MAX], efn[PATH_MAX], pfn[PATH_MAX];
483    struct link_map *so;
484    struct r_debug *rdebug = &_r_debug;
485    for( so=rdebug->r_map; so!=NULL; so=so->l_next ) {
486       int n = tsym->n;
487       char *nm = &so->l_name[0];
488       if( nm[0] == 0 ) nm = "/proc/self/exe";
489       dprintf(stderr,"resolving %s\n",nm);
490       if( nm[0] == '.' && nm[1] == '/' ) {
491         getcwd(&pfn[0],sizeof(pfn));
492         strcat(&pfn[0],&nm[1]);
493         nm = &pfn[0];
494       }
495       while( (i=readlink(nm,&efn[0],sizeof(efn)-1)) >= 0 ) {
496         efn[i] = 0;
497         dprintf(stderr,"  link %s\n",&efn[0]);
498         if( efn[0] != '/' ) { /* get dirname */
499            for( k=-1,i=0; i<sizeof(dfn)-1 && (dfn[i++]=*nm)!=0; ++nm )
500              if( *nm == '/' ) k = i;
501            if( k >= 0 ) {     /* add link name */
502              for( i=0; k<sizeof(dfn)-1 && (dfn[k++]=efn[i])!=0; ++i );
503            }
504            strncpy(&efn[0],&dfn[0],sizeof(efn));
505         }
506         nm = &efn[0];
507         dprintf(stderr,"  path %s\n",nm);
508       }
509       if( findMod(tmod,nm) >= 0 ) continue;
510       int map = !so->l_name[0] ? -1 : findMap(tmap, nm);
511       tMap *mp = map>=0 ? &tmap->map[map] : 0;
512       adr = mp ? mp->offs : (unsigned long) so->l_addr;
513       if( !adr ) adr = fwa;
514       dprintf(stderr,"  map %s adr = 0x"_LX" ofs = 0x"_LX"\n",
515           nm,adr,(unsigned long)so->l_addr);
516       int mod = newMod(&tmod,nm,adr);
517       if( mod >= 0 ) {
518          if( mapMod(nm,&tsym,mod) < 0 || tsym->n == n ) {
519             char dfn[PATH_MAX];  memset(dfn,0,sizeof(dfn));
520             strcpy(&dfn[0],"/usr/lib/debug");
521             strncat(&dfn[0],nm,sizeof(dfn)-1);
522             strncat(&dfn[0],".debug",sizeof(dfn)-1);
523             dprintf(stderr,", %s",&dfn[0]);
524             if( mapMod(&dfn[0],&tsym,mod) < 0 )
525               mod = -1;
526          }
527       }
528       dprintf(stderr,", %d symbols\n",tsym->n - n);
529       if( mod < 0 ) {
530          fprintf(stderr,"profile - Cannot map module - %s\n",nm);
531 //         exit(1);
532 //         return -1;
533       }
534    }
535
536    /* sort the symbols by address */
537    qsort(&tsym->sym[0],tsym->n,sizeof(tsym->sym[0]),adrCmpr);
538    if( prof_debug != 0 ) {
539       int i; tSym *sp;
540       sp = &tsym->sym[0];
541       for( i=tsym->n; --i>=0; ++sp ) {
542          tMod *mp = &tmod->mod[sp->mod];
543          fprintf(stdout,_LX" %-24s %s\n",sp->adr,
544             &sp->name[0],&mp->name[0]);
545       }
546    }
547    return 0;
548 }
549
550 /** profile_handler(sign,c)
551  * profile periodic timer signal handler.
552  * decodes pc/fp from sigcontext, and tallys pc (count)
553  *  and stack trace (calls).
554  */
555 #define LOW_TEXT 0x01000UL
556
557 static void profile_handler(int n, siginfo_t * info, void *sc)
558 {
559   ucontext_t *uc = (ucontext_t *)sc;
560   struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
561    struct reg_frame *fp, *last_fp;
562    unsigned long pc;
563    /* tid marks top of thread stack (currently) */
564    unsigned long tid = (unsigned long)pthread_self();
565    ++nTicks;
566    /* tally pc value */
567 #ifdef __i386__
568    pc = (unsigned long)c->eip;
569 #define ARCH_OK
570 #endif
571 #ifdef __x86_64__
572    pc = (unsigned long)c->rip;
573 #define ARCH_OK
574 #endif
575 #ifndef ARCH_OK
576 #error unknown arch
577 #endif
578    if( pc < LOW_TEXT ) return;
579    if( profile_tally(pc,1) != 0 ) return;
580
581    /* access frame pointer and validate */
582 #ifdef __i386__
583    fp = (struct reg_frame *)c->ebp;
584    if( (unsigned long)fp < c->esp ) return;
585 #endif
586 #ifdef __x86_64__
587    fp = (struct reg_frame *)c->rbp;
588    if( (unsigned long)fp < c->rsp ) return;
589 #endif
590    if( (unsigned long)fp > prof_stkptr) return;
591    if( (unsigned long)fp > tid) return;
592    dprintf(stderr,"unwind "_LX"",(unsigned long)pc);
593    /* unwind the stack frames, and tally pc values */
594    while( (last_fp=fp) != 0 ) {
595       pc = fp->rf_rtn;
596       dprintf(stderr," "_LX"",(unsigned long)pc);
597       if( pc < LOW_TEXT ) break;
598       if( profile_tally(pc,0) != 0 ) break;
599       fp = (struct reg_frame *)fp->rf_fp;
600       if( ((long)fp & 3) != 0 ) break;
601       if( fp <= last_fp ) break;
602       if( (unsigned long)fp > prof_stkptr ) break;
603       if( (unsigned long)fp > tid ) break;
604    }
605    dprintf(stderr,"\n");
606
607    return;
608 }
609
610 void profileExit(void)
611 {
612    profileStop();
613    profileShow();
614    profileDone();
615 }
616
617 static void profile_segv(int n, siginfo_t * info, void *sc)
618 {
619   ucontext_t *uc = (ucontext_t *)sc;
620   struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
621   profileExit();
622   fprintf(stderr,"segv at 0x"_LX"\n",
623 #if __i386__
624     c->eip
625 #endif
626 #if __x86_64__
627     c->rip
628 #endif
629     );
630 }
631
632 void profileStart(void)
633 {
634    char *fn;
635    int typ, sig;
636    struct itimerval it;
637    struct timezone tz;
638    struct sigaction act;
639    struct sigaction segv;
640    unsigned long fwa;
641
642    if( profileActive != 0 ) return;
643
644    if( tmod == NULL || tsym == NULL || tmap == NULL ) {
645       setbuf(stderr,NULL);
646       dprintf(stderr,"start profile\n");
647       delAllTbls();
648       unsetenv("LD_PRELOAD");
649       profileDone();
650       fn = (char *)&prof_output[0];
651       if( strcmp("-",&fn[0]) == 0 )
652          prof_fp = stdout;
653       else if( strcmp("--",&fn[0]) == 0 )
654          prof_fp = stderr;
655       else
656          prof_fp = fopen(&fn[0],"w");
657       if( prof_fp == NULL ) {
658          perror(&fn[0]);
659          fprintf(stderr,"profile: no output path\n");
660          exit(1);
661       }
662       tmap = newMapTbl();  fwa = 0;
663       if( tmap != NULL && readProcMaps(&fwa) == 0 ) {
664          tmod = newModTbl();  tsym = newSymTbl();
665          if( tmod == NULL || tsym == NULL || readNmMaps(fwa) != 0 ) {
666             fprintf(stderr,"profile: unable to read nm maps\n");
667             delModTbl(&tmod);  delSymTbl(&tsym);
668          }
669       }
670       else {
671          fprintf(stderr,"profile: unable to read proc maps\n");
672          delMapTbl(&tmap);
673       }
674    }
675    if( tmap == NULL || tmod == NULL || tsym == NULL ) {
676       delAllTbls();
677       return;
678    }
679
680    switch( prof_type ) {
681    case 1:  typ = ITIMER_VIRTUAL;  sig = SIGVTALRM;  break;
682    case 2:  typ = ITIMER_REAL;     sig = SIGALRM;    break;
683    default: typ = ITIMER_PROF;     sig = SIGPROF;    break;
684    }
685
686    /* record the start time (cpu/real). */
687    times(&start_cpu);
688    gettimeofday(&start_clk,&tz);
689
690    /* enable the profile timer signal handler. */
691    profileActive = 1;
692    memset(&act,0,sizeof(act));
693    act.sa_sigaction = profile_handler;
694    act.sa_flags = SA_RESTART + SA_SIGINFO;
695    sigaction(sig, &act, &oldSig);
696    memset(&segv,0,sizeof(segv));
697    segv.sa_sigaction = profile_segv;
698    segv.sa_flags = SA_SIGINFO;
699    sigaction(SIGSEGV,&segv,&oldSegv);
700
701    /* start the periodic profile timer signal */
702    if( prof_tmrval == 0 ) prof_tmrval = 1;
703    it.it_value.tv_sec = 
704    it.it_interval.tv_sec = prof_tmrval / 1000000;
705    it.it_value.tv_usec =
706    it.it_interval.tv_usec = prof_tmrval % 1000000;
707    setitimer(typ, &it, NULL);
708    dprintf(stderr,"starting profile.\n");
709 }
710
711 int profileMain(int ac,char **av,char **ev)
712 {
713    int (*fmain)(int ac,char **av,char **ev);
714    unsigned long adr;
715    profileStart();
716    if( findSymAdr(tsym,"main",&adr) != 0 ) {
717      fprintf(stderr,"cant locate sym \"main\"\n");
718      exit(1);
719    }
720    atexit(profileExit);
721    fmain = (int (*)(int,char **,char **))adr;
722    dprintf(stderr,"starting \"main\" at: %p\n",fmain);
723    int ret = fmain(ac,av,ev);
724    dprintf(stderr,"ended \"main\" = %d\n",ret);
725    exit(ret);
726 }
727
728 void profileStop(void)
729 {
730    struct itimerval it;
731    struct timezone tz;
732    struct sigaction act;
733
734    if( profileActive == 0 ) return;
735    dprintf(stderr,"stopping profile.\n");
736
737    /* records the stop time (cpu/real). */
738    times(&stop_cpu);
739    gettimeofday(&stop_clk,&tz);
740
741    /* disables the profile timer signal handler. */
742    it.it_interval.tv_sec = 0;
743    it.it_interval.tv_usec = 0;
744    it.it_value.tv_sec = 0;
745    it.it_value.tv_usec = 0;
746    setitimer(ITIMER_PROF, &it, NULL);
747
748    /* restore the original profile signal handler. */
749    sigaction(SIGPROF, &oldSig, &act);
750    sigaction(SIGSEGV, &oldSegv, &act);
751    profileActive = 0;
752    return;
753 }
754
755
756 void profileClear(void)
757 {
758    int i;
759    nTicks = 0;
760    if( tsym != NULL ) {
761       tSym *sp = &tsym->sym[0];
762       for( i=tsym->n; --i>=0; ++sp ) {
763          sp->count = sp->calls = 0;
764          sp->tick = sp->tcalls = 0;
765       }
766    }
767    if( tmod != NULL ) {
768       tMod *mp = &tmod->mod[0];
769       for( i=tmod->n; --i>=0; ++mp )
770          mp->count = mp->calls = 0;
771    }
772
773
774
775 void profileShow(void)
776 {
777    FILE *fp; int i;
778    double userDt, systDt;
779    double tickDt, realDt;
780    if( tsym == NULL || tmod == NULL ) return;
781    fp = prof_fp;
782    qsort(&tsym->sym[0],tsym->n,sizeof(tsym->sym[0]),cntCmpr);
783    /* outputs sorted (by count) list of functions called. */
784    fprintf(fp,"---- profile start ----\n");
785    fprintf(fp," %d ticks %d modules %d syms\n",nTicks,tmod->n,tsym->n);
786    tSym *sp = &tsym->sym[0];
787    for( i=tsym->n; --i>=0; ++sp ) {
788       if( !sp->count ) continue;
789       tMod *mp = &tmod->mod[sp->mod];
790       fprintf(fp,"%8.3fs %5.1f%% %-24s %s\n",
791         prof_tmrval*sp->count/1000000.0,
792         sp->count*100.0/nTicks,&sp->name[0],
793         &mp->name[0]);
794    }
795
796    qsort(&tsym->sym[0],tsym->n,sizeof(tsym->sym[0]),tclCmpr);
797    /* outputs sorted (by calls) list of functions called. */
798    fprintf(fp,"---- profile calls ----\n");
799    sp = &tsym->sym[0];
800    for( i=tsym->n; --i>=0; ++sp ) {
801       if( !sp->tcalls ) continue;
802       tMod *mp = &tmod->mod[sp->mod];
803       fprintf(fp,"%8.3fs %5.1f%% %-24s %5.1f %s\n",
804         prof_tmrval*sp->tcalls/1000000.0,
805         sp->tcalls*100.0/nTicks,&sp->name[0],
806         (double)sp->calls/sp->tcalls,&mp->name[0]);
807    }
808
809    realDt = ((stop_clk.tv_sec - start_clk.tv_sec) * 1000000.0 +
810                (stop_clk.tv_usec - start_clk.tv_usec) ) / 1000000.0;
811    /* output sorted (by count) list of modules called. */
812    qsort(&tmod->mod[0],tmod->n,sizeof(tmod->mod[0]),modCmpr);
813    fprintf(fp,"----\n");
814    tMod *mp = &tmod->mod[0];
815    for( i=tmod->n; --i>=0; ++mp ) {
816       if( mp->count != 0 && mp->name != NULL ) {
817          double dt = prof_tmrval*mp->count/1000000.0;
818          fprintf(fp,"%8.3fs %5.1f%%/%5.1f%% %s\n",
819             dt,mp->count*100.0/nTicks,dt*100.0/realDt,mp->name);
820       }
821    }
822
823    /* output tick time, cpu user/kernal time, real time. */
824    tickDt = prof_tmrval*nTicks / 1000000.0;
825    userDt = (stop_cpu.tms_utime - start_cpu.tms_utime) / (double)CLOCKS_PER_SEC;
826    systDt = (stop_cpu.tms_stime - start_cpu.tms_stime) / (double)CLOCKS_PER_SEC;
827
828    fprintf(fp,"%8.3ft %0.3fu+%0.3fs %0.3fr %5.1f%%\n",
829       tickDt, userDt, systDt, realDt, tickDt*100.0/realDt);
830    fprintf(fp,"---- profile end ----\n\n");
831    qsort(&tsym->sym[0],tsym->n,sizeof(tsym->sym[0]),adrCmpr);
832 }
833
834 void profileDone(void)
835 {
836    profileStop();
837    delModTbl(&tmod);
838    delSymTbl(&tsym);
839    if( prof_fp != NULL && prof_fp != stdout && prof_fp != stderr ) {
840       fclose(prof_fp);  prof_fp = NULL;
841    }
842 }
843