From: Good Guy Date: Wed, 3 Jul 2019 17:11:05 +0000 (-0600) Subject: add prof2 profiler X-Git-Tag: 2019-08~24 X-Git-Url: https://git.cinelerra-gg.org/git/?a=commitdiff_plain;h=c053918502e6ec08433a0650ce37af35431f15b7;p=goodguy%2Fcinelerra.git add prof2 profiler --- diff --git a/cinelerra-5.1/cinelerra/performanceprefs.C b/cinelerra-5.1/cinelerra/performanceprefs.C index c00ca732..dbeee004 100644 --- a/cinelerra-5.1/cinelerra/performanceprefs.C +++ b/cinelerra-5.1/cinelerra/performanceprefs.C @@ -332,8 +332,8 @@ PrefsUseHWDev::PrefsUseHWDev(PreferencesWindow *pwindow, void PrefsUseHWDev::create_objects() { BC_PopupTextBox::create_objects(); - set_tooltip(_("vdpau - Nvidia, Nouveau, Amdgpu\n" - "vaapi - Broadcom, Intel HD graphics, Radeon\n" + set_tooltip(_("vdpau - Nvidia, Nouveau\n" + "vaapi - Broadcom, Intel HD graphics, Amdgup, Radeon\n" "cuda - Nvidia + Cuda SDK")); hw_dev_names.append(new BC_ListBoxItem(_("none"))); #ifdef HAVE_VAAPI diff --git a/cinelerra-5.1/plugins/histeq/.gdb_history b/cinelerra-5.1/plugins/histeq/.gdb_history deleted file mode 100644 index ee24cbc7..00000000 --- a/cinelerra-5.1/plugins/histeq/.gdb_history +++ /dev/null @@ -1,23 +0,0 @@ -p /x 0xff*3 -p /x 0xff*3 * 0x55 -p /x 0xff*3 * 0x57 -p /x 0xff*3 * 0x56 -p /x 0x5555*3 -p /x 0xff*3 * 0x5555 -p /x 0xff*3 * 0x5555 /257 -p /x 0xff*3 * 0x5555 /256 -p /x 0xff*3 * 0x5555 /254 -p /x 0xff*3 * 0x5555 /253 -p /x 0xff*3 * 0x5555 /254 -p /x 0xff*3 * 0x5555 /255 -p (766*0x5555)/255 -p /x (766*0x5555)/255 -p /x (0x2fd*0x5555)/255 -0xffff/0x5555 -p (double)0xffff/0x5555 -r -y -r -y -r -q diff --git a/cinelerra-5.1/prof2/Makefile b/cinelerra-5.1/prof2/Makefile new file mode 100644 index 00000000..b7e02473 --- /dev/null +++ b/cinelerra-5.1/prof2/Makefile @@ -0,0 +1,47 @@ +TARGETS = libprofile.so prof smap +BITS=64 +#CFLAGS += -O +CFLAGS += -g -Wall +BIN_PATH ?= /usr/bin +LIB_PATH ?= $(dir $(lastword $(wildcard /lib*/ld-*.so /lib*/*-linux-gnu/ld-*.so))) +$(if $(LIB_PATH),,$(error cant find ld-.so)) +CFLAGS += -DLIB='"$(LIB_PATH)"' +ASM_PATH ?= $(dir $(lastword $(wildcard /usr/include/asm/signal.h /usr/include/x86_64-linux-gnu/asm/signal.h))) + +.PHONY: all +all: $(TARGETS) + +.PHONY: install +install: + cp -a libprofile.so $(DESTDIR)$(LIB_PATH)/. + cp -a prof smap $(DESTDIR)$(BIN_PATH)/. + +.PHONY: uninstall +uninstall: + rm -f $(DESTDIR)$(LIB_PATH)/libprofile.so + rm -f $(DESTDIR)$(BIN_PATH)/prof + rm -f $(DESTDIR)$(BIN_PATH)/smap + +libprofile.so: profile.c + $(CC) $(CFLAGS) -fPIC -shared -o $@ $< + +prof: prof.c sys.h + $(CC) $(CFLAGS) -o $@ $< + +smap: smap.C + $(CXX) $(CFLAGS) -o $@ $< \ + -Wall -I/usr/include/libiberty -llzma -liberty + +tst: tst.c + +sys.h: $(ASM_PATH/signal.h $(ASM_PATH)/unistd.h + echo "char *sysreq_name[] = {" > sys.h + ./mksyscallent < $(ASM_PATH)/unistd_$(BITS).h >> sys.h + echo "};" >> sys.h + echo "char *signal_name[] = {" >> sys.h + ./mksignalent < $(ASM_PATH)/signal.h >> sys.h + echo "};" >> sys.h + +.PHONY: clean +clean: + rm -f $(TARGETS) tst sys.h diff --git a/cinelerra-5.1/prof2/mksignalent b/cinelerra-5.1/prof2/mksignalent new file mode 100755 index 00000000..316d81f0 --- /dev/null +++ b/cinelerra-5.1/prof2/mksignalent @@ -0,0 +1,33 @@ +#!/usr/bin/awk -f + +# hack expression to generate arch/signalent.h from +# It reads from stdin and writes to stdout + +BEGIN { + max=0; +} + +{ + if (($1 ~ /^#define$/) && (!SIGNAL[$3]) && ($2 ~ /^SIG[A-Z]/) \ + && ($2 !~ /^SIGRTMIN$/) && ($2 !~ /^SIGRTMAX$/) && ($2 !~ /^SIGSTKSZ$/) \ + && ($3>=0) && ($3<=1000)) { + SIGNAL[$3]=$2; + if ($3 > max) { + max=$3; + } + } +} + +END { + for(i=0; i<=max; i++) { + if (!SIGNAL[i]) { + SIGNAL[i] = "SIG_" i; + } + pad = 16 - length(SIGNAL[i]); + if (pad<1) { + pad=1; + } + printf("\t\"%s\",%*s/* %d */\n", SIGNAL[i], pad, "", i); + } +} + diff --git a/cinelerra-5.1/prof2/mksyscallent b/cinelerra-5.1/prof2/mksyscallent new file mode 100755 index 00000000..e3d919fb --- /dev/null +++ b/cinelerra-5.1/prof2/mksyscallent @@ -0,0 +1,43 @@ +#!/usr/bin/awk -f + +# hack expression to generate arch/syscallent.h from +# It reads from stdin and writes to stdout +# Currently (linux-2.2.3), it works OK on i386,m68k,arm +# It does NOT work in mips +# It is untested in other architectures + +BEGIN { + max=0; + FS="[ \t\n()+]+"; +} + +{ +# printf("/%s/%s/%s/%s/\n", $1, $2, $3, $4); + if (($1 ~ /^#define$/) && ($2 ~ /^__NR_/)) { + if (($3>=0) && ($3<=1000)) { + SYSCALL[$3]=substr($2,6); + if ($3 > max) { + max=$3; + } + } else if (($3 ~ /^__NR_SYSCALL_BASE$/) && ($4>=0) && ($4<=1000)) { + SYSCALL[$4]=substr($2,6); + if ($4 > max) { + max=$4; + } + } + } +} + +END { + for(i=0; i<=max; i++) { + if (!SYSCALL[i]) { + SYSCALL[i] = i; + } + pad = 32 - length(SYSCALL[i]); + if (pad<1) { + pad=1; + } + printf("\t\"%s\",%*s/* %d */\n", SYSCALL[i], pad, "", i); + } +} + diff --git a/cinelerra-5.1/prof2/prof.c b/cinelerra-5.1/prof2/prof.c new file mode 100644 index 00000000..35af3810 --- /dev/null +++ b/cinelerra-5.1/prof2/prof.c @@ -0,0 +1,782 @@ +/* prof.c - initialize sampling profiler + load user executable in ptrace mode + at exec catchpoint, set breakpoint at main + add LD_PRELOAD = libprofile + run ld-linux to map libraries, stop at main + add ptr to main as argument, start profileStart +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sys.h" + +#define unlikely(x) __builtin_expect(!!(x), 0) +#define dprintf(s...) do { if( unlikely(debug!=0) ) fprintf(s); } while(0) + +#ifdef __i386__ +#define nmAddr 0 +#define nmType 9 +#define nmName 11 +#define _LX "%08lx" +#ifndef LIB +#define LIB "/lib/" +#endif +#endif +#ifdef __x86_64__ +#define nmAddr 0 +#define nmType 17 +#define nmName 19 +#define _LX "%016lx" +#ifndef LIB +#define LIB "/lib64/" +#endif +#endif + +#define SYM2MAX 1024*256 +#define SYMINCR 1024 + +#define DEBUG 0 +#define PROF_DEBUG 0 +#define PROF_OUTPUT "--" +#define PROF_TMR_TYPE 0 +#define PROF_TMR_USEC 10000 + +static int debug = DEBUG; +static int prof_debug = PROF_DEBUG; +static int prof_type = PROF_TMR_TYPE; +static int prof_tmrval = PROF_TMR_USEC; +static long prof_stkptr = 0; +static char *prof_output = NULL; +static const int max_signals = sizeof(signal_name)/sizeof(signal_name[0]); +static const int max_sysreqs = sizeof(sysreq_name)/sizeof(sysreq_name[0]); + +#define LIBPROFILE_NAME "libprofile.so" +#define LIBPROFILE_PATH LIB LIBPROFILE_NAME + +#ifdef __i386__ +#define LD_LINUX_SO LIB "ld-linux.so.2" +#endif +#ifdef __x86_64__ +#define LD_LINUX_SO LIB "ld-linux-x86-64.so.2" +#endif + +char *libprofile_path = NULL; + +typedef struct { + pid_t pid; + unsigned long addr; + long orig_value; + int enabled; +} breakpoint; + +typedef struct { + char *nm; + unsigned long adr; +} tSym; + +typedef struct { + int n, m; + tSym sym[SYMINCR]; +} tSymTbl; + +static tSymTbl * +newSymTbl(void) +{ + tSymTbl *tp; + if( (tp=malloc(sizeof(*tp))) != NULL ) { + tp->n = 0; + tp->m = sizeof(tp->sym)/sizeof(tp->sym[0]); + } + return tp; +} + +static int +symCmpr(const void *a, const void *b) +{ + const char *anm = ((tSym const*)a)->nm; + const char *bnm = ((tSym const*)b)->nm; + return strcmp(anm,bnm); +} + +static int +newSym(tSymTbl **ptp, const char *nm, unsigned long adr) +{ + tSym *sp; + tSymTbl *tp = *ptp; + int n = tp->n; + int m = tp->m; + if( n >= m ) { + tSymTbl *rp; int l; + m = m < SYM2MAX ? m*2 : m+SYMINCR; + l = sizeof(*tp)-sizeof(tp->sym)+m*sizeof(tp->sym[0]); + rp = (tSymTbl*) realloc(tp,l); + if( rp == NULL ) return -1; + tp = rp; + tp->m = m; + *ptp = tp; + } + sp = &tp->sym[n++]; + tp->n = n; + sp->nm = strdup(nm); + sp->adr = adr; + return 0; +} +static void +delSymTbl(tSymTbl **ptp) +{ + int i; char *nm; + tSymTbl *tp = *ptp; + tSym *sp = &tp->sym[0]; + for( i=tp->n; --i>=0; ++sp ) { + if( (nm=sp->nm) != NULL ) + free(nm); + } + free(tp); + *ptp = NULL; +} + +static int +isSymNm(char *cp, char *bp) +{ + if( !*cp || *cp=='\n' ) return 0; + int ch; + while( (ch=*cp++) != 0 ) { + if( ch == '\n' ) break; + *bp++ = ch; + if( ch == '#' || ch == '$' || ch == '.' ) return 0; + } + *bp = 0; + return 1; +} + +static int +mapObj(char *fn,tSymTbl **ptp,const char *typs) +{ + char nmLine[512], nmCmd[512], *lp, *rp; + unsigned long adr; + FILE *fp; + if( access(fn,R_OK) < 0 ) return -1; + strcpy(&nmCmd[0],"smap 2> /dev/null < "); + strcat(&nmCmd[0],fn); + if( (fp=popen(&nmCmd[0],"r")) == NULL ) return -1; + while((lp=fgets(&nmLine[0],sizeof nmLine,fp)) != NULL ) { + int t = nmLine[nmType]; + if( t != 'T' && t != 't' && (!typs || !strchr(typs,t)) ) continue; + if( isSymNm(&nmLine[nmName],&nmCmd[0]) == 0 ) continue; + adr = strtoul(&nmLine[nmAddr],&rp,16); + if( (rp-&nmLine[nmAddr]) != nmType-1 ) continue; + if( newSym(ptp,&nmCmd[0],adr) != 0 ) break; + } + pclose(fp); + return lp != NULL ? -1 : 0; +} + + +static tSymTbl * +mapFile(char *fn,const char *typs) +{ + tSymTbl *tp = newSymTbl(); + if( tp && mapObj(fn,&tp,typs) != 0 ) + delSymTbl(&tp); + if( tp ) + qsort(&tp->sym,tp->n,sizeof(tp->sym[0]),symCmpr); + return tp; +} + +static tSym * +symLkup(tSymTbl *tp,const char *nm) +{ + int l, m, n, r; + tSym *mp = NULL; + tSym *sp = &tp->sym[0]; + l = n = -1; r = tp->n; + while( (r-l) > 1 ) { + m = (r+l) >> 1; + mp = &sp[m]; + n = strcmp(mp->nm,nm); + if( n == 0 ) break; + if( n < 0 ) l = m; + else r = m; + } + return n == 0 ? mp : NULL; +} + +static void +findSym(tSymTbl *tp,char *nm,tSym **psp) +{ + tSym *sp = symLkup(tp,nm); + if( sp == NULL ) { + fprintf(stderr,"cant find symbol '%s'\n",nm); + exit(1); + } + *psp = sp; +} + +int +execute_program(char *fn,char **argv) +{ + pid_t pid = fork(); + if( pid < 0 ) { + perror("fork"); + return -1; + } + if( pid != 0 ) + return pid; + if (ptrace(PTRACE_TRACEME, 0, 1, 0)<0) { + perror("PTRACE_TRACEME"); + exit(1); + } + setenv("LD_PRELOAD",libprofile_path,1); + //setenv("LD_DEBUG","files",1); + execvp(fn,argv); + fprintf(stderr, "Can't execute `%s': %s\n", fn, strerror(errno)); + exit(1); +} + +#ifdef __i386__ +static long +peek_text(pid_t pid,long addr) +{ + return ptrace(PTRACE_PEEKTEXT,pid,addr,0); +} + +static void +poke_text(pid_t pid,long addr,long value) +{ + ptrace(PTRACE_POKETEXT,pid,addr,value); +} + +static long +get_eip(pid_t pid) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + return regs.eip; +} + +static void +set_eip(pid_t pid,long addr) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + regs.eip = addr; + ptrace(PTRACE_SETREGS,pid,0,®s); +} + +static long +get_sysreq(pid_t pid) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + return regs.orig_eax; +} + +static long +get_sysret(pid_t pid) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + return regs.eax; +} + +static long +get_esp(pid_t pid) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + return regs.esp; +} + +static long +peek_stk(pid_t pid,long addr) +{ + return ptrace(PTRACE_PEEKDATA,pid,addr,0); +} + +static void +poke_stk(pid_t pid,long addr,long value) +{ + ptrace(PTRACE_POKEDATA,pid,addr,value); +} + +#endif + +#ifdef __x86_64__ +static long +peek_text(pid_t pid,long addr) +{ + long ret = ptrace(PTRACE_PEEKTEXT,pid,addr,0); + return ret; +} + +static void +poke_text(pid_t pid,long addr,long value) +{ + ptrace(PTRACE_POKETEXT,pid,addr,value); +} + +static long +get_eip(pid_t pid) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + return regs.rip; +} + +static void +set_eip(pid_t pid,long addr) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + regs.rip = addr; + ptrace(PTRACE_SETREGS,pid,0,®s); +} + +static long +get_sysreq(pid_t pid) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + return regs.orig_rax; +} + +static long +get_sysret(pid_t pid) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + return regs.rax; +} + +static long +get_esp(pid_t pid) +{ + struct user_regs_struct regs; + ptrace(PTRACE_GETREGS,pid,0,®s); + return regs.rsp; +} + +static long +peek_stk(pid_t pid,long addr) +{ + errno = 0; + long ret = ptrace(PTRACE_PEEKDATA,pid,addr,0); + if( ret == -1 && errno ) + dprintf(stderr,"peek_stk failed @ "_LX" = %d:%s", + addr, errno, strerror(errno)); + return ret; +} + +static void +poke_stk(pid_t pid,long addr,long value) +{ + ptrace(PTRACE_POKEDATA,pid,addr,value); +} +#endif + +static int +pstrcmp(pid_t pid,long addr,char *cp) +{ + int n, v; long w, adr; + adr = addr & ~(sizeof(w)-1); + n = addr & (sizeof(w)-1); + w = peek_stk(pid,adr); + adr += sizeof(long); + w >>= n*8; + n = sizeof(w) - n; + for(;;) { + if( (v=(w&0xff)-*cp) != 0 ) break; + if( *cp == 0 ) break; + if( --n <= 0 ) { + w = peek_stk(pid,adr); + adr += sizeof(w); + n = sizeof(w); + } + else + w >>= 8; + ++cp; + } + return v; +} + +static char * +pstrcpy(pid_t pid,long addr,char *cp) +{ + long w, by; + unsigned char *bp = (unsigned char *)cp; + long adr = addr & ~(sizeof(w)-1); + int n = 8*(addr & (sizeof(w)-1)); + w = peek_stk(pid,adr); + do { + by = *bp++; + w &= ~(0xffUL << n); + w |= by << n; + if( (n+=8) >= sizeof(w)*8 ) { + poke_stk(pid,adr,w); + adr += sizeof(w); + w = peek_stk(pid,adr); + n = 0; + } + } while( by ); + if( n ) + poke_stk(pid,adr,w); + return cp; +} + +/* instruction = int 3 */ +#define BREAKPOINT_VALUE 0xcc +#define BREAKPOINT_LENGTH 1 +#define DECR_PC_AFTER_BREAK 1 + +void +enable_breakpoint(breakpoint *bp) +{ + if( bp->enabled == 0 ) { + int shft; long word, mask; + pid_t pid = bp->pid; + unsigned long addr = bp->addr & ~(sizeof(long)-1); + word = peek_text(pid,addr); + bp->orig_value = word; + shft = 8 * (bp->addr&(sizeof(long)-1)); + mask = 0xffUL << shft; + word = (word & ~mask) | ((unsigned long)BREAKPOINT_VALUE << shft); + poke_text(pid,addr,word); + bp->enabled = 1; + dprintf(stderr,"set bp "_LX"\n",bp->addr); + } +} + +void +disable_breakpoint(breakpoint *bp) +{ + if( bp->enabled != 0 ) { + int shft; long word, mask; + pid_t pid = bp->pid; + unsigned long addr = bp->addr & ~(sizeof(long)-1); + word = peek_text(pid,addr); + shft = 8 * (bp->addr&(sizeof(long)-1)); + mask = 0xffUL << shft; + word = (word & ~mask) | (bp->orig_value & mask); + poke_text(pid,addr,word); + bp->enabled = 0; + } +} + +breakpoint *newBrkpt(pid_t pid,unsigned long addr) +{ + breakpoint *bp; + bp = (breakpoint *)malloc(sizeof(*bp)); + memset(bp,0,sizeof(*bp)); + bp->pid = pid; + bp->addr = addr; + return bp; +} + +static long +get_map_fwa(pid_t pid,char *pfx1, char *pfx2) +{ + FILE *fp; int n, len1, len2; + long fwa, lwa1, pgoff; + unsigned long inode; + int major, minor; + char f1, f2, f3, f4, path[512], bfr[512]; + sprintf(&bfr[0],"/proc/%d/maps",(int)pid); + if( (fp=fopen(&bfr[0],"r")) == NULL ) { + perror(&bfr[0]); exit(1); + } + len1 = pfx1 ? strlen(pfx1) : 0; + len2 = pfx2 ? strlen(pfx2) : 0; + for(;;) { + if( fgets(&bfr[0],sizeof(bfr),fp) == NULL ) { + fprintf(stderr,"cant find process %d '%s' maps\n",(int)pid,pfx1); + exit(1); + } + n = sscanf(&bfr[0],"%lx-%lx %c%c%c%c %lx %x:%x %lu %s", + &fwa,&lwa1,&f1,&f2,&f3,&f4,&pgoff,&major,&minor,&inode,&path[0]); + if( n != 11 ) continue; + // if( f3 != 'x' ) continue; + if( !pfx1 && !pfx2 ) break; + if( pfx1 && strncmp(pfx1,&path[0],len1) == 0 ) break; + if( pfx2 && strncmp(pfx2,&path[0],len2) == 0 ) break; + } + fclose(fp); + return fwa; +} +#ifdef __i386__ +#define CHILD(v) (peek_stk(pid,(long)(v))) +#define ACHILD(v) CHILD(v) +#endif +#ifdef __x86_64__ +#define CHILD(v) (peek_stk(pid,(long)(v)&0x7fffffffffffL)) +#define ACHILD(v) (CHILD(v)&0x7fffffffffffL) +#endif +#define VCHILD(v) ((void*)CHILD(v)) + +static void +set_parm(pid_t pid,tSymTbl *tprof,long ofs,char *nm,long val) +{ + tSym *var; + dprintf(stderr,"set %s = %#lx\n",nm,val); + findSym(tprof,nm,&var); + var->adr += ofs; + poke_stk(pid,var->adr,val); +} + +static void +usage(char *av0) +{ + fprintf(stderr,"usage: %s [-o path] [-d] [-e] [-p libpath] [-012] [-u #] cmd args...\n",av0); + fprintf(stderr," -o profile output pathname, -=stdout, --=stderr\n"); + fprintf(stderr," -d debug output enabled\n"); + fprintf(stderr," -e child debug output enabled\n"); + fprintf(stderr," -p specify path for libprofile.so\n"); + fprintf(stderr," -0 usr+sys cpu timer intervals (sigprof)\n"); + fprintf(stderr," -1 usr only cpu timer intervals (sigvtalrm)\n"); + fprintf(stderr," -2 real time timer intervals (sigalrm)\n"); + fprintf(stderr," -u profile timer interval in usecs\n"); +} + +long run_to_breakpoint(pid_t ppid, int init_syscall) +{ + int in_syscall = init_syscall; + if( in_syscall < 0 ) + ptrace(PTRACE_SYSCALL,ppid,0,0); + for(;;) { + int status; + pid_t pid = wait(&status); /* wait for ptrace status */ + if( ppid != pid ) continue; + if( WIFSTOPPED(status) ) { + int stopsig = WSTOPSIG(status); + if( stopsig == SIGTRAP ) { + long eip = get_eip(pid); + int sysnum = get_sysreq(pid); + if( in_syscall < 0 ) { /* starting syscall */ + if( sysnum < 0 ) { /* hit breakpoint */ + dprintf(stderr,"hit bp at 0x"_LX"\n",eip); + return eip; + } + dprintf(stderr,"syscall %3d "_LX" - %s",sysnum,eip, + sysnum >= max_sysreqs ? "unknown" : sysreq_name[sysnum]); + in_syscall = sysnum; + } + else { /* finishing syscall */ + long ret = get_sysret(pid); + if( in_syscall != sysnum ) + dprintf(stderr,"(%d!)",sysnum); + dprintf(stderr," = %#lx\n",ret); + if( init_syscall >= 0 ) + return eip; + in_syscall = -1; + } + } + /* resume execution */ + ptrace(PTRACE_SYSCALL,pid,0,0); + } + else { /* unexepected status returned */ + if( WIFEXITED(status) ) { + int exit_code = WEXITSTATUS(status); + fprintf(stderr,"exit %d\n",exit_code); + } + else if( WIFSIGNALED(status) ) { + int signum = WTERMSIG(status); + fprintf(stderr,"killed %d - %s\n", signum, + signum < 0 || signum >= max_signals ? + "unknown" : signal_name[signum]); + } + else { + fprintf(stderr,"unknown status %d\n",status); + } + exit(1); + } + } +} + +static void +setup_profile(pid_t pid,long r_debug,long bp_main) +{ + tSymTbl *tprof; + tSym *sym; + struct link_map *so; + long absOffset = get_map_fwa(pid, LIB "ld-", "/usr" LIB "ld-"); + struct r_debug *rdebug = (struct r_debug *)(r_debug+absOffset); + /* look through maps - find libprofile image, save absOffset */ + dprintf(stderr,"rdebug "_LX"\n",(long)rdebug); + for( so=VCHILD(&rdebug->r_map); so!=NULL; so=VCHILD(&so->l_next) ) { + absOffset = CHILD(&so->l_addr); + if( absOffset == 0 ) continue; + if( pstrcmp(pid,ACHILD(&so->l_name),libprofile_path) == 0 ) break; + } + if( so == NULL ) { + fprintf(stderr,"%s did not preload\n",libprofile_path); + exit(1); + } + dprintf(stderr,"libprofile "_LX"\n",absOffset); + /* map libprofile with nm */ + tprof = mapFile(libprofile_path,"bd"); + prof_stkptr = get_esp(pid); + dprintf(stderr,"orig esp "_LX"\n",prof_stkptr); + set_parm(pid,tprof,absOffset,"prof_stkptr",prof_stkptr); + set_parm(pid,tprof,absOffset,"prof_debug",prof_debug); + set_parm(pid,tprof,absOffset,"prof_type",prof_type); + set_parm(pid,tprof,absOffset,"prof_tmrval",prof_tmrval); + findSym(tprof,"prof_output",&sym); + sym->adr += absOffset; + pstrcpy(pid,sym->adr,&prof_output[0]); + findSym(tprof,"profileMain",&sym); + sym->adr += absOffset; + dprintf(stderr,"set eip = %#lx\n",sym->adr); + /* resume execution in profileStart */ + set_eip(pid,sym->adr); +} + +int main(int ac, char **av) +{ + char *cp; + tSymTbl *tpath, *tld_linux; + tSym *sym_main, *sym__r_debug; + int pid, status, execve; + breakpoint *main_bp = NULL; + char *lib_path = NULL, *lib_argv = NULL; + + + /* decode cmdline params */ + while( ac > 1 && *(cp=av[1]) == '-' ) { + switch( cp[1] ) { + case 'd': /* enable debug output */ + debug = 1; + break; + case 'e': /* enable child debug output */ + prof_debug = 1; + break; + case '0': /* use sigprof */ + prof_type = 0; + break; + case '1': /* use sigvtalrm */ + prof_type = 1; + break; + case '2': /* use sigalrm */ + prof_type = 2; + break; + case 'u': /* use timer interval usec */ + prof_tmrval = strtoul(av[2],NULL,0); + --ac; ++av; + break; + case 'o': /* specify output path */ + prof_output = av[2]; + --ac; ++av; + break; + case 'p': /* specify libprofile */ + lib_argv = av[2]; + --ac; ++av; + break; + default: + fprintf(stderr,"unknown parameter - '%s'\n",cp); + case 'h': /* help */ + ac = 0; + break; + } + --ac; ++av; + } + + if( ac < 2 ) { + usage(av[0]); + exit(1); + } + + if( lib_argv != NULL ) { + if( access(&lib_argv[0],R_OK) == 0 ) + lib_path = strdup(lib_argv); + } + if( lib_path == NULL ) { + cp = "./" LIBPROFILE_NAME; + if( access(cp,R_OK) == 0 ) + lib_path = strdup(cp); + } + if( lib_path == NULL && (cp=getenv("HOME")) != NULL ) { + char libpath[PATH_MAX]; + snprintf(&libpath[0],sizeof(libpath),"%s/bin/%s", + cp,LIBPROFILE_NAME); + if( access(&libpath[0],R_OK) == 0 ) + lib_path = strdup(&libpath[0]); + } + if( lib_path == NULL ) { + if( access(cp=LIBPROFILE_PATH,R_OK) == 0 ) + lib_path = strdup(cp); + } + if( lib_path == NULL ) { + fprintf(stderr,"cant find %s\n",LIBPROFILE_NAME); + exit(1); + } + libprofile_path = lib_path; + + if( prof_output == NULL ) + prof_output = PROF_OUTPUT; + + /* read nm output for executable */ + tpath = mapFile(av[1],0); + if( tpath == NULL ) { + fprintf(stderr,"cant map %s\n",av[1]); + exit(1); + } + /* read nm output for ld-linux */ + tld_linux = mapFile(LD_LINUX_SO,"BD"); + if( tld_linux == NULL ) { + fprintf(stderr,"cant map %s\n",LD_LINUX_SO); + exit(1); + } + + /* lookup main, _r_debug */ + findSym(tpath,"main",&sym_main); + findSym(tld_linux,"_r_debug",&sym__r_debug); + /* fork child with executable */ + pid = execute_program(av[1],&av[1]); + if( pid < 0 ) { + perror(av[1]); + exit(1); + } + + execve = sizeof(sysreq_name)/sizeof(sysreq_name[0]); + while( --execve >= 0 && strcmp("execve",sysreq_name[execve]) ); + if( execve < 0 ) { + fprintf(stderr,"cant find execve sysreq\n"); + exit(1); + } + main_bp = newBrkpt(pid,sym_main->adr); + run_to_breakpoint(pid, execve); + /* add offset of main module */ + main_bp->addr += get_map_fwa(pid,0,0); + enable_breakpoint(main_bp); + run_to_breakpoint(pid, -1); + /* hit breakpoint at 'main', setup profiler */ + setup_profile(pid,sym__r_debug->adr,main_bp->addr); + disable_breakpoint(main_bp); + ptrace(PTRACE_DETACH,pid,0,0); /* turn off ptrace */ + pid = wait(&status); /* wait for child status */ + if( WIFEXITED(status) ) { + status = WEXITSTATUS(status); + dprintf(stderr,"exit %d\n",status); + } + else if( WIFSIGNALED(status) ) { + int signum = WTERMSIG(status); + fprintf(stderr,"killed %d - %s\n", signum, + signum < 0 || signum >= max_signals ? + "unknown" : signal_name[signum]); + } + else { + fprintf(stderr,"unknown status %d\n",status); + status = 1; + } + return status; +} + diff --git a/cinelerra-5.1/prof2/profile.c b/cinelerra-5.1/prof2/profile.c new file mode 100644 index 00000000..6103b533 --- /dev/null +++ b/cinelerra-5.1/prof2/profile.c @@ -0,0 +1,843 @@ +#include +#include +#include +#include +#include +#include + +#define smCommand "smap 2> /dev/null < " +#ifdef __i386__ +#define nmAddr 0 +#define nmType 9 +#define nmName 11 +#define _LX "%08lx" +#ifndef LIB +#define LIB "/lib" +#endif +#endif +#ifdef __x86_64__ +#define nmAddr 0 +#define nmType 17 +#define nmName 19 +#define _LX "%016lx" +#ifndef LIB +#define LIB "/lib64" +#endif +#endif + +#define unlikely(x) __builtin_expect(!!(x), 0) +#define dprintf(s...) do { if( unlikely(prof_debug!=0) ) fprintf(s); } while(0) + +int profileMain(int ac,char **av,char **ev); +void profileStart(void); +void profileStop(void); +void profileClear(void); +void profileShow(void); +void profileDone(void); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAP2MAX 256 +#define MAPINCR 64 +#define MOD2MAX 256 +#define MODINCR 64 +#define SYM2MAX 1024*256 +#define SYMINCR 1024*16 +#if 0 +static volatile long prof_debug = 1; +static volatile long prof_type = 0; +static volatile long prof_tmrval = 10000; +static volatile unsigned long prof_stkptr = INT_MAX-0x1000; +static volatile char prof_output[256] = { "-" }; +#else +static volatile long prof_debug; /* enable debug output */ +static volatile long prof_type; /* 0=usr+sys, 1=usr only, 2=real time */ +static volatile long prof_tmrval; /* profile timer interval in usecs */ +static volatile unsigned long prof_stkptr; /* top of stack at profileStart */ +static volatile char prof_output[256]; /* profile output file name */ +#endif + +static FILE *prof_fp = NULL; + +typedef struct { + char *name; + unsigned long offs; +} tMap; + +typedef struct { + int n, m; + tMap map[MAPINCR]; +} tMapTbl; + +typedef struct { + char *name; + unsigned long offs; + int count; + int calls; +} tMod; + +typedef struct { + int n, m; + tMod mod[MODINCR]; +} tModTbl; + +typedef struct { + char *name; + unsigned long adr; + int count; + int calls; + int tick; + int tcalls; + int mod; +} tSym; + +typedef struct { + int n, m; + tSym sym[SYMINCR]; +} tSymTbl; + +static tSymTbl *tsym = NULL; +static tModTbl *tmod = NULL; +static tMapTbl *tmap = NULL; +static int nTicks; /* profile timer tick count */ +static struct tms start_cpu; /* cpu time at profile_start */ +static struct tms stop_cpu; /* cpu time at profile_stop */ +static struct timeval start_clk; /* real time at profile_start */ +static struct timeval stop_clk; /* real time at profile_stop */ + +int profileActive = 0; /* flag, true if profile handler active */ +static struct sigaction oldSig; /* orignal profile handler data */ +static struct sigaction oldSegv; /* orignal segv handler data */ + +static tMapTbl * +newMapTbl(void) +{ + tMapTbl *tp; + if( (tp=malloc(sizeof(*tp))) != NULL ) { + tp->n = 0; + tp->m = sizeof(tp->map)/sizeof(tp->map[0]); + } + return tp; +} + +static tModTbl * +newModTbl(void) +{ + tModTbl *tp; + if( (tp=malloc(sizeof(*tp))) != NULL ) { + tp->n = 0; + tp->m = sizeof(tp->mod)/sizeof(tp->mod[0]); + } + return tp; +} + +static tSymTbl * +newSymTbl(void) +{ + tSymTbl *tp; + if( (tp=malloc(sizeof(*tp))) != NULL ) { + tp->n = 0; + tp->m = sizeof(tp->sym)/sizeof(tp->sym[0]); + } + return tp; +} + +static tSym * +newSym(tSymTbl **ptp,const char *name,unsigned long adr,int mod) +{ + tSym *sp; + tSymTbl *tp = *ptp; + int n = tp->n; + int m = tp->m; + if( n >= m ) { + tSymTbl *rp; int l; + m = m < SYM2MAX ? m*2 : m+SYMINCR; + l = sizeof(*tp)-sizeof(tp->sym)+m*sizeof(tp->sym[0]); + rp = (tSymTbl*) realloc(tp,l); + if( rp == NULL ) return NULL; + tp = rp; + tp->m = m; + *ptp = tp; + } + sp = &tp->sym[n++]; + tp->n = n; + sp->name = strdup(name); + sp->adr = adr; + sp->mod = mod; + sp->count = sp->calls = 0; + sp->tick = sp->tcalls = 0; + return sp; +} + +static int +newMod(tModTbl **ptp, const char *name, unsigned long offs) +{ + tMod *mp; + tModTbl *tp = *ptp; + int n = tp->n; + int m = tp->m; + if( n >= m ) { + tModTbl *rp; int l; + m = m < MOD2MAX ? m*2 : m+MODINCR; + l = sizeof(*tp)-sizeof(tp->mod)+m*sizeof(tp->mod[0]); + rp = (tModTbl*) realloc(tp,l); + if( !rp ) return -1; + tp = rp; + *ptp = tp; + } + int ret = n++; + mp = &tp->mod[ret]; + tp->n = n; + mp->name = strdup(name); + mp->offs = offs; + mp->count = mp->calls = 0; + return ret; +} + +int +findMod(tModTbl *tp, const char *nm) +{ + int n = tp->n; + while( --n >= 0 && strcmp(nm,tp->mod[n].name) ); + return n; +} + +static int +newMap(tMapTbl **ptp, const char *name, unsigned long offs) +{ + tMap *mp; + tMapTbl *tp = *ptp; + int n = tp->n; + int m = tp->m; + if( n >= m ) { + tMapTbl *rp; int l; + m = m < MAP2MAX ? m*2 : m+MAPINCR; + l = sizeof(*tp)-sizeof(tp->map)+m*sizeof(tp->map[0]); + rp = (tMapTbl*) realloc(tp,l); + if( !rp ) return -1; + tp = rp; + *ptp = tp; + } + int ret = n++; + mp = &tp->map[ret]; + tp->n = n; + mp->name = strdup(name); + mp->offs = offs; + return ret; +} + +static void +delMapTbl(tMapTbl **ptp) +{ + int i; char *name; + tMapTbl *tp; tMap *mp; + if( (tp=*ptp) == NULL ) return; + mp = &tp->map[0]; + for( i=tp->n; --i>=0; ++mp ) { + if( (name=mp->name) != NULL ) + free(name); + } + free(tp); + *ptp = NULL; +} + +static void +delModTbl(tModTbl **ptp) +{ + int i; char *name; + tModTbl *tp; tMod *mp; + if( (tp=*ptp) == NULL ) return; + mp = &tp->mod[0]; + for( i=tp->n; --i>=0; ++mp ) { + if( (name=mp->name) != NULL ) + free(name); + } + free(tp); + *ptp = NULL; +} + +static void +delSymTbl(tSymTbl **ptp) +{ + int i; char *name; + tSymTbl *tp; tSym *sp; + if( (tp=*ptp) == NULL ) return; + sp = &tp->sym[0]; + for( i=tp->n; --i>=0; ++sp ) { + if( (name=sp->name) != NULL ) + free(name); + } + free(tp); + *ptp = NULL; +} + +static void +delAllTbls(void) +{ + delMapTbl(&tmap); + delModTbl(&tmod); + delSymTbl(&tsym); +} + +static int +mapLkup(tMapTbl *tp,const char *nm) +{ + tMap *mp = &tp->map[0]; + int n = tp->n; + while( --n >= 0 && strcmp(nm,mp->name)!=0 ) + ++mp; + return n; +} + +static int +findMap(tMapTbl *tp,char *nm) +{ + int ret = mapLkup(tp,nm); + if( ret < 0 ) + dprintf(stderr,"cant find map '%s'\n",nm); + return ret; +} + +static int +findSymAdr(tSymTbl *tp,const char *nm,unsigned long *adr) +{ + int n = tp->n; + while( --n >= 0 && strcmp(nm,tp->sym[n].name) ); + if( n < 0 ) return 1; + *adr = tp->sym[n].adr; + return 0; +} + +static int +isSymNm(char *cp, char *bp) +{ + int ch; + while( (ch=*cp++) != 0 ) { + if( ch == '\n' ) break; + *bp++ = ch; + if( ch == '#' || ch == '$' || ch == '.' ) return 0; + } + *bp = 0; + return 1; +} + +static int +hex(char *cp, unsigned long *val) +{ + char *bp = cp; + *val = strtoul(cp,&bp,16); + return bp - cp; +} + +static int +mapMod(char *fn,tSymTbl **ptp, int mod) +{ + char nmLine[512], nmCmd[512], *lp; + unsigned long adr, ofs; int typ; + FILE *fp; tMod *mp; + if( access(fn, R_OK) < 0 ) return -1; + strcpy(&nmCmd[0],smCommand); + strcat(&nmCmd[0],fn); + lp = NULL; + if( (fp=popen(&nmCmd[0],"r")) == NULL ) return -1; + mp = &tmod->mod[mod]; ofs = mp->offs; + while((lp=fgets(&nmLine[0],sizeof nmLine,fp)) != NULL ) { + typ = nmLine[nmType]; + if( typ != 'T' && typ != 't' ) continue; + if( isSymNm(&nmLine[nmName],&nmCmd[0]) == 0 ) continue; + if( hex(&nmLine[nmAddr],&adr) != nmType-1 ) continue; + adr += ofs; + if( newSym(ptp,&nmCmd[0],adr,mod) == NULL ) break; + } while((lp=fgets(&nmLine[0],sizeof nmLine,fp)) != NULL ); + pclose(fp); + return lp != NULL ? -1 : 0; +} + +static int +adrCmpr(const void *a, const void *b) +{ + unsigned long aadr = ((tSym*)a)->adr; + unsigned long badr = ((tSym*)b)->adr; + if( aadr > badr ) return 1; + if( aadr < badr ) return -1; + return 0; +} + +static int +cntCmpr(const void *a, const void *b) +{ + int acnt = ((tSym*)a)->count; + int bcnt = ((tSym*)b)->count; + return acnt - bcnt; +} + +static int +tclCmpr(const void *a, const void *b) +{ + int atcl = ((tSym*)a)->tcalls; + int btcl = ((tSym*)b)->tcalls; + return atcl - btcl; +} + +static int +modCmpr(const void *a, const void *b) +{ + int acnt = ((tMod*)a)->count; + int bcnt = ((tMod*)b)->count; + return acnt - bcnt; +} + +static int +profile_tally(unsigned long pc, int cnt) +{ + tSym *sp, *tp; + int l, m, r; + if( tsym == NULL ) + return -1; + sp = tp = &tsym->sym[0]; + l = -1; r = tsym->n; + /* find function containing pc */ + while( (r-l) > 1 ) { + m = (r+l) >> 1; + sp = &tp[m]; + if( sp->adr == pc ) break; + if( sp->adr < pc ) l = m; + else r = m; + } + if( sp->adr != pc ) { + if( l < 0 ) + return -1; + sp = &tp[l]; + } + tMod *mp = &tmod->mod[sp->mod]; +/* incr function/module calls/counts */ + ++sp->calls; + ++mp->calls; +/* only 1 call per unwinding */ + if( sp->tick < nTicks ) { + sp->tick = nTicks; + ++sp->tcalls; + } + if( cnt != 0 ) { + ++sp->count; + ++mp->count; + } + return 0; +} + +struct reg_frame { + unsigned long rf_fp; /* frame pointer */ + unsigned long rf_rtn; /* return addr */ +}; + +static int readProcMaps(unsigned long *fwa) +{ + int ret, n, m, fd, len; + unsigned long vm_start, vm_end, ino; + char mr, mw, mx, ms, *bp; + off_t pgoff; + unsigned int maj, min; + char mfn[PATH_MAX], ifn[PATH_MAX]; + char bfr[65536]; + pid_t pid = getpid(); + ret = 1; + snprintf(&mfn[0],sizeof mfn,"/proc/%d/maps",pid); + fd = open(&mfn[0],O_RDONLY); + if( fd >= 0 ) { + len = read(fd,bp=&bfr[0],sizeof bfr); + close(fd); + while( len > 0 ) { + m = sscanf(bp, "%lx-%lx %c%c%c%c %lx %x:%x %lu%n", + &vm_start,&vm_end,&mr,&mw,&mx,&ms,&pgoff,&maj,&min,&ino,&n); + if( m == 10 && fwa ) { *fwa = vm_start; fwa = 0; } + if( m == 10 && mx == 'x' && pgoff == 0 ) { + for( bp+=n,len-=n ; len>0 && *bp!='\n' && *bp==' '; ++bp,--len ); + for( m=0 ; len>0 && *bp!='\n' && m=0 && *bp++!='\n' ); + } + ret = 0; + } + return ret; +} + +static int readNmMaps(unsigned long fwa) +{ + unsigned long adr; + int i, k; + char dfn[PATH_MAX], efn[PATH_MAX], pfn[PATH_MAX]; + struct link_map *so; + struct r_debug *rdebug = &_r_debug; + for( so=rdebug->r_map; so!=NULL; so=so->l_next ) { + int n = tsym->n; + char *nm = &so->l_name[0]; + if( nm[0] == 0 ) nm = "/proc/self/exe"; + dprintf(stderr,"resolving %s\n",nm); + if( nm[0] == '.' && nm[1] == '/' ) { + getcwd(&pfn[0],sizeof(pfn)); + strcat(&pfn[0],&nm[1]); + nm = &pfn[0]; + } + while( (i=readlink(nm,&efn[0],sizeof(efn)-1)) >= 0 ) { + efn[i] = 0; + dprintf(stderr," link %s\n",&efn[0]); + if( efn[0] != '/' ) { /* get dirname */ + for( k=-1,i=0; i= 0 ) { /* add link name */ + for( i=0; k= 0 ) continue; + int map = !so->l_name[0] ? -1 : findMap(tmap, nm); + tMap *mp = map>=0 ? &tmap->map[map] : 0; + adr = mp ? mp->offs : (unsigned long) so->l_addr; + if( !adr ) adr = fwa; + dprintf(stderr," map %s adr = 0x"_LX" ofs = 0x"_LX"\n", + nm,adr,(unsigned long)so->l_addr); + int mod = newMod(&tmod,nm,adr); + if( mod >= 0 ) { + if( mapMod(nm,&tsym,mod) < 0 || tsym->n == n ) { + char dfn[PATH_MAX]; memset(dfn,0,sizeof(dfn)); + strcpy(&dfn[0],"/usr/lib/debug"); + strncat(&dfn[0],nm,sizeof(dfn)-1); + strncat(&dfn[0],".debug",sizeof(dfn)-1); + dprintf(stderr,", %s",&dfn[0]); + if( mapMod(&dfn[0],&tsym,mod) < 0 ) + mod = -1; + } + } + dprintf(stderr,", %d symbols\n",tsym->n - n); + if( mod < 0 ) { + fprintf(stderr,"profile - Cannot map module - %s\n",nm); +// exit(1); +// return -1; + } + } + + /* sort the symbols by address */ + qsort(&tsym->sym[0],tsym->n,sizeof(tsym->sym[0]),adrCmpr); + if( prof_debug != 0 ) { + int i; tSym *sp; + sp = &tsym->sym[0]; + for( i=tsym->n; --i>=0; ++sp ) { + tMod *mp = &tmod->mod[sp->mod]; + fprintf(stdout,_LX" %-24s %s\n",sp->adr, + &sp->name[0],&mp->name[0]); + } + } + return 0; +} + +/** profile_handler(sign,c) + * profile periodic timer signal handler. + * decodes pc/fp from sigcontext, and tallys pc (count) + * and stack trace (calls). + */ +#define LOW_TEXT 0x01000UL + +static void profile_handler(int n, siginfo_t * info, void *sc) +{ + ucontext_t *uc = (ucontext_t *)sc; + struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext; + struct reg_frame *fp, *last_fp; + unsigned long pc; + /* tid marks top of thread stack (currently) */ + unsigned long tid = (unsigned long)pthread_self(); + ++nTicks; + /* tally pc value */ +#ifdef __i386__ + pc = (unsigned long)c->eip; +#define ARCH_OK +#endif +#ifdef __x86_64__ + pc = (unsigned long)c->rip; +#define ARCH_OK +#endif +#ifndef ARCH_OK +#error unknown arch +#endif + if( pc < LOW_TEXT ) return; + if( profile_tally(pc,1) != 0 ) return; + + /* access frame pointer and validate */ +#ifdef __i386__ + fp = (struct reg_frame *)c->ebp; + if( (unsigned long)fp < c->esp ) return; +#endif +#ifdef __x86_64__ + fp = (struct reg_frame *)c->rbp; + if( (unsigned long)fp < c->rsp ) return; +#endif + if( (unsigned long)fp > prof_stkptr) return; + if( (unsigned long)fp > tid) return; + dprintf(stderr,"unwind "_LX"",(unsigned long)pc); + /* unwind the stack frames, and tally pc values */ + while( (last_fp=fp) != 0 ) { + pc = fp->rf_rtn; + dprintf(stderr," "_LX"",(unsigned long)pc); + if( pc < LOW_TEXT ) break; + if( profile_tally(pc,0) != 0 ) break; + fp = (struct reg_frame *)fp->rf_fp; + if( ((long)fp & 3) != 0 ) break; + if( fp <= last_fp ) break; + if( (unsigned long)fp > prof_stkptr ) break; + if( (unsigned long)fp > tid ) break; + } + dprintf(stderr,"\n"); + + return; +} + +void profileExit(void) +{ + profileStop(); + profileShow(); + profileDone(); +} + +static void profile_segv(int n, siginfo_t * info, void *sc) +{ + ucontext_t *uc = (ucontext_t *)sc; + struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext; + profileExit(); + fprintf(stderr,"segv at 0x"_LX"\n", +#if __i386__ + c->eip +#endif +#if __x86_64__ + c->rip +#endif + ); +} + +void profileStart(void) +{ + char *fn; + int typ, sig; + struct itimerval it; + struct timezone tz; + struct sigaction act; + struct sigaction segv; + unsigned long fwa; + + if( profileActive != 0 ) return; + + if( tmod == NULL || tsym == NULL || tmap == NULL ) { + setbuf(stderr,NULL); + dprintf(stderr,"start profile\n"); + delAllTbls(); + unsetenv("LD_PRELOAD"); + profileDone(); + fn = (char *)&prof_output[0]; + if( strcmp("-",&fn[0]) == 0 ) + prof_fp = stdout; + else if( strcmp("--",&fn[0]) == 0 ) + prof_fp = stderr; + else + prof_fp = fopen(&fn[0],"w"); + if( prof_fp == NULL ) { + perror(&fn[0]); + fprintf(stderr,"profile: no output path\n"); + exit(1); + } + tmap = newMapTbl(); fwa = 0; + if( tmap != NULL && readProcMaps(&fwa) == 0 ) { + tmod = newModTbl(); tsym = newSymTbl(); + if( tmod == NULL || tsym == NULL || readNmMaps(fwa) != 0 ) { + fprintf(stderr,"profile: unable to read nm maps\n"); + delModTbl(&tmod); delSymTbl(&tsym); + } + } + else { + fprintf(stderr,"profile: unable to read proc maps\n"); + delMapTbl(&tmap); + } + } + if( tmap == NULL || tmod == NULL || tsym == NULL ) { + delAllTbls(); + return; + } + + switch( prof_type ) { + case 1: typ = ITIMER_VIRTUAL; sig = SIGVTALRM; break; + case 2: typ = ITIMER_REAL; sig = SIGALRM; break; + default: typ = ITIMER_PROF; sig = SIGPROF; break; + } + + /* record the start time (cpu/real). */ + times(&start_cpu); + gettimeofday(&start_clk,&tz); + + /* enable the profile timer signal handler. */ + profileActive = 1; + memset(&act,0,sizeof(act)); + act.sa_sigaction = profile_handler; + act.sa_flags = SA_RESTART + SA_SIGINFO; + sigaction(sig, &act, &oldSig); + memset(&segv,0,sizeof(segv)); + segv.sa_sigaction = profile_segv; + segv.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV,&segv,&oldSegv); + + /* start the periodic profile timer signal */ + if( prof_tmrval == 0 ) prof_tmrval = 1; + it.it_value.tv_sec = + it.it_interval.tv_sec = prof_tmrval / 1000000; + it.it_value.tv_usec = + it.it_interval.tv_usec = prof_tmrval % 1000000; + setitimer(typ, &it, NULL); + dprintf(stderr,"starting profile.\n"); +} + +int profileMain(int ac,char **av,char **ev) +{ + int (*fmain)(int ac,char **av,char **ev); + unsigned long adr; + profileStart(); + if( findSymAdr(tsym,"main",&adr) != 0 ) { + fprintf(stderr,"cant locate sym \"main\"\n"); + exit(1); + } + atexit(profileExit); + fmain = (int (*)(int,char **,char **))adr; + dprintf(stderr,"starting \"main\" at: %p\n",fmain); + int ret = fmain(ac,av,ev); + dprintf(stderr,"ended \"main\" = %d\n",ret); + exit(ret); +} + +void profileStop(void) +{ + struct itimerval it; + struct timezone tz; + struct sigaction act; + + if( profileActive == 0 ) return; + dprintf(stderr,"stopping profile.\n"); + + /* records the stop time (cpu/real). */ + times(&stop_cpu); + gettimeofday(&stop_clk,&tz); + + /* disables the profile timer signal handler. */ + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = 0; + it.it_value.tv_usec = 0; + setitimer(ITIMER_PROF, &it, NULL); + + /* restore the original profile signal handler. */ + sigaction(SIGPROF, &oldSig, &act); + sigaction(SIGSEGV, &oldSegv, &act); + profileActive = 0; + return; +} + + +void profileClear(void) +{ + int i; + nTicks = 0; + if( tsym != NULL ) { + tSym *sp = &tsym->sym[0]; + for( i=tsym->n; --i>=0; ++sp ) { + sp->count = sp->calls = 0; + sp->tick = sp->tcalls = 0; + } + } + if( tmod != NULL ) { + tMod *mp = &tmod->mod[0]; + for( i=tmod->n; --i>=0; ++mp ) + mp->count = mp->calls = 0; + } +} + + +void profileShow(void) +{ + FILE *fp; int i; + double userDt, systDt; + double tickDt, realDt; + if( tsym == NULL || tmod == NULL ) return; + fp = prof_fp; + qsort(&tsym->sym[0],tsym->n,sizeof(tsym->sym[0]),cntCmpr); + /* outputs sorted (by count) list of functions called. */ + fprintf(fp,"---- profile start ----\n"); + fprintf(fp," %d ticks %d modules %d syms\n",nTicks,tmod->n,tsym->n); + tSym *sp = &tsym->sym[0]; + for( i=tsym->n; --i>=0; ++sp ) { + if( !sp->count ) continue; + tMod *mp = &tmod->mod[sp->mod]; + fprintf(fp,"%8.3fs %5.1f%% %-24s %s\n", + prof_tmrval*sp->count/1000000.0, + sp->count*100.0/nTicks,&sp->name[0], + &mp->name[0]); + } + + qsort(&tsym->sym[0],tsym->n,sizeof(tsym->sym[0]),tclCmpr); + /* outputs sorted (by calls) list of functions called. */ + fprintf(fp,"---- profile calls ----\n"); + sp = &tsym->sym[0]; + for( i=tsym->n; --i>=0; ++sp ) { + if( !sp->tcalls ) continue; + tMod *mp = &tmod->mod[sp->mod]; + fprintf(fp,"%8.3fs %5.1f%% %-24s %5.1f %s\n", + prof_tmrval*sp->tcalls/1000000.0, + sp->tcalls*100.0/nTicks,&sp->name[0], + (double)sp->calls/sp->tcalls,&mp->name[0]); + } + + realDt = ((stop_clk.tv_sec - start_clk.tv_sec) * 1000000.0 + + (stop_clk.tv_usec - start_clk.tv_usec) ) / 1000000.0; + /* output sorted (by count) list of modules called. */ + qsort(&tmod->mod[0],tmod->n,sizeof(tmod->mod[0]),modCmpr); + fprintf(fp,"----\n"); + tMod *mp = &tmod->mod[0]; + for( i=tmod->n; --i>=0; ++mp ) { + if( mp->count != 0 && mp->name != NULL ) { + double dt = prof_tmrval*mp->count/1000000.0; + fprintf(fp,"%8.3fs %5.1f%%/%5.1f%% %s\n", + dt,mp->count*100.0/nTicks,dt*100.0/realDt,mp->name); + } + } + + /* output tick time, cpu user/kernal time, real time. */ + tickDt = prof_tmrval*nTicks / 1000000.0; + userDt = (stop_cpu.tms_utime - start_cpu.tms_utime) / (double)CLOCKS_PER_SEC; + systDt = (stop_cpu.tms_stime - start_cpu.tms_stime) / (double)CLOCKS_PER_SEC; + + fprintf(fp,"%8.3ft %0.3fu+%0.3fs %0.3fr %5.1f%%\n", + tickDt, userDt, systDt, realDt, tickDt*100.0/realDt); + fprintf(fp,"---- profile end ----\n\n"); + qsort(&tsym->sym[0],tsym->n,sizeof(tsym->sym[0]),adrCmpr); +} + +void profileDone(void) +{ + profileStop(); + delModTbl(&tmod); + delSymTbl(&tsym); + if( prof_fp != NULL && prof_fp != stdout && prof_fp != stderr ) { + fclose(prof_fp); prof_fp = NULL; + } +} + diff --git a/cinelerra-5.1/prof2/smap.C b/cinelerra-5.1/prof2/smap.C new file mode 100644 index 00000000..e6887e6b --- /dev/null +++ b/cinelerra-5.1/prof2/smap.C @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +#include +#include +#define HAVE_DECL_BASENAME 1 +#include + +// install binutils-dev +// c++ -Wall -I/usr/include/libiberty -g smap.C -llzma -liberty + +typedef uint64_t addr_t; + +class smap_t; + +class smap_t { + static void demangle(const char *cp, char *bp, int n) { + const char *dp = cplus_demangle(cp, DMGL_PARAMS+DMGL_ANSI); + strncpy(bp, (dp ? dp : cp), n); + } + + class chrbfr { + char *cp; + public: + unsigned char *ptr() { return (unsigned char *)cp; } + operator char*() { return cp; } + chrbfr(int n) { cp = new char[n]; } + ~chrbfr() { delete [] cp; } + }; + Elf64_Ehdr elf; + FILE *sfp; + char *shdrs, *strtbl; + addr_t adr_plt; + int is_rela; + size_t plt_sz; char *plts; + size_t rel_sz; char *rels; + size_t sym_sz, sym_entsize; + size_t dyn_sz, dyn_entsize; + char *symstr, *symtbl; + char *dynstr, *dyntbl; + addr_t vofs, pofs; + int init(); + void finit(); + char *unxz(unsigned char *ibfr, size_t isz, size_t &out); + void splt(); + int sect(int num); + + int rdtbl(char *tbl, off_t ofs, int sz) { + if( fseek(sfp, ofs, SEEK_SET) ) return -1; + if( fread(tbl, sz, 1, sfp) != 1 ) return -1; + return 0; + } + int ldtbl(char *&tbl, off_t ofs, int sz) { + if( !rdtbl(tbl=new char[sz], ofs, sz) ) return 0; + delete [] tbl; tbl = 0; return -1; + } +public: + int symbols(); + + smap_t(FILE *fp) { + sfp = fp; + shdrs = 0; strtbl = 0; + adr_plt = 0; is_rela = -1; + plt_sz = 0; plts = 0; + rel_sz = 0; rels = 0; + sym_sz = 0; sym_entsize = 0; + dyn_sz = 0; dyn_entsize = 0; + symstr = 0; symtbl = 0; + dynstr = 0; dyntbl = 0; + vofs = 0; pofs = 0; + } + ~smap_t() { + delete [] shdrs; delete [] strtbl; + delete [] plts; delete [] rels; + delete [] symstr; delete [] symtbl; + delete [] dynstr; delete [] dyntbl; + } +}; + + +char *smap_t:: +unxz(unsigned char *ibfr, size_t isz, size_t &out) +{ + size_t in = 0, lmt = ~0, osz = 8*isz; + unsigned char *obfr = new unsigned char[osz]; + out = 0; + for(;;) { + int err = lzma_stream_buffer_decode(&lmt,0,0, + ibfr,&in,isz, obfr,&out,osz); + if( !err ) return (char *)obfr; + if( err != LZMA_BUF_ERROR ) break; + size_t nsz = 2*osz; + unsigned char *nbfr = new unsigned char[nsz]; + memcpy(nbfr, obfr, osz); + delete [] obfr; obfr = nbfr; + osz = nsz; + } + delete [] obfr; + return 0; +} + +int smap_t:: +sect(int num) +{ + Elf64_Shdr *shdr = (Elf64_Shdr *)(shdrs + num * elf.e_shentsize); + switch( shdr->sh_type ) { + case SHT_REL: + case SHT_RELA: { + if( !strtbl ) return 0; + const char *tp = strtbl + shdr->sh_name; + is_rela = shdr->sh_type == SHT_RELA ? 1 : 0; + if( strcmp(tp, is_rela ? ".rela.plt" : ".rel.plt") ) return 0; + int n = shdr->sh_entsize; + int esz = is_rela ? sizeof(Elf64_Rela) : sizeof(Elf64_Rel); + if( n < esz ) return -1; + if( (rel_sz = shdr->sh_size) <= 0 ) return -1; + return ldtbl(rels, shdr->sh_offset, rel_sz); + } + case SHT_PROGBITS: { + if( !strtbl ) return 0; + const char *tp = strtbl + shdr->sh_name; + if( !strcmp(tp, ".plt") ) { + if( (plt_sz = shdr->sh_size) <= 0 ) return -1; + adr_plt = shdr->sh_addr; + return ldtbl(plts, shdr->sh_offset, plt_sz); + } + if( !strcmp(tp, ".gnu_debugdata") ) { + size_t isz = shdr->sh_size, out = 0; + if( !isz ) return -1; + chrbfr ibfr(isz); + if( rdtbl(ibfr, shdr->sh_offset, isz) ) return -1; + char *obfr = unxz(ibfr.ptr(), isz, out); + int ret = -1; + if( obfr && out > 0 ) { + FILE *fp = fmemopen(obfr, out, "r"); + smap_t dmap(fp); + ret = dmap.symbols(); + fclose(fp); + } + delete [] obfr; + return ret; + } + return 0; + } + case SHT_SYMTAB: + case SHT_DYNSYM: { + size_t &esz = shdr->sh_type == SHT_SYMTAB ? sym_entsize : dyn_entsize; + esz = shdr->sh_entsize; + if( esz < sizeof(Elf64_Sym) ) return -1; + int strtab = shdr->sh_link; + if( strtab >= elf.e_shnum ) return -1; + Elf64_Shdr *strhdr = (Elf64_Shdr *)(shdrs + strtab * elf.e_shentsize); + int ssz = strhdr->sh_size; + if( ssz <= 0 ) return -1; + char *&str = shdr->sh_type == SHT_SYMTAB ? symstr : dynstr; + if( ldtbl(str, strhdr->sh_offset, ssz) ) return -1; + size_t &syz = shdr->sh_type == SHT_SYMTAB ? sym_sz : dyn_sz; + char *&tbl = shdr->sh_type == SHT_SYMTAB ? symtbl : dyntbl; + if( ldtbl(tbl, shdr->sh_offset, syz=shdr->sh_size) ) return -1; + int nsy = syz / esz, nsyms = 0; + /**/ printf("\n[section %3d] contains [%d] symbols @ 0x%016lx\n", num, nsy, pofs); + for( int i=0; ist_name, snm, sizeof(snm)); + int bind = ELF64_ST_BIND(sym->st_info); + switch( bind ) { + case STB_LOCAL: break; + case STB_GLOBAL: break; + case STB_WEAK: break; + default: continue; + } + int type = ELF32_ST_TYPE(sym->st_info); + switch( type ) { + case STT_FUNC: break; +/**/ case STT_OBJECT: break; // for r_debug + default: continue; + } + int ndx = sym->st_shndx; + if( ndx == SHN_UNDEF ) continue; + if( ndx >= SHN_LORESERVE ) continue; + addr_t adr = sym->st_value - pofs; + /**/ int tch = (type == STT_FUNC ? "tTW": "dDW")[bind]; + /**/ printf("%016lx %c %s\n", adr, tch, snm); + ++nsyms; + } + return nsyms; + } + default: + break; + } + return 0; +} + +int smap_t:: +init() +{ + if( fread(&elf, sizeof(elf), 1, sfp) != 1 || + memcmp(elf.e_ident, ELFMAG, SELFMAG) != 0 ) return -1; + // scan region map + int psz = elf.e_phentsize, pct = elf.e_phnum; + if( psz < (int) sizeof(Elf64_Phdr) || pct < 1 ) return -1; + if( fseek(sfp, elf.e_phoff, SEEK_SET) ) return -1; + chrbfr pbfr(psz); + Elf64_Phdr *phdr = (Elf64_Phdr *) pbfr.ptr(); + while( --pct >= 0 ) { + if( fread(pbfr, psz, 1, sfp) != 1 ) break; + if( phdr->p_type == PT_LOAD && phdr->p_offset == 0 ) { + vofs = phdr->p_vaddr; + pofs = phdr->p_paddr; + break; + } + } + // scan sections + int n = elf.e_shnum, esz = elf.e_shentsize; + if( n <= 0 || esz < (int)sizeof(Elf64_Shdr) ) return -1; + if( ldtbl(shdrs, elf.e_shoff, n*esz) ) return -1; + // load strtab + int sndx = elf.e_shstrndx; + if( sndx >= n ) return -1; + Elf64_Shdr *stbl = (Elf64_Shdr *)(shdrs + sndx * elf.e_shentsize); + return ldtbl(strtbl, stbl->sh_offset, stbl->sh_size); +} + +void smap_t:: +finit() +{ + delete [] strtbl; strtbl = 0; + delete [] shdrs; shdrs = 0; +} + +void smap_t:: +splt() +{ + if( !plts || !rels ) return; + if( !dynstr || !dyntbl ) return; + if( is_rela < 0 ) return; + int rel_entsize = is_rela ? sizeof(Elf64_Rela) : sizeof(Elf64_Rel); + int plt_entsize = 0x10; + unsigned char *bp = (unsigned char *)plts; + for( uint64_t plt_ofs=plt_entsize ; plt_ofsr_offset : rp->r_offset; + info = is_rela ? rap->r_info : rp->r_info; + addend = is_rela ? rap->r_addend : 0; + if( addr == ofs ) found = 1; + } + if( !found ) continue; + int sndx = ELF64_R_SYM(info); + uint64_t dyn_ofs = sndx * dyn_entsize; + if( dyn_ofs >= dyn_sz ) continue; + Elf64_Sym *sym = (Elf64_Sym *)(dyntbl + dyn_ofs); + const char *snm = dynstr + sym->st_name; + string pnm(snm); pnm += "@plt"; + if( addend ) { + char adn[64]; int op = addend>=0 ? '+' : '-'; + snprintf(adn,sizeof(adn),"%c%ld",op,addend>=0 ? addend : -addend); + pnm += adn; + } + addr_t adr = adr_plt + plt_ofs - pofs; + /**/ printf("%016lx %c %s\n", adr, 't', pnm.c_str()); + } +} + +int smap_t:: +symbols() +{ + if( init() >= 0 ) { + int n = elf.e_shnum; + for( int i=0; i + +void tst0() +{ + printf("."); +} + +void tst1() +{ + printf("\r"); +} + +void tst2(int n) +{ + volatile int i = n; + while( --i >= 0 ); + if( (n&1) != 0 ) tst1(); else tst0(); +} + +int main(int ac, char **av,char **ev) +{ + int i; + //profileStart(); + setbuf(stdout,NULL); + printf("ok. %s\n",ev[0]); + for( i=30000; --i>=0; ) tst2(i); + printf("\n"); + printf("done\n"); + return 0; +} +