389 lines
7.3 KiB
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://code.9front.org/hg/plan9front/ 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,
|
|
};
|