35af38102da3c2d2f427c5d18568db45ddd3f755
[goodguy/cinelerra.git] / prof.c
1 /* prof.c - initialize sampling profiler
2     load user executable in ptrace mode
3     at exec catchpoint, set breakpoint at main
4     add LD_PRELOAD = libprofile
5     run ld-linux to map libraries, stop at main
6     add ptr to main as argument, start profileStart
7 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <signal.h>
12 #include <malloc.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <limits.h>
16 #include <link.h>
17 #include <errno.h>
18 #include <sys/ptrace.h>
19 #include <sys/user.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include "sys.h"
23
24 #define unlikely(x) __builtin_expect(!!(x), 0)
25 #define dprintf(s...) do { if( unlikely(debug!=0) ) fprintf(s); } while(0)
26
27 #ifdef __i386__
28 #define nmAddr 0
29 #define nmType 9
30 #define nmName 11
31 #define _LX "%08lx"
32 #ifndef LIB
33 #define LIB "/lib/"
34 #endif
35 #endif
36 #ifdef __x86_64__
37 #define nmAddr 0
38 #define nmType 17
39 #define nmName 19
40 #define _LX "%016lx"
41 #ifndef LIB
42 #define LIB "/lib64/"
43 #endif
44 #endif
45
46 #define SYM2MAX 1024*256
47 #define SYMINCR 1024
48
49 #define DEBUG 0
50 #define PROF_DEBUG 0
51 #define PROF_OUTPUT "--"
52 #define PROF_TMR_TYPE 0
53 #define PROF_TMR_USEC 10000
54
55 static int debug = DEBUG;
56 static int prof_debug = PROF_DEBUG;
57 static int prof_type = PROF_TMR_TYPE;
58 static int prof_tmrval = PROF_TMR_USEC;
59 static long prof_stkptr = 0;
60 static char *prof_output = NULL;
61 static const int max_signals = sizeof(signal_name)/sizeof(signal_name[0]);
62 static const int max_sysreqs = sizeof(sysreq_name)/sizeof(sysreq_name[0]);
63
64 #define LIBPROFILE_NAME "libprofile.so"
65 #define LIBPROFILE_PATH LIB LIBPROFILE_NAME
66
67 #ifdef __i386__
68 #define LD_LINUX_SO LIB "ld-linux.so.2"
69 #endif
70 #ifdef __x86_64__
71 #define LD_LINUX_SO LIB "ld-linux-x86-64.so.2"
72 #endif
73
74 char *libprofile_path = NULL;
75
76 typedef struct {
77    pid_t pid;
78    unsigned long addr;
79    long orig_value;
80    int enabled;
81 } breakpoint;
82
83 typedef struct {
84   char *nm;
85   unsigned long adr;
86 } tSym;
87
88 typedef struct {
89   int n, m;
90   tSym sym[SYMINCR];
91 } tSymTbl;
92
93 static tSymTbl *
94 newSymTbl(void)
95 {
96    tSymTbl *tp;
97    if( (tp=malloc(sizeof(*tp))) != NULL ) {
98       tp->n = 0;
99       tp->m = sizeof(tp->sym)/sizeof(tp->sym[0]);
100    }
101    return tp;
102 }
103
104 static int
105 symCmpr(const void *a, const void *b)
106 {
107    const char *anm = ((tSym const*)a)->nm;
108    const char *bnm = ((tSym const*)b)->nm;
109    return strcmp(anm,bnm);
110 }
111
112 static int
113 newSym(tSymTbl **ptp, const char *nm, unsigned long adr)
114 {
115    tSym *sp;
116    tSymTbl *tp = *ptp;
117    int n = tp->n;
118    int m = tp->m;
119    if( n >= m ) {
120       tSymTbl *rp;  int l;
121       m = m < SYM2MAX ? m*2 : m+SYMINCR;
122       l = sizeof(*tp)-sizeof(tp->sym)+m*sizeof(tp->sym[0]);
123       rp = (tSymTbl*) realloc(tp,l);
124       if( rp == NULL ) return -1;
125       tp = rp;
126       tp->m = m;
127       *ptp = tp;
128    }
129    sp = &tp->sym[n++];
130    tp->n = n;
131    sp->nm = strdup(nm);
132    sp->adr = adr;
133    return 0;
134 }
135 static void
136 delSymTbl(tSymTbl **ptp)
137 {
138    int i;  char *nm;
139    tSymTbl *tp = *ptp;
140    tSym *sp = &tp->sym[0];
141    for( i=tp->n; --i>=0; ++sp ) {
142       if( (nm=sp->nm) != NULL )
143          free(nm);
144    }
145    free(tp);
146    *ptp = NULL;
147 }
148
149 static int
150 isSymNm(char *cp, char *bp)
151 {
152    if( !*cp || *cp=='\n' ) return 0;
153    int ch;
154    while( (ch=*cp++) != 0 ) {
155       if( ch == '\n' ) break;
156       *bp++ = ch;
157       if( ch == '#' || ch == '$' || ch == '.' ) return 0;
158    }
159    *bp = 0;
160    return 1;
161 }
162
163 static int
164 mapObj(char *fn,tSymTbl **ptp,const char *typs)
165 {
166    char nmLine[512], nmCmd[512], *lp, *rp;
167    unsigned long adr;
168    FILE *fp;
169    if( access(fn,R_OK) < 0 ) return -1;
170    strcpy(&nmCmd[0],"smap 2> /dev/null < ");
171    strcat(&nmCmd[0],fn);
172    if( (fp=popen(&nmCmd[0],"r")) == NULL ) return -1;
173    while((lp=fgets(&nmLine[0],sizeof nmLine,fp)) != NULL ) {
174       int t = nmLine[nmType];
175       if( t != 'T' && t != 't' && (!typs || !strchr(typs,t)) ) continue;
176       if( isSymNm(&nmLine[nmName],&nmCmd[0]) == 0 ) continue;
177       adr = strtoul(&nmLine[nmAddr],&rp,16);
178       if( (rp-&nmLine[nmAddr]) != nmType-1 ) continue;
179       if( newSym(ptp,&nmCmd[0],adr) != 0 ) break;
180    }
181    pclose(fp);
182    return lp != NULL ? -1 : 0;
183 }
184
185
186 static tSymTbl *
187 mapFile(char *fn,const char *typs)
188 {
189    tSymTbl *tp = newSymTbl();
190    if( tp && mapObj(fn,&tp,typs) != 0 )
191      delSymTbl(&tp);
192    if( tp )
193      qsort(&tp->sym,tp->n,sizeof(tp->sym[0]),symCmpr);
194    return tp;
195 }
196
197 static tSym *
198 symLkup(tSymTbl *tp,const char *nm)
199 {
200    int l, m, n, r;
201    tSym *mp = NULL;
202    tSym *sp = &tp->sym[0];
203    l = n = -1;  r = tp->n;
204    while( (r-l) > 1 ) {
205       m = (r+l) >> 1;
206       mp = &sp[m];
207       n = strcmp(mp->nm,nm);
208       if( n == 0 ) break;
209       if( n < 0 ) l = m;
210       else r = m;
211    }
212    return n == 0 ? mp : NULL;
213 }
214
215 static void
216 findSym(tSymTbl *tp,char *nm,tSym **psp)
217 {
218    tSym *sp = symLkup(tp,nm);
219    if( sp == NULL ) {
220       fprintf(stderr,"cant find symbol '%s'\n",nm);
221       exit(1);
222    }
223    *psp = sp;
224 }
225
226 int
227 execute_program(char *fn,char **argv)
228 {
229    pid_t pid = fork();
230    if( pid < 0 ) {
231       perror("fork");
232       return -1;
233    }
234    if( pid != 0 )
235       return pid;
236    if (ptrace(PTRACE_TRACEME, 0, 1, 0)<0) {
237       perror("PTRACE_TRACEME");
238       exit(1);
239    }
240    setenv("LD_PRELOAD",libprofile_path,1);
241    //setenv("LD_DEBUG","files",1);
242    execvp(fn,argv);
243    fprintf(stderr, "Can't execute `%s': %s\n", fn, strerror(errno));
244    exit(1);
245 }
246
247 #ifdef __i386__
248 static long
249 peek_text(pid_t pid,long addr)
250 {
251    return ptrace(PTRACE_PEEKTEXT,pid,addr,0);
252 }
253
254 static void
255 poke_text(pid_t pid,long addr,long value)
256 {
257    ptrace(PTRACE_POKETEXT,pid,addr,value);
258 }
259
260 static long
261 get_eip(pid_t pid)
262 {
263    struct user_regs_struct regs;
264    ptrace(PTRACE_GETREGS,pid,0,&regs);
265    return regs.eip;
266 }
267
268 static void
269 set_eip(pid_t pid,long addr)
270 {
271    struct user_regs_struct regs;
272    ptrace(PTRACE_GETREGS,pid,0,&regs);
273    regs.eip = addr;
274    ptrace(PTRACE_SETREGS,pid,0,&regs);
275 }
276
277 static long
278 get_sysreq(pid_t pid)
279 {
280    struct user_regs_struct regs;
281    ptrace(PTRACE_GETREGS,pid,0,&regs);
282    return regs.orig_eax;
283 }
284
285 static long
286 get_sysret(pid_t pid)
287 {
288    struct user_regs_struct regs;
289    ptrace(PTRACE_GETREGS,pid,0,&regs);
290    return regs.eax;
291 }
292
293 static long
294 get_esp(pid_t pid)
295 {
296    struct user_regs_struct regs;
297    ptrace(PTRACE_GETREGS,pid,0,&regs);
298    return regs.esp;
299 }
300
301 static long
302 peek_stk(pid_t pid,long addr)
303 {
304    return ptrace(PTRACE_PEEKDATA,pid,addr,0);
305 }
306
307 static void
308 poke_stk(pid_t pid,long addr,long value)
309 {
310    ptrace(PTRACE_POKEDATA,pid,addr,value);
311 }
312
313 #endif
314
315 #ifdef __x86_64__
316 static long
317 peek_text(pid_t pid,long addr)
318 {
319    long ret = ptrace(PTRACE_PEEKTEXT,pid,addr,0);
320    return ret;
321 }
322
323 static void
324 poke_text(pid_t pid,long addr,long value)
325 {
326    ptrace(PTRACE_POKETEXT,pid,addr,value);
327 }
328
329 static long
330 get_eip(pid_t pid)
331 {
332    struct user_regs_struct regs;
333    ptrace(PTRACE_GETREGS,pid,0,&regs);
334    return regs.rip;
335 }
336
337 static void
338 set_eip(pid_t pid,long addr)
339 {
340    struct user_regs_struct regs;
341    ptrace(PTRACE_GETREGS,pid,0,&regs);
342    regs.rip = addr;
343    ptrace(PTRACE_SETREGS,pid,0,&regs);
344 }
345
346 static long
347 get_sysreq(pid_t pid)
348 {
349    struct user_regs_struct regs;
350    ptrace(PTRACE_GETREGS,pid,0,&regs);
351    return regs.orig_rax;
352 }
353
354 static long
355 get_sysret(pid_t pid)
356 {
357    struct user_regs_struct regs;
358    ptrace(PTRACE_GETREGS,pid,0,&regs);
359    return regs.rax;
360 }
361
362 static long
363 get_esp(pid_t pid)
364 {
365    struct user_regs_struct regs;
366    ptrace(PTRACE_GETREGS,pid,0,&regs);
367    return regs.rsp;
368 }
369
370 static long
371 peek_stk(pid_t pid,long addr)
372 {
373    errno = 0;
374    long ret = ptrace(PTRACE_PEEKDATA,pid,addr,0);
375    if( ret == -1 && errno )
376       dprintf(stderr,"peek_stk failed @ "_LX" = %d:%s",
377          addr, errno, strerror(errno));
378    return ret;
379 }
380
381 static void
382 poke_stk(pid_t pid,long addr,long value)
383 {
384    ptrace(PTRACE_POKEDATA,pid,addr,value);
385 }
386 #endif
387
388 static int
389 pstrcmp(pid_t pid,long addr,char *cp)
390 {
391    int n, v;  long w, adr;
392    adr = addr & ~(sizeof(w)-1);
393    n = addr & (sizeof(w)-1);
394    w = peek_stk(pid,adr);
395    adr += sizeof(long);
396    w >>= n*8;
397    n = sizeof(w) - n;
398    for(;;) {
399       if( (v=(w&0xff)-*cp) != 0 ) break;
400       if( *cp == 0 ) break;
401       if( --n <= 0 ) {
402          w = peek_stk(pid,adr);
403          adr += sizeof(w);
404          n = sizeof(w);
405       }
406       else
407          w >>= 8;
408       ++cp;
409    }
410    return v;
411 }
412
413 static char *
414 pstrcpy(pid_t pid,long addr,char *cp)
415 {
416    long w, by;
417    unsigned char *bp = (unsigned char *)cp;
418    long adr = addr & ~(sizeof(w)-1);
419    int n = 8*(addr & (sizeof(w)-1));
420    w = peek_stk(pid,adr);
421    do {
422       by = *bp++;
423       w &= ~(0xffUL << n);
424       w |= by << n;
425       if( (n+=8) >= sizeof(w)*8 ) {
426          poke_stk(pid,adr,w);
427          adr += sizeof(w);
428          w = peek_stk(pid,adr);
429          n = 0;
430       }
431    } while( by );
432    if( n )
433      poke_stk(pid,adr,w);
434    return cp;
435 }
436
437 /* instruction = int 3 */
438 #define BREAKPOINT_VALUE 0xcc
439 #define BREAKPOINT_LENGTH 1
440 #define DECR_PC_AFTER_BREAK 1
441
442 void
443 enable_breakpoint(breakpoint *bp)
444 {
445    if( bp->enabled == 0 ) {
446       int shft;  long word, mask;
447       pid_t pid = bp->pid;
448       unsigned long addr = bp->addr & ~(sizeof(long)-1);
449       word = peek_text(pid,addr);
450       bp->orig_value = word;
451       shft = 8 * (bp->addr&(sizeof(long)-1));
452       mask = 0xffUL << shft;
453       word = (word & ~mask) | ((unsigned long)BREAKPOINT_VALUE << shft);
454       poke_text(pid,addr,word);
455       bp->enabled = 1;
456       dprintf(stderr,"set bp "_LX"\n",bp->addr);
457    }
458 }
459
460 void
461 disable_breakpoint(breakpoint *bp)
462 {
463    if( bp->enabled != 0 ) {
464       int shft;  long word, mask;
465       pid_t pid = bp->pid;
466       unsigned long addr = bp->addr & ~(sizeof(long)-1);
467       word = peek_text(pid,addr);
468       shft = 8 * (bp->addr&(sizeof(long)-1));
469       mask = 0xffUL << shft;
470       word = (word & ~mask) | (bp->orig_value & mask);
471       poke_text(pid,addr,word);
472       bp->enabled = 0;
473    }
474 }
475
476 breakpoint *newBrkpt(pid_t pid,unsigned long addr)
477 {
478    breakpoint *bp;
479    bp = (breakpoint *)malloc(sizeof(*bp));
480    memset(bp,0,sizeof(*bp));
481    bp->pid = pid;
482    bp->addr = addr;
483    return bp;
484 }
485
486 static long
487 get_map_fwa(pid_t pid,char *pfx1, char *pfx2)
488 {
489    FILE *fp; int n, len1, len2;
490    long fwa, lwa1, pgoff;
491    unsigned long inode;
492    int major, minor;
493    char f1, f2, f3, f4, path[512], bfr[512];
494    sprintf(&bfr[0],"/proc/%d/maps",(int)pid);
495    if( (fp=fopen(&bfr[0],"r")) == NULL ) {
496       perror(&bfr[0]);  exit(1);
497    }
498    len1 = pfx1 ? strlen(pfx1) : 0;
499    len2 = pfx2 ? strlen(pfx2) : 0;
500    for(;;) {
501       if( fgets(&bfr[0],sizeof(bfr),fp) == NULL ) {
502          fprintf(stderr,"cant find process %d '%s' maps\n",(int)pid,pfx1);
503          exit(1);
504       }
505       n = sscanf(&bfr[0],"%lx-%lx %c%c%c%c %lx %x:%x %lu %s",
506             &fwa,&lwa1,&f1,&f2,&f3,&f4,&pgoff,&major,&minor,&inode,&path[0]); 
507       if( n != 11 ) continue;
508       // if( f3 != 'x' ) continue;
509       if( !pfx1 && !pfx2 ) break;
510       if( pfx1 && strncmp(pfx1,&path[0],len1) == 0 ) break;
511       if( pfx2 && strncmp(pfx2,&path[0],len2) == 0 ) break;
512    }
513    fclose(fp);
514    return fwa;
515 }
516 #ifdef __i386__
517 #define CHILD(v) (peek_stk(pid,(long)(v)))
518 #define ACHILD(v) CHILD(v)
519 #endif
520 #ifdef __x86_64__
521 #define CHILD(v) (peek_stk(pid,(long)(v)&0x7fffffffffffL))
522 #define ACHILD(v) (CHILD(v)&0x7fffffffffffL)
523 #endif
524 #define VCHILD(v) ((void*)CHILD(v))
525
526 static void
527 set_parm(pid_t pid,tSymTbl *tprof,long ofs,char *nm,long val)
528 {
529    tSym *var;
530    dprintf(stderr,"set %s = %#lx\n",nm,val);
531    findSym(tprof,nm,&var);
532    var->adr += ofs;
533    poke_stk(pid,var->adr,val);
534 }
535
536 static void
537 usage(char *av0)
538 {
539    fprintf(stderr,"usage: %s [-o path] [-d] [-e] [-p libpath] [-012] [-u #] cmd args...\n",av0);
540    fprintf(stderr,"  -o  profile output pathname, -=stdout, --=stderr\n");
541    fprintf(stderr,"  -d  debug output enabled\n");
542    fprintf(stderr,"  -e  child debug output enabled\n");
543    fprintf(stderr,"  -p  specify path for libprofile.so\n");
544    fprintf(stderr,"  -0  usr+sys cpu timer intervals (sigprof)\n");
545    fprintf(stderr,"  -1  usr only cpu timer intervals (sigvtalrm)\n");
546    fprintf(stderr,"  -2  real time timer intervals (sigalrm)\n");
547    fprintf(stderr,"  -u  profile timer interval in usecs\n");
548 }
549
550 long run_to_breakpoint(pid_t ppid, int init_syscall)
551 {
552    int in_syscall = init_syscall;
553    if( in_syscall < 0 )
554      ptrace(PTRACE_SYSCALL,ppid,0,0);
555    for(;;) {
556       int status;
557       pid_t pid = wait(&status);    /* wait for ptrace status */
558       if( ppid != pid ) continue;
559       if( WIFSTOPPED(status) ) {
560          int stopsig = WSTOPSIG(status);
561          if( stopsig == SIGTRAP ) {
562             long eip = get_eip(pid);
563             int sysnum = get_sysreq(pid);
564             if( in_syscall < 0 ) {  /* starting syscall */
565                if( sysnum < 0 ) {   /* hit breakpoint */
566                   dprintf(stderr,"hit bp at 0x"_LX"\n",eip);
567                   return eip;
568                }
569                dprintf(stderr,"syscall %3d "_LX" - %s",sysnum,eip,
570                  sysnum >= max_sysreqs ? "unknown" : sysreq_name[sysnum]);
571                in_syscall = sysnum;
572             }
573             else {     /* finishing syscall */
574                long ret = get_sysret(pid);
575                if( in_syscall != sysnum )
576                   dprintf(stderr,"(%d!)",sysnum);
577                dprintf(stderr," = %#lx\n",ret);
578                if( init_syscall >= 0 )
579                  return eip;
580                in_syscall = -1;
581             }
582          }
583          /* resume execution */
584          ptrace(PTRACE_SYSCALL,pid,0,0);
585       }
586       else {      /* unexepected status returned */
587          if( WIFEXITED(status) ) {
588             int exit_code = WEXITSTATUS(status);
589             fprintf(stderr,"exit %d\n",exit_code);
590          }
591          else if( WIFSIGNALED(status) ) {
592             int signum = WTERMSIG(status);
593             fprintf(stderr,"killed %d - %s\n", signum,
594                signum < 0 || signum >= max_signals ?
595                  "unknown" : signal_name[signum]);
596          }
597          else {
598             fprintf(stderr,"unknown status %d\n",status);
599          }
600          exit(1);
601       }
602    }
603 }
604
605 static void
606 setup_profile(pid_t pid,long r_debug,long bp_main)
607 {
608    tSymTbl *tprof;
609    tSym *sym;
610    struct link_map *so;
611    long absOffset = get_map_fwa(pid, LIB "ld-", "/usr" LIB "ld-");
612    struct r_debug *rdebug = (struct r_debug *)(r_debug+absOffset);
613    /* look through maps - find libprofile image, save absOffset */
614    dprintf(stderr,"rdebug "_LX"\n",(long)rdebug);
615    for( so=VCHILD(&rdebug->r_map); so!=NULL; so=VCHILD(&so->l_next) ) {
616       absOffset = CHILD(&so->l_addr);
617       if( absOffset == 0 ) continue;
618       if( pstrcmp(pid,ACHILD(&so->l_name),libprofile_path) == 0 ) break;
619    }
620    if( so == NULL ) {
621       fprintf(stderr,"%s did not preload\n",libprofile_path);
622       exit(1);
623    }
624    dprintf(stderr,"libprofile "_LX"\n",absOffset);
625    /* map libprofile with nm */
626    tprof = mapFile(libprofile_path,"bd");
627    prof_stkptr = get_esp(pid);
628    dprintf(stderr,"orig esp "_LX"\n",prof_stkptr);
629    set_parm(pid,tprof,absOffset,"prof_stkptr",prof_stkptr);
630    set_parm(pid,tprof,absOffset,"prof_debug",prof_debug);
631    set_parm(pid,tprof,absOffset,"prof_type",prof_type);
632    set_parm(pid,tprof,absOffset,"prof_tmrval",prof_tmrval);
633    findSym(tprof,"prof_output",&sym);
634    sym->adr += absOffset;
635    pstrcpy(pid,sym->adr,&prof_output[0]);
636    findSym(tprof,"profileMain",&sym);
637    sym->adr += absOffset;
638    dprintf(stderr,"set eip = %#lx\n",sym->adr);
639    /* resume execution in profileStart */
640    set_eip(pid,sym->adr);
641 }
642
643 int main(int ac, char **av)
644 {
645    char *cp;
646    tSymTbl *tpath, *tld_linux;
647    tSym *sym_main, *sym__r_debug;
648    int pid, status, execve;
649    breakpoint *main_bp = NULL;
650    char *lib_path = NULL, *lib_argv = NULL;
651
652
653    /* decode cmdline params */
654    while( ac > 1 && *(cp=av[1]) == '-' ) {
655       switch( cp[1] ) {
656       case 'd':  /* enable debug output */
657          debug = 1;
658          break;
659       case 'e':  /* enable child debug output */
660          prof_debug = 1;
661          break;
662       case '0':  /* use sigprof */
663          prof_type = 0;
664          break;
665       case '1':  /* use sigvtalrm */
666          prof_type = 1;
667          break;
668       case '2':  /* use sigalrm */
669          prof_type = 2;
670          break;
671       case 'u':  /* use timer interval usec */
672          prof_tmrval = strtoul(av[2],NULL,0);
673          --ac;  ++av;
674          break;
675       case 'o':  /* specify output path */
676          prof_output = av[2];
677          --ac;  ++av;
678          break;
679       case 'p':  /* specify libprofile */
680          lib_argv = av[2];
681          --ac;  ++av;
682          break;
683       default:
684          fprintf(stderr,"unknown parameter - '%s'\n",cp);
685       case 'h':  /* help */
686          ac = 0;
687          break;
688       }
689       --ac;  ++av;
690    }
691
692    if( ac < 2 ) {
693       usage(av[0]);
694       exit(1);
695    }
696
697    if( lib_argv != NULL ) {
698       if( access(&lib_argv[0],R_OK) == 0 )
699          lib_path = strdup(lib_argv);
700    }
701    if( lib_path == NULL ) {
702       cp = "./" LIBPROFILE_NAME;
703       if( access(cp,R_OK) == 0 )
704          lib_path = strdup(cp);
705    }
706    if( lib_path == NULL && (cp=getenv("HOME")) != NULL ) {
707       char libpath[PATH_MAX];
708       snprintf(&libpath[0],sizeof(libpath),"%s/bin/%s",
709                cp,LIBPROFILE_NAME);
710       if( access(&libpath[0],R_OK) == 0 )
711          lib_path = strdup(&libpath[0]);
712    }
713    if( lib_path == NULL ) {
714       if( access(cp=LIBPROFILE_PATH,R_OK) == 0 )
715          lib_path = strdup(cp);
716    }
717    if( lib_path == NULL ) {
718       fprintf(stderr,"cant find %s\n",LIBPROFILE_NAME);
719       exit(1);
720    }
721    libprofile_path = lib_path;
722
723    if( prof_output == NULL )
724       prof_output = PROF_OUTPUT;
725
726    /* read nm output for executable */
727    tpath = mapFile(av[1],0);
728    if( tpath == NULL ) {
729       fprintf(stderr,"cant map %s\n",av[1]);
730       exit(1);
731    }
732    /* read nm output for ld-linux */
733    tld_linux = mapFile(LD_LINUX_SO,"BD");
734    if( tld_linux == NULL ) {
735       fprintf(stderr,"cant map %s\n",LD_LINUX_SO);
736       exit(1);
737    }
738
739    /* lookup main, _r_debug */
740    findSym(tpath,"main",&sym_main);
741    findSym(tld_linux,"_r_debug",&sym__r_debug);
742    /* fork child with executable */
743    pid = execute_program(av[1],&av[1]);
744    if( pid < 0 ) {
745       perror(av[1]);
746       exit(1);
747    }
748
749    execve = sizeof(sysreq_name)/sizeof(sysreq_name[0]);
750    while( --execve >= 0 && strcmp("execve",sysreq_name[execve]) );
751    if( execve < 0 ) {
752       fprintf(stderr,"cant find execve sysreq\n");
753       exit(1);
754    }
755    main_bp = newBrkpt(pid,sym_main->adr);
756    run_to_breakpoint(pid, execve);
757    /* add offset of main module */
758    main_bp->addr += get_map_fwa(pid,0,0);
759    enable_breakpoint(main_bp);
760    run_to_breakpoint(pid, -1);
761    /* hit breakpoint at 'main', setup profiler */
762    setup_profile(pid,sym__r_debug->adr,main_bp->addr);
763    disable_breakpoint(main_bp);
764    ptrace(PTRACE_DETACH,pid,0,0);  /* turn off ptrace */
765    pid = wait(&status);            /* wait for child status */
766    if( WIFEXITED(status) ) {
767       status = WEXITSTATUS(status);
768       dprintf(stderr,"exit %d\n",status);
769    }
770    else if( WIFSIGNALED(status) ) {
771       int signum = WTERMSIG(status);
772       fprintf(stderr,"killed %d - %s\n", signum,
773          signum < 0 || signum >= max_signals ?
774             "unknown" : signal_name[signum]);
775    }
776    else {
777       fprintf(stderr,"unknown status %d\n",status);
778       status = 1;
779    }
780    return status;
781 }
782