Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include        "u.h"
#include        "../port/lib.h"
#include        "mem.h"
#include        "dat.h"
#include        "fns.h"
#include        "../port/error.h"


#define LRES    3               /* log of PC resolution */
#define SZ      4               /* sizeof of count cell; well known as 4 */

struct
{
        int     minpc;
        int     maxpc;
        int     nbuf;
        int     time;
        ulong   *buf;
}kprof;

enum{
        Kprofdirqid,
        Kprofdataqid,
        Kprofctlqid,
};
Dirtab kproftab[]={
        ".",    {Kprofdirqid, 0, QTDIR},                0,      DMDIR|0550,
        "kpdata",       {Kprofdataqid},         0,      0600,
        "kpctl",        {Kprofctlqid},          0,      0600,
};

static void
_kproftimer(ulong pc)
{
        extern void spldone(void);

        if(kprof.time == 0)
                return;
        /*
         *  if the pc is coming out of spllo or splx,
         *  use the pc saved when we went splhi.
         */
        if(pc>=(ulong)spllo && pc<=(ulong)spldone)
                pc = m->splpc;

        kprof.buf[0] += TK2MS(1);
        if(kprof.minpc<=pc && pc<kprof.maxpc){
                pc -= kprof.minpc;
                pc >>= LRES;
                kprof.buf[pc] += TK2MS(1);
        }else
                kprof.buf[1] += TK2MS(1);
}

static void
kprofinit(void)
{
        if(SZ != sizeof kprof.buf[0])
                panic("kprof size");
        kproftimer = _kproftimer;
}

static Chan*
kprofattach(char *spec)
{
        ulong n;

        /* allocate when first used */
        kprof.minpc = KTZERO;
        kprof.maxpc = (ulong)etext;
        kprof.nbuf = (kprof.maxpc-kprof.minpc) >> LRES;
        n = kprof.nbuf*SZ;
        if(kprof.buf == 0) {
                kprof.buf = xalloc(n);
                if(kprof.buf == 0)
                        error(Enomem);
        }
        kproftab[1].length = n;
        return devattach('K', spec);
}

static Walkqid*
kprofwalk(Chan *c, Chan *nc, char **name, int nname)
{
        return devwalk(c, nc, name, nname, kproftab, nelem(kproftab), devgen);
}

static int
kprofstat(Chan *c, uchar *db, int n)
{
        return devstat(c, db, n, kproftab, nelem(kproftab), devgen);
}

static Chan*
kprofopen(Chan *c, int omode)
{
        if(c->qid.type == QTDIR){
                if(omode != OREAD)
                        error(Eperm);
        }
        c->mode = openmode(omode);
        c->flag |= COPEN;
        c->offset = 0;
        return c;
}

static void
kprofclose(Chan*)
{
}

static long
kprofread(Chan *c, void *va, long n, vlong off)
{
        ulong end;
        ulong w, *bp;
        uchar *a, *ea;
        ulong offset = off;

        switch((int)c->qid.path){
        case Kprofdirqid:
                return devdirread(c, va, n, kproftab, nelem(kproftab), devgen);

        case Kprofdataqid:
                end = kprof.nbuf*SZ;
                if(offset & (SZ-1))
                        error(Ebadarg);
                if(offset >= end){
                        n = 0;
                        break;
                }
                if(offset+n > end)
                        n = end-offset;
                n &= ~(SZ-1);
                a = va;
                ea = a + n;
                bp = kprof.buf + offset/SZ;
                while(a < ea){
                        w = *bp++;
                        *a++ = w>>24;
                        *a++ = w>>16;
                        *a++ = w>>8;
                        *a++ = w>>0;
                }
                break;

        default:
                n = 0;
                break;
        }
        return n;
}

static long
kprofwrite(Chan *c, void *a, long n, vlong)
{
        switch((int)(c->qid.path)){
        case Kprofctlqid:
                if(strncmp(a, "startclr", 8) == 0){
                        memset((char *)kprof.buf, 0, kprof.nbuf*SZ);
                        kprof.time = 1;
                }else if(strncmp(a, "start", 5) == 0)
                        kprof.time = 1;
                else if(strncmp(a, "stop", 4) == 0)
                        kprof.time = 0;
                break;
        default:
                error(Ebadusefd);
        }
        return n;
}

Dev kprofdevtab = {
        'K',
        "kprof",

        devreset,
        kprofinit,
        devshutdown,
        kprofattach,
        kprofwalk,
        kprofstat,
        kprofopen,
        devcreate,
        kprofclose,
        kprofread,
        devbread,
        kprofwrite,
        devbwrite,
        devremove,
        devwstat,
};