Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>

typedef struct Data     Data;
typedef struct Pc       Pc;
typedef struct Acc      Acc;

struct Data
{
        ushort  down;
        ushort  right;
        ulong   pc;
        ulong   count;
        ulong   time;
};

struct Pc
{
        Pc      *next;
        ulong   pc;
};

struct Acc
{
        char    *name;
        ulong   pc;
        ulong   ms;
        ulong   calls;
};

Data*   data;
Acc*    acc;
ulong   ms;
long    nsym;
long    ndata;
int     dflag;
int     rflag;
Biobuf  bout;
int     tabstop = 4;
int     verbose;

void    syms(char*);
void    datas(char*);
void    graph(int, ulong, Pc*);
void    plot(void);
char*   name(ulong);
void    indent(int);
char*   defaout(void);

void
main(int argc, char *argv[])
{
        char *s;

        s = getenv("tabstop");
        if(s!=nil && strtol(s,0,0)>0)
                tabstop = strtol(s,0,0);
        ARGBEGIN{
        case 'v':
                verbose = 1;
                break;
        case 'd':
                dflag = 1;
                break;
        case 'r':
                rflag = 1;
                break;
        default:
                fprint(2, "usage: prof [-dr] [8.out] [prof.out]\n");
                exits("usage");
        }ARGEND
        Binit(&bout, 1, OWRITE);
        if(argc > 0)
                syms(argv[0]);
        else
                syms(defaout());
        if(argc > 1)
                datas(argv[1]);
        else
                datas("prof.out");
        if(ndata){
                if(dflag)
                        graph(0, data[0].down, 0);
                else
                        plot();
        }
        exits(0);
}

void
swapdata(Data *dp)
{
        dp->down = beswab(dp->down);
        dp->right = beswab(dp->right);
        dp->pc = beswal(dp->pc);
        dp->count = beswal(dp->count);
        dp->time = beswal(dp->time);
}

int
acmp(void *va, void *vb)
{
        Acc *a, *b;
        ulong ua, ub;

        a = va;
        b = vb;
        ua = a->ms;
        ub = b->ms;

        if(ua > ub)
                return 1;
        if(ua < ub)
                return -1;
        return 0;
}

void
syms(char *cout)
{
        Fhdr f;
        int fd;

        if((fd = open(cout, 0)) < 0){
                perror(cout);
                exits("open");
        }
        if (!crackhdr(fd, &f)) {
                fprint(2, "can't read text file header\n");
                exits("read");
        }
        if (f.type == FNONE) {
                fprint(2, "text file not an a.out\n");
                exits("file type");
        }
        if (syminit(fd, &f) < 0) {
                fprint(2, "syminit: %r\n");
                exits("syms");
        }
        close(fd);
}

void
datas(char *dout)
{
        int fd;
        Dir *d;
        int i;

        if((fd = open(dout, 0)) < 0){
                perror(dout);
                exits("open");
        }
        d = dirfstat(fd);
        if(d == nil){
                perror(dout);
                exits("stat");
        }
        ndata = d->length/sizeof(data[0]);
        data = malloc(ndata*sizeof(Data));
        if(data == 0){
                fprint(2, "prof: can't malloc data\n");
                exits("data malloc");
        }
        if(read(fd, data, d->length) != d->length){
                fprint(2, "prof: can't read data file\n");
                exits("data read");
        }
        free(d);
        close(fd);
        for (i = 0; i < ndata; i++)
                swapdata(data+i);
}

char*
name(ulong pc)
{
        Symbol s;
        static char buf[16];

        if (findsym(pc, CTEXT, &s))
                return(s.name);
        snprint(buf, sizeof(buf), "#%lux", pc);
        return buf;
}

void
graph(int ind, ulong i, Pc *pc)
{
        long time, count, prgm;
        Pc lpc;

        if(i >= ndata){
                fprint(2, "prof: index out of range %ld [max %ld]\n", i, ndata);
                return;
        }
        count = data[i].count;
        time = data[i].time;
        prgm = data[i].pc;
        if(time < 0)
                time += data[0].time;
        if(data[i].right != 0xFFFF)
                graph(ind, data[i].right, pc);
        indent(ind);
        if(count == 1)
                Bprint(&bout, "%s:%lud\n", name(prgm), time);
        else
                Bprint(&bout, "%s:%lud/%lud\n", name(prgm), time, count);
        if(data[i].down == 0xFFFF)
                return;
        lpc.next = pc;
        lpc.pc = prgm;
        if(!rflag){
                while(pc){
                        if(pc->pc == prgm){
                                indent(ind+1);
                                Bprint(&bout, "...\n");
                                return;
                        }
                        pc = pc->next;
                }
        }
        graph(ind+1, data[i].down, &lpc);
}
/*
 *      assume acc is ordered by increasing text address.
 */
long
symind(ulong pc)
{
        int top, bot, mid;

        bot = 0;
        top = nsym;
        for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
                if (pc < acc[mid].pc)
                        top = mid;
                else
                if (mid != nsym-1 && pc >= acc[mid+1].pc)
                        bot = mid;
                else
                        return mid;
        }
        return -1;
}

ulong
sum(ulong i)
{
        long j, dtime, time;
        int k;
        static indent;

        if(i >= ndata){
                fprint(2, "prof: index out of range %ld [max %ld]\n", i, ndata);
                return 0;
        }
        j = symind(data[i].pc);
        time = data[i].time;
        if(time < 0)
                time += data[0].time;
        if (verbose){
                for(k = 0; k < indent; k++)
                        print(" ");
                print("%lud: %ld/%lud", i, data[i].time, data[i].count);
                if (j >= 0)
                        print(" %s\n", acc[j].name);
                else
                        print(" 0x%lux\n", data[i].pc);
        }
        dtime = 0;
        if(data[i].down != 0xFFFF){
                indent++;
                dtime = sum(data[i].down);
                indent--;
        }
        j = symind(data[i].pc);
        if (j >= 0) {
                acc[j].ms += time - dtime;
                ms += time - dtime;
                acc[j].calls += data[i].count;
        }
        if(data[i].right == 0xFFFF)
                return time;
        return time + sum(data[i].right);
}

void
plot(void)
{
        Symbol s;

        for (nsym = 0; textsym(&s, nsym); nsym++) {
                acc = realloc(acc, (nsym+1)*sizeof(Acc));
                if(acc == 0){
                        fprint(2, "prof: malloc fail\n");
                        exits("acc malloc");
                }
                acc[nsym].name = s.name;
                acc[nsym].pc = s.value;
                acc[nsym].calls = acc[nsym].ms = 0;
        }
        sum(data[0].down);
        qsort(acc, nsym, sizeof(Acc), acmp);
        Bprint(&bout, "  %%     Time     Calls  Name\n");
        if(ms == 0)
                ms = 1;
        while (--nsym >= 0) {
                if(acc[nsym].calls)
                        Bprint(&bout, "%4.1f %8.3f %8lud\t%s\n",
                                (100.0*acc[nsym].ms)/ms,
                                acc[nsym].ms/1000.0,
                                acc[nsym].calls,
                                acc[nsym].name);
        }
}

void
indent(int ind)
{
        int j;

        j = 2*ind;
        while(j >= tabstop){
                Bwrite(&bout, ".\t", 2);
                j -= tabstop;
        }
        if(j)
                Bwrite(&bout, ".                            ", j);
}

char*   trans[] =
{
        "386",          "8.out",
        "68020",                "2.out",
        "alpha",                "7.out",
        "amd64",        "6.out",
        "arm",          "5.out",
        "mips",         "v.out",
        "power",                "q.out",
        "sparc",                "k.out",
        "spim",         "0.out",
        0,0
};

char*
defaout(void)
{
        char *p;
        int i;

        p = getenv("objtype");
        if(p)
        for(i=0; trans[i]; i+=2)
                if(strcmp(p, trans[i]) == 0)
                        return trans[i+1];
        return trans[1];
}