/* * Performance counters */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "amd64.h" #include "pmc.h" enum{ Qdir = 0, Qgctl, Qcore, Qctr, Qdata, Qctl, PmcCtlRdStr = 4*1024, }; #define PMCTYPE(x) (((unsigned)x)&0xffful) #define PMCID(x) (((unsigned)x)>>12) #define PMCQID(i, t) ((((unsigned)i)<<12)|(t)) Dirtab *pmctab; static int npmctab; Dirtab *toptab; static int ntoptab; int pmcdebug; static void topdirinit(int ncores) { int i; Dirtab *d; ntoptab = 2 + ncores; toptab = malloc(ntoptab * sizeof(Dirtab)); if (toptab == nil) return; d = toptab; strncpy(d->name, ".", KNAMELEN); mkqid(&d->qid, Qdir, 0, QTDIR); d->perm = DMDIR|0555; d++; strncpy(d->name, "ctrdesc", KNAMELEN); mkqid(&d->qid, Qgctl, 0, 0); d->perm = 0444; for (i = 2; i < ncores + 2; i++) { d = &toptab[i]; snprint(d->name, KNAMELEN, "core%4.4ud", i - 2); mkqid(&d->qid, PMCQID(i - 2, Qcore), 0, QTDIR); d->perm = DMDIR|0555; } } static void ctrdirinit(void) { int nr, i; Dirtab *d; nr = pmcnregs(); npmctab = 1 + 2*nr; pmctab = malloc(npmctab * sizeof(Dirtab)); if (pmctab == nil){ free(toptab); toptab = nil; return; } d = pmctab; strncpy(d->name, ".", KNAMELEN); mkqid(&d->qid, Qctr, 0, QTDIR); d->perm = DMDIR|0555; for (i = 1; i < nr + 1; i++) { d = &pmctab[i]; snprint(d->name, KNAMELEN, "ctr%2.2ud", i - 1); mkqid(&d->qid, PMCQID(i - 1, Qdata), 0, 0); d->perm = 0600; d = &pmctab[nr + i]; snprint(d->name, KNAMELEN, "ctr%2.2udctl", i - 1); mkqid(&d->qid, PMCQID(i - 1, Qctl), 0, 0); d->perm = 0600; } } static void pmcnull(PmcCtl *p) { memset(p, 0xff, sizeof(PmcCtl)); p->enab = PmcCtlNullval; p->user = PmcCtlNullval; p->os = PmcCtlNullval; p->reset = PmcCtlNullval; p->nodesc = 1; } static void pmcinit(void) { int i, j, ncores, nr; Mach *mp; _pmcupdate = pmcupdate; ncores = 0; nr = pmcnregs(); for(i = 0; i < MACHMAX; i++) if((mp = sys->machptr[i]) != nil && mp->online){ ncores++; for(j = 0; j < nr; j++) pmcnull(&mp->pmc[j]); } topdirinit(ncores); ctrdirinit(); } static Chan * pmcattach(char *spec) { if (pmctab == nil) error(Enomem); return devattach(L'ε', spec); } int pmcgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp) { int t, i, n; Dirtab *l, *d; if(s == DEVDOTDOT){ devdir(c, (Qid){Qdir, 0, QTDIR}, "#ε", 0, eve, 0555, dp); c->aux = nil; return 1; } /* first, for directories, generate children */ switch((int)PMCTYPE(c->qid.path)){ case Qdir: return devgen(c, name, toptab, ntoptab, s, dp); case Qctr: return devgen(c, name, pmctab, npmctab, s, dp); case Qcore: c->aux = (void *)PMCID(c->qid.path); /* core no */ return devgen(c, name, pmctab, npmctab, s, dp); default: if(s != 0) return -1; t = PMCTYPE(c->qid.path); if(t < Qctr){ i = t; l = toptab; n = ntoptab; }else{ i = PMCID(t); if (t == Qctl) i += (npmctab - 1)/2; l = pmctab; n = npmctab; } if(i >=n) return -1; d = &l[i]; devdir(c, d->qid, d->name, d->length, eve, d->perm, dp); return 1; } } static Walkqid* pmcwalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, nil, 0, pmcgen); } static long pmcstat(Chan *c, uchar *dp, long n) { return devstat(c, dp, n, nil, 0, pmcgen); } static Chan* pmcopen(Chan *c, int omode) { if (!iseve()) error(Eperm); return devopen(c, omode, nil, 0, pmcgen); } static void pmcclose(Chan *) { } static long pmcread(Chan *c, void *a, long n, vlong offset) { ulong type, id; PmcCtl p; char *s; u64int v; u64int coreno; type = PMCTYPE(c->qid.path); id = PMCID(c->qid.path); switch(type){ case Qcore: case Qdir: case Qctr: return devdirread(c, a, n, nil, 0, pmcgen); } s = malloc(PmcCtlRdStr); if(waserror()){ free(s); nexterror(); } coreno = (u64int)c->aux; p.coreno = coreno; switch(type){ case Qdata: v = pmcgetctr(coreno, id); snprint(s, PmcCtlRdStr, "%#ullx", v); break; case Qctl: if (pmcgetctl(coreno, &p, id) < 0) error("bad ctr"); if (pmcctlstr(s, PmcCtlRdStr, &p) < 0) error("bad pmc"); break; case Qgctl: if (pmcdescstr(s, PmcCtlRdStr) < 0) error("bad pmc"); break; default: error(Eperm); } n = readstr(offset, a, n, s); free(s); poperror(); return n; } enum{ Enable, Disable, User, Os, NoUser, NoOs, Reset, Debug, }; static Cmdtab pmcctlmsg[] = { Enable, "enable", 0, Disable, "disable", 0, User, "user", 0, Os, "os", 0, NoUser, "nouser", 0, NoOs, "noos", 0, Reset, "reset", 0, Debug, "debug", 0, }; typedef void (*APfunc)(void); typedef struct AcPmcArg AcPmcArg; struct AcPmcArg { int regno; int coreno; PmcCtl; }; typedef struct AcCtrArg AcCtrArg; struct AcCtrArg { int regno; int coreno; u64int v; }; void acpmcsetctl(void) { AcPmcArg p; Mach *mp; mp = up->ac; memmove(&p, mp->icc->data, sizeof(AcPmcArg)); mp->icc->rc = pmcsetctl(p.coreno, &p, p.regno); return; } void acpmcsetctr(void) { AcCtrArg ctr; Mach *mp; mp = up->ac; memmove(&ctr, mp->icc->data, sizeof(AcCtrArg)); mp->icc->rc = pmcsetctr(ctr.coreno, ctr.v, ctr.regno); return; } static long pmcwrite(Chan *c, void *a, long n, vlong) { Cmdbuf *cb; Cmdtab *ct; ulong type; char str[64]; /* 0x0000000000000000\0 */ AcPmcArg p; AcCtrArg ctr; u64int coreno; Mach *mp; if (c->qid.type == QTDIR) error(Eperm); if (c->qid.path == Qgctl) error(Eperm); if (n >= sizeof(str)) error(Ebadctl); pmcnull(&p); coreno = (u64int)c->aux; p.coreno = coreno; type = PMCTYPE(c->qid.path); p.regno = PMCID(c->qid.path); memmove(str, a, n); str[n] = '\0'; mp = up->ac; ctr.coreno = coreno; ctr.regno = p.regno; if (type == Qdata) { /* I am a handler for a proc in the core, run an RPC*/ if (mp != nil && mp->machno == coreno) { if (runac(mp, acpmcsetctr, 0, &ctr, sizeof(AcCtrArg)) < 0) n = -1; } else { if (pmcsetctr(coreno, strtoull(str, 0, 0), p.regno) < 0) n = -1; } return n; } /* TODO: should iterate through multiple lines */ if (strncmp(str, "set ", 4) == 0){ memmove(p.descstr, (char *)str + 4, n - 4); p.descstr[n - 4] = '\0'; p.nodesc = 0; } else { cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, pmcctlmsg, nelem(pmcctlmsg)); switch(ct->index){ case Enable: p.enab = 1; break; case Disable: p.enab = 0; break; case User: p.user = 1; break; case Os: p.os = 1; break; case NoUser: p.user = 0; break; case NoOs: p.os = 0; break; case Reset: p.reset = 1; break; case Debug: pmcdebug = ~pmcdebug; break; default: cmderror(cb, "invalid ctl"); break; } free(cb); poperror(); } /* I am a handler for a proc in the core, run an RPC*/ if (mp != nil && mp->machno == coreno) { if (runac(mp, acpmcsetctl, 0, &p, sizeof(AcPmcArg)) < 0) n = -1; } else { if (pmcsetctl(coreno, &p, p.regno) < 0) n = -1; } return n; } Dev pmcdevtab = { L'ε', "pmc", pmcinit, devinit, devshutdown, pmcattach, pmcwalk, pmcstat, pmcopen, devcreate, pmcclose, pmcread, devbread, pmcwrite, devbwrite, devremove, devwstat, };