jehanne/sys/src/kern/amd64/devpmc.c

389 lines
7.3 KiB
C

/* Copyright (C) Charles Forsyth
* See /doc/license/NOTICE.Plan9-9k.txt for details about the licensing.
*/
/* Portions of this file are Copyright (C) 9front's team.
* See /doc/license/9front-mit for details about the licensing.
* See http://git.9front.org/plan9front/plan9front/HEAD/info.html for a list of authors.
*/
/*
* Performance counters
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "pmc.h"
enum{
Qdir = 0,
Qdesc,
Qcore,
PmcCtlRdStr = 4*1024,
};
#define PMCTYPE(x) (((unsigned)x)&0xffful)
#define PMCID(x) (((unsigned)x)>>12)
#define PMCQID(i, t) ((((unsigned)i)<<12)|(t))
static Dirtab *toptab;
static Lock toptablck;
static int ntoptab;
int pmcdebug;
static void
topdirinit(void)
{
Dirtab *d;
int nent;
nent = 1 + MACHMAX;
toptab = jehanne_mallocz(nent * sizeof(Dirtab), 1);
if (toptab == nil)
return;
d = toptab;
jehanne_strncpy(d->name, "ctrdesc", KNAMELEN);
mkqid(&d->qid, Qdesc, 0, 0);
d->perm = 0440;
}
static int
corefilesinit(void)
{
int i, nc, newn;
Dirtab *d;
Mach *mp;
nc = 0;
lock(&toptablck);
for(i = 0; i < MACHMAX; i++) {
if((mp = sys->machptr[i]) != nil && mp->online != 0){
d = &toptab[nc + 1];
/* if you take them out, be careful in pmcgen too */
if(d->name[0] != '\0'){
if(PMCQID(i, Qcore) == d->qid.path){
nc++;
continue;
}else{
/* a new one appeared, make space, should almost never happen */
jehanne_memmove(d + 1, d, (MACHMAX - i)*sizeof(*d));
jehanne_memset(d, 0, sizeof(*d));
}
}
jehanne_snprint(d->name, KNAMELEN, "core%4.4ud", i);
mkqid(&d->qid, PMCQID(i, Qcore), 0, 0);
d->perm = 0660;
nc++;
}
}
newn = 1 + nc;
ntoptab = newn;
unlock(&toptablck);
return newn;
}
static void
pmcinit(void)
{
pmcconfigure();
topdirinit();
corefilesinit();
}
static Chan *
pmcattach(Chan *c, Chan *ac, char *spec, int flags)
{
corefilesinit();
return devattach(L'ε', spec);
}
int
pmcgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
{
int ntab;
Dirtab *d;
ntab = corefilesinit();
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:
case Qcore:
if(s >= ntab)
return -1;
d = &toptab[s];
devdir(c, d->qid, d->name, d->length, eve, d->perm, dp);
return 1;
default:
return -1;
}
}
static Walkqid*
pmcwalk(Chan *c, Chan *nc, char **name, int nname)
{
if(PMCTYPE(c->qid.path) == Qcore)
c->aux = (void *)PMCID(c->qid.path); /* core no */
return devwalk(c, nc, name, nname, nil, 0, pmcgen);
}
static long
pmcstat(Chan *c, uint8_t *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 int
pmcctlstr(char *str, int nstr, PmcCtl *p, int64_t v)
{
int ns;
ns = 0;
ns += jehanne_snprint(str + ns, nstr - ns, "%#ullx ", v);
if (p->enab && p->enab != PmcCtlNullval)
ns += jehanne_snprint(str + ns, nstr - ns, "on ");
else
ns += jehanne_snprint(str + ns, nstr - ns, "off ");
if (p->user && p->user != PmcCtlNullval)
ns += jehanne_snprint(str + ns, nstr - ns, "user ");
else
ns += jehanne_snprint(str + ns, nstr - ns, "nouser ");
if (p->os && p->user != PmcCtlNullval)
ns += jehanne_snprint(str + ns, nstr - ns, "os ");
else
ns += jehanne_snprint(str + ns, nstr - ns, "noos ");
/* TODO, inverse pmctrans? */
if(!p->nodesc)
ns += jehanne_snprint(str + ns, nstr - ns, "%s", p->descstr);
else
ns += jehanne_snprint(str + ns, nstr - ns, "no desc");
ns += jehanne_snprint(str + ns, nstr - ns, "\n");
return ns;
}
/* this should be safe to use even if there is no core anymore */
static long
pmcread(Chan *c, void *a, long n, int64_t offset)
{
uint32_t type;
PmcCtl p;
char *s;
uint64_t v;
uint64_t coreno;
int nr, i, ns, nn;
type = PMCTYPE(c->qid.path);
coreno = PMCID(c->qid.path);
if(type == Qdir)
return devdirread(c, a, n, nil, 0, pmcgen);
s = jehanne_malloc(PmcCtlRdStr);
if(waserror()){
jehanne_free(s);
nexterror();
}
p.coreno = coreno;
nr = pmcnregs();
switch(type){
case Qcore:
ns = 0;
for(i = 0; i < nr; i ++){
if (pmcgetctl(coreno, &p, i) < 0)
error("bad ctr");
if(! p.enab)
continue;
v = pmcgetctr(coreno, i);
ns += jehanne_snprint(s + ns, PmcCtlRdStr - ns, "%2.2ud ", i);
nn = pmcctlstr(s + ns, PmcCtlRdStr - ns, &p, v);
if (n < 0)
error("bad pmc");
ns += nn;
}
break;
case Qdesc:
if (pmcdescstr(s, PmcCtlRdStr) < 0)
error("bad pmc");
break;
default:
error(Eperm);
}
n = readstr(offset, a, n, s);
jehanne_free(s);
poperror();
return n;
}
static int
isset(char *str)
{
return jehanne_strncmp(str, "-", 2) != 0;
}
static int
pickregno(int coreno)
{
PmcCtl p;
int nr, i;
nr = pmcnregs();
for(i = 0; i < nr; i++){
if (pmcgetctl(coreno, &p, i) || p.enab)
continue;
return i;
}
return -1;
}
static int
fillctl(PmcCtl *p, Cmdbuf *cb, int start, int end)
{
int i;
if(end > cb->nf -1)
end = cb->nf -1;
for(i = start; i <= end; i++){
if(pmcdebug != 0)
jehanne_print("setting field %d to %s\n", i, cb->f[i]);
if(!isset(cb->f[i]))
continue;
else if(jehanne_strcmp("on", cb->f[i]) == 0)
p->enab = 1;
else if(jehanne_strcmp("off", cb->f[i]) == 0)
p->enab = 0;
else if(jehanne_strcmp("user", cb->f[i]) == 0)
p->user = 1;
else if(jehanne_strcmp("os", cb->f[i]) == 0)
p->os = 1;
else if(jehanne_strcmp("nouser", cb->f[i]) == 0)
p->user = 0;
else if(jehanne_strcmp("noos", cb->f[i]) == 0)
p->os = 0;
else
error("bad ctl");
}
return 0;
}
/* this should be safe to use even if there is no core anymore */
static long
pmcwrite(Chan *c, void *a, long n, int64_t)
{
Cmdbuf *cb;
uint64_t coreno;
int regno, i, ns;
PmcCtl p;
char *s;
if (c->qid.type == QTDIR)
error(Eperm);
if (c->qid.path == Qdesc)
error(Eperm);
coreno = PMCID(c->qid.path);;
p.coreno = coreno;
/* TODO, multiple lines? */
cb = parsecmd(a, n);
if(waserror()){
jehanne_free(cb);
nexterror();
}
if(cb->nf < 1)
error("short ctl");
if(jehanne_strcmp("debug", cb->f[0]) == 0)
pmcdebug = ~pmcdebug;
else{
if(cb->nf < 2)
error("short ctl");
if(!isset(cb->f[0])){
/* racy, it does not reserve the core */
regno = pickregno(coreno);
if(regno < 0)
error("no free regno");
if(pmcdebug != 0)
jehanne_print("picked regno %d\n", regno);
}else{
regno = jehanne_strtoull(cb->f[0], 0, 0);
if(regno > pmcnregs())
error("ctr number too big");
if(pmcdebug != 0)
jehanne_print("setting regno %d\n", regno);
}
if(isset(cb->f[1]))
pmcsetctr(coreno, jehanne_strtoull(cb->f[1], 0, 0), regno);
pmcinitctl(&p);
fillctl(&p, cb, 2, 4);
ns = 0;
s = p.descstr;
s[0] = '\0';
for(i = 5; i < cb->nf; i++){
if(!isset(cb->f[i]))
continue;
ns += jehanne_snprint(s + ns, KNAMELEN - ns, "%s ", cb->f[i]);
p.nodesc = 0;
}
if(pmcdebug != 0)
jehanne_print("setting desc to %s\n", p.descstr);
pmcsetctl(coreno, &p, regno);
}
jehanne_free(cb);
poperror();
return n;
}
Dev pmcdevtab = {
L'ε',
"pmc",
pmcinit,
devinit,
devshutdown,
pmcattach,
pmcwalk,
pmcstat,
pmcopen,
devcreate,
pmcclose,
pmcread,
devbread,
pmcwrite,
devbwrite,
devremove,
devwstat,
};