ref: 21ed259dcd8fcbbe93c78c5958a48176b6953e7a
dir: /nstats.c/
#include <u.h>
#include <libc.h>
#include <ctype.h>
#define MAXNUM 10 /* maximum number of numbers on data line */
typedef struct Graph Graph;
typedef struct Machine Machine;
struct Graph
{
char *label;
void (*newvalue)(Machine*, uvlong*, uvlong*, int);
Machine *mach;
};
enum
{
/* /dev/swap */
Mem = 0,
Maxmem,
Swap,
Maxswap,
Reclaim,
Maxreclaim,
Kern,
Maxkern,
Draw,
Maxdraw,
/* /dev/sysstats */
Procno = 0,
Context,
Interrupt,
Syscall,
Fault,
TLBfault,
TLBpurge,
Load,
Idle,
InIntr,
/* /net/ether0/stats */
In = 0,
Link,
Out,
Err0,
};
struct Machine
{
char *name;
char *shortname;
int remote;
int statsfd;
int swapfd;
int etherfd[10];
int batteryfd;
int bitsybatfd;
int tempfd;
int disable;
uvlong devswap[10];
uvlong devsysstat[10];
uvlong prevsysstat[10];
int nproc;
int lgproc;
uvlong netetherstats[8];
uvlong prevetherstats[8];
uvlong batterystats[2];
uvlong temp[10];
/* big enough to hold /dev/sysstat even with many processors */
char buf[8*1024];
char *bufp;
char *ebufp;
};
enum Menu2
{
Mbattery,
Mcontext,
Mether,
Methererr,
Metherin,
Metherout,
Mfault,
Midle,
Minintr,
Mintr,
Mload,
Mmem,
Mswap,
Mreclaim,
Mkern,
Mdraw,
Msyscall,
Mtlbmiss,
Mtlbpurge,
Mtemp,
Nmenu2,
};
char *labels[Nmenu2+1] = {
"battery",
"context",
"ether",
"ethererr",
"etherin",
"etherout",
"fault",
"idle",
"inintr",
"intr",
"load",
"mem",
"swap",
"reclaim",
"kern",
"draw",
"syscall",
"tlbmiss",
"tlbpurge",
"temp",
nil,
};
int padlabels = 10;
void contextval(Machine*, uvlong*, uvlong*, int),
etherval(Machine*, uvlong*, uvlong*, int),
ethererrval(Machine*, uvlong*, uvlong*, int),
etherinval(Machine*, uvlong*, uvlong*, int),
etheroutval(Machine*, uvlong*, uvlong*, int),
faultval(Machine*, uvlong*, uvlong*, int),
intrval(Machine*, uvlong*, uvlong*, int),
inintrval(Machine*, uvlong*, uvlong*, int),
loadval(Machine*, uvlong*, uvlong*, int),
idleval(Machine*, uvlong*, uvlong*, int),
memval(Machine*, uvlong*, uvlong*, int),
swapval(Machine*, uvlong*, uvlong*, int),
reclaimval(Machine*, uvlong*, uvlong*, int),
kernval(Machine*, uvlong*, uvlong*, int),
drawval(Machine*, uvlong*, uvlong*, int),
syscallval(Machine*, uvlong*, uvlong*, int),
tlbmissval(Machine*, uvlong*, uvlong*, int),
tlbpurgeval(Machine*, uvlong*, uvlong*, int),
batteryval(Machine*, uvlong*, uvlong*, int),
tempval(Machine*, uvlong*, uvlong*, int);
int present[Nmenu2];
void (*newvaluefn[Nmenu2])(Machine*, uvlong*, uvlong*, int init) = {
batteryval,
contextval,
etherval,
ethererrval,
etherinval,
etheroutval,
faultval,
idleval,
inintrval,
intrval,
loadval,
memval,
swapval,
reclaimval,
kernval,
drawval,
syscallval,
tlbmissval,
tlbpurgeval,
tempval,
};
Graph *graph;
Machine *mach;
char *mysysname;
char argchars[] = "8bcdeEfiIkmlnprstwz";
int nmach;
int ngraph; /* totaly number is ngraph*nmach */
int sleeptime = 1000;
int batteryperiod = 1000;
int tempperiod = 1000;
void
killall(char *s)
{
exits(s);
}
void*
emalloc(ulong sz)
{
void *v;
v = malloc(sz);
if(v == nil) {
fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
killall("mem");
}
memset(v, 0, sz);
return v;
}
void*
erealloc(void *v, ulong sz)
{
v = realloc(v, sz);
if(v == nil) {
fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
killall("mem");
}
return v;
}
char*
estrdup(char *s)
{
char *t;
if((t = strdup(s)) == nil) {
fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
killall("mem");
}
return t;
}
int
loadbuf(Machine *m, int *fd)
{
int n;
if(*fd < 0)
return 0;
seek(*fd, 0, 0);
n = read(*fd, m->buf, sizeof m->buf-1);
if(n <= 0){
close(*fd);
*fd = -1;
return 0;
}
m->bufp = m->buf;
m->ebufp = m->buf+n;
m->buf[n] = 0;
return 1;
}
/* read one line of text from buffer and process integers */
int
readnums(Machine *m, int n, uvlong *a, int spanlines)
{
int i;
char *p, *ep;
if(spanlines)
ep = m->ebufp;
else
for(ep=m->bufp; ep<m->ebufp; ep++)
if(*ep == '\n')
break;
p = m->bufp;
for(i=0; i<n && p<ep; i++){
while(p<ep && (!isascii(*p) || !isdigit(*p)) && *p!='-')
p++;
if(p == ep)
break;
a[i] = strtoull(p, &p, 10);
}
if(ep < m->ebufp)
ep++;
m->bufp = ep;
return i == n;
}
int
readswap(Machine *m, uvlong *a)
{
static int xxx = 0;
if(strstr(m->buf, "memory\n")){
/* new /dev/swap - skip first 3 numbers */
if(!readnums(m, 7, a, 1))
return 0;
a[Mem] = a[3];
a[Maxmem] = a[4];
a[Swap] = a[5];
a[Maxswap] = a[6];
a[Reclaim] = 0;
a[Maxreclaim] = 0;
if(m->bufp = strstr(m->buf, "reclaim")){
while(m->bufp > m->buf && m->bufp[-1] != '\n')
m->bufp--;
a[Reclaim] = strtoull(m->bufp, &m->bufp, 10);
while(*m->bufp++ == '/')
a[Maxreclaim] = strtoull(m->bufp, &m->bufp, 10);
}
a[Kern] = 0;
a[Maxkern] = 0;
if(m->bufp = strstr(m->buf, "kernel malloc")){
while(m->bufp > m->buf && m->bufp[-1] != '\n')
m->bufp--;
a[Kern] = strtoull(m->bufp, &m->bufp, 10);
while(*m->bufp++ == '/')
a[Maxkern] = strtoull(m->bufp, &m->bufp, 10);
}
a[Draw] = 0;
a[Maxdraw] = 0;
if(m->bufp = strstr(m->buf, "kernel draw")){
while(m->bufp > m->buf && m->bufp[-1] != '\n')
m->bufp--;
a[Draw] = strtoull(m->bufp, &m->bufp, 10);
while(*m->bufp++ == '/')
a[Maxdraw] = strtoull(m->bufp, &m->bufp, 10);
}
return 1;
}
a[Reclaim] = 0;
a[Maxreclaim] = 0;
a[Kern] = 0;
a[Maxkern] = 0;
a[Draw] = 0;
a[Maxdraw] = 0;
return readnums(m, 4, a, 0);
}
char*
shortname(char *s)
{
char *p, *e;
p = estrdup(s);
e = strchr(p, '.');
if(e)
*e = 0;
return p;
}
int
ilog10(uvlong j)
{
int i;
for(i = 0; j >= 10; i++)
j /= 10;
return i;
}
int
initmach(Machine *m, char *name)
{
int n, i, j, fd;
uvlong a[MAXNUM];
char *p, mpt[256], buf[256];
Dir *d;
p = strchr(name, '!');
if(p)
p++;
else
p = name;
m->name = estrdup(p);
m->shortname = shortname(p);
m->remote = (strcmp(p, mysysname) != 0);
if(m->remote == 0)
strcpy(mpt, "");
else{
Waitmsg *w;
int pid;
snprint(mpt, sizeof mpt, "/n/%s", p);
pid = fork();
switch(pid){
case -1:
fprint(2, "can't fork: %r\n");
return 0;
case 0:
execl("/bin/rimport", "rimport", name, "/", mpt, nil);
fprint(2, "can't exec: %r\n");
exits("exec");
}
w = wait();
if(w == nil || w->pid != pid || w->msg[0] != '\0'){
free(w);
return 0;
}
free(w);
}
snprint(buf, sizeof buf, "%s/dev/swap", mpt);
m->swapfd = open(buf, OREAD);
if(loadbuf(m, &m->swapfd) && readswap(m, a))
memmove(m->devswap, a, sizeof m->devswap);
snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
m->statsfd = open(buf, OREAD);
if(loadbuf(m, &m->statsfd)){
for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
;
m->nproc = n;
}else
m->nproc = 1;
m->lgproc = ilog10(m->nproc);
/* find all the ethernets */
n = 0;
snprint(buf, sizeof buf, "%s/net/", mpt);
if((fd = open(buf, OREAD)) >= 0){
for(d = nil; (i = dirread(fd, &d)) > 0; free(d)){
for(j=0; j<i; j++){
if(strncmp(d[j].name, "ether", 5))
continue;
snprint(buf, sizeof buf, "%s/net/%s/stats", mpt, d[j].name);
if((m->etherfd[n] = open(buf, OREAD)) < 0)
continue;
if(++n >= nelem(m->etherfd))
break;
}
if(n >= nelem(m->etherfd))
break;
}
close(fd);
}
while(n < nelem(m->etherfd))
m->etherfd[n++] = -1;
snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
m->batteryfd = open(buf, OREAD);
if(m->batteryfd < 0){
snprint(buf, sizeof buf, "%s/mnt/pm/battery", mpt);
m->batteryfd = open(buf, OREAD);
}
m->bitsybatfd = -1;
if(m->batteryfd >= 0){
batteryperiod = 10000;
if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
}else{
snprint(buf, sizeof buf, "%s/dev/battery", mpt);
m->bitsybatfd = open(buf, OREAD);
if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
}
snprint(buf, sizeof buf, "%s/dev/cputemp", mpt);
m->tempfd = open(buf, OREAD);
if(m->tempfd < 0){
tempperiod = 5000;
snprint(buf, sizeof buf, "%s/mnt/pm/cputemp", mpt);
m->tempfd = open(buf, OREAD);
}
if(loadbuf(m, &m->tempfd))
for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
m->temp[n] = a[0];
return 1;
}
jmp_buf catchalarm;
int
alarmed(void *a, char *s)
{
if(strcmp(s, "alarm") == 0)
notejmp(a, catchalarm, 1);
return 0;
}
int
needswap(int init)
{
return init | present[Mmem] | present[Mswap] | present[Mreclaim] | present[Mkern] | present[Mdraw];
}
int
needstat(int init)
{
return init | present[Mcontext] | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] |
present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
}
int
needether(int init)
{
return init | present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
}
int
needbattery(int init)
{
static uint step = 0;
if(++step*sleeptime >= batteryperiod){
step = 0;
return init | present[Mbattery];
}
return 0;
}
int
needtemp(int init)
{
static uint step = 0;
if(++step*sleeptime >= tempperiod){
step = 0;
return init | present[Mtemp];
}
return 0;
}
void
vadd(uvlong *a, uvlong *b, int n)
{
int i;
for(i=0; i<n; i++)
a[i] += b[i];
}
void
readmach(Machine *m, int init)
{
int n;
uvlong a[nelem(m->devsysstat)];
char buf[32];
if(m->remote && (m->disable || setjmp(catchalarm))){
if (m->disable++ >= 5)
m->disable = 0; /* give it another chance */
memmove(m->devsysstat, m->prevsysstat, sizeof m->devsysstat);
memmove(m->netetherstats, m->prevetherstats, sizeof m->netetherstats);
return;
}
snprint(buf, sizeof buf, "%s", m->name);
if (strcmp(m->name, buf) != 0){
free(m->name);
m->name = estrdup(buf);
free(m->shortname);
m->shortname = shortname(buf);
}
if(m->remote){
atnotify(alarmed, 1);
alarm(5000);
}
if(needswap(init) && loadbuf(m, &m->swapfd) && readswap(m, a))
memmove(m->devswap, a, sizeof m->devswap);
if(needstat(init) && loadbuf(m, &m->statsfd)){
memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
memset(m->devsysstat, 0, sizeof m->devsysstat);
for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
vadd(m->devsysstat, a, nelem(m->devsysstat));
}
if(needether(init)){
memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
memset(m->netetherstats, 0, sizeof(m->netetherstats));
for(n=0; n<nelem(m->etherfd) && m->etherfd[n] >= 0; n++){
if(loadbuf(m, &m->etherfd[n]) && readnums(m, nelem(m->netetherstats), a, 1))
vadd(m->netetherstats, a, nelem(m->netetherstats));
}
}
if(needbattery(init)){
if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
else if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
}
if(needtemp(init) && loadbuf(m, &m->tempfd))
for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
m->temp[n] = a[0];
if(m->remote){
alarm(0);
atnotify(alarmed, 0);
}
}
void
memval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->devswap[Mem];
*vmax = m->devswap[Maxmem];
if(*vmax == 0)
*vmax = 1;
}
void
swapval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->devswap[Swap];
*vmax = m->devswap[Maxswap];
if(*vmax == 0)
*vmax = 1;
}
void
reclaimval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->devswap[Reclaim];
*vmax = m->devswap[Maxreclaim];
if(*vmax == 0)
*vmax = 1;
}
void
kernval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->devswap[Kern];
*vmax = m->devswap[Maxkern];
if(*vmax == 0)
*vmax = 1;
}
void
drawval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->devswap[Draw];
*vmax = m->devswap[Maxdraw];
if(*vmax == 0)
*vmax = 1;
}
void
contextval(Machine *m, uvlong *v, uvlong *vmax, int init)
{
*v = (m->devsysstat[Context]-m->prevsysstat[Context])&0xffffffff;
*vmax = sleeptime*m->nproc;
if(init)
*vmax = sleeptime;
}
/*
* bug: need to factor in HZ
*/
void
intrval(Machine *m, uvlong *v, uvlong *vmax, int init)
{
*v = (m->devsysstat[Interrupt]-m->prevsysstat[Interrupt])&0xffffffff;
*vmax = sleeptime*m->nproc*10;
if(init)
*vmax = sleeptime*10;
}
void
syscallval(Machine *m, uvlong *v, uvlong *vmax, int init)
{
*v = (m->devsysstat[Syscall]-m->prevsysstat[Syscall])&0xffffffff;
*vmax = sleeptime*m->nproc;
if(init)
*vmax = sleeptime;
}
void
faultval(Machine *m, uvlong *v, uvlong *vmax, int init)
{
*v = (m->devsysstat[Fault]-m->prevsysstat[Fault])&0xffffffff;
*vmax = sleeptime*m->nproc;
if(init)
*vmax = sleeptime;
}
void
tlbmissval(Machine *m, uvlong *v, uvlong *vmax, int init)
{
*v = (m->devsysstat[TLBfault]-m->prevsysstat[TLBfault])&0xffffffff;
*vmax = (sleeptime/1000)*10*m->nproc;
if(init)
*vmax = (sleeptime/1000)*10;
}
void
tlbpurgeval(Machine *m, uvlong *v, uvlong *vmax, int init)
{
*v = (m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge])&0xffffffff;
*vmax = (sleeptime/1000)*10*m->nproc;
if(init)
*vmax = (sleeptime/1000)*10;
}
void
loadval(Machine *m, uvlong *v, uvlong *vmax, int init)
{
*v = m->devsysstat[Load];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
idleval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->devsysstat[Idle]/m->nproc;
*vmax = 100;
}
void
inintrval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->devsysstat[InIntr]/m->nproc;
*vmax = 100;
}
void
etherval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
*vmax = sleeptime;
}
void
etherinval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->netetherstats[In]-m->prevetherstats[In];
*vmax = sleeptime;
}
void
etheroutval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->netetherstats[Out]-m->prevetherstats[Out];
*vmax = sleeptime;
}
void
ethererrval(Machine *m, uvlong *v, uvlong *vmax, int)
{
int i;
*v = 0;
for(i=Err0; i<nelem(m->netetherstats); i++)
*v += m->netetherstats[i]-m->prevetherstats[i];
*vmax = (sleeptime/1000)*10;
}
void
batteryval(Machine *m, uvlong *v, uvlong *vmax, int)
{
*v = m->batterystats[0];
if(m->bitsybatfd >= 0)
*vmax = 184; // at least on my bitsy...
else
*vmax = 100;
}
void
tempval(Machine *m, uvlong *v, uvlong *vmax, int)
{
ulong l;
*vmax = 100;
l = m->temp[0];
if(l == ~0 || l == 0)
*v = 0;
else
*v = l;
}
void
usage(void)
{
fprint(2, "usage: nstats [-%s] [machine...]\n", argchars);
exits("usage");
}
void
addgraph(int n)
{
Graph *g, *ograph;
int i, j;
if(n > Nmenu2-1)
abort();
ograph = graph;
graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
for(i=0; i<nmach; i++)
for(j=0; j<ngraph; j++)
graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
free(ograph);
ngraph++;
for(i=0; i<nmach; i++){
g = &graph[i*ngraph+(ngraph-1)];
memset(g, 0, sizeof(Graph));
g->label = labels[n];
g->newvalue = newvaluefn[n];
g->mach = &mach[i];
}
present[n] = 1;
}
int
addmachine(char *name)
{
if(ngraph > 0){
fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
usage();
}
if(mach == nil)
nmach = 0; /* a little dance to get us started with local machine by default */
mach = erealloc(mach, (nmach+1)*sizeof(Machine));
memset(mach+nmach, 0, sizeof(Machine));
if (initmach(mach+nmach, name)){
nmach++;
return 1;
} else
return 0;
}
void
main(int argc, char *argv[])
{
int i, j;
double secs;
uvlong v, vmax, nargs;
double percentage;
char args[100];
Machine *currmach;
quotefmtinstall();
nmach = 1;
mysysname = getenv("sysname");
if(mysysname == nil){
fprint(2, "stats: can't find $sysname: %r\n");
exits("sysname");
}
nargs = 0;
ARGBEGIN{
case 'T':
secs = atof(EARGF(usage()));
if(secs > 0)
sleeptime = 1000*secs;
break;
default:
if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
usage();
args[nargs++] = ARGC();
}ARGEND
if(argc == 0){
mach = emalloc(nmach*sizeof(Machine));
initmach(&mach[0], mysysname);
readmach(&mach[0], 1);
}else{
rfork(RFNAMEG);
for(i=j=0; i<argc; i++){
if (addmachine(argv[i]))
readmach(&mach[j++], 1);
}
if (j == 0)
exits("connect");
}
for(i=0; i<nargs; i++)
switch(args[i]){
default:
fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
usage();
case 'b':
addgraph(Mbattery);
break;
case 'c':
addgraph(Mcontext);
break;
case 'e':
addgraph(Mether);
break;
case 'E':
addgraph(Metherin);
addgraph(Metherout);
break;
case 'f':
addgraph(Mfault);
break;
case 'i':
addgraph(Mintr);
break;
case 'I':
addgraph(Mload);
addgraph(Midle);
addgraph(Minintr);
break;
case 'l':
addgraph(Mload);
break;
case 'm':
addgraph(Mmem);
break;
case 'n':
addgraph(Metherin);
addgraph(Metherout);
addgraph(Methererr);
break;
case 'p':
addgraph(Mtlbpurge);
break;
case 'r':
addgraph(Mreclaim);
break;
case 's':
addgraph(Msyscall);
break;
case 't':
addgraph(Mtlbmiss);
addgraph(Mtlbpurge);
break;
case 'w':
addgraph(Mswap);
break;
case 'k':
addgraph(Mkern);
break;
case 'd':
addgraph(Mdraw);
break;
case 'z':
addgraph(Mtemp);
break;
}
if(ngraph == 0)
addgraph(Mload);
for(i=0; i<nmach; i++)
for(j=0; j<ngraph; j++)
graph[i*ngraph+j].mach = &mach[i];
for(i=0; i<nmach; i++)
readmach(&mach[i], 0);
currmach = nil;
for(i=0; i<nmach*ngraph; i++){
if (nmach > 1 && currmach != graph[i].mach) {
currmach = graph[i].mach;
print("%s:\n", currmach->name);
}
graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
if(vmax == 0)
vmax = 1;
percentage = (double)v/vmax;
print("%*s: %6.2f%% (%ulld/%ulld)\n", padlabels, graph[i].label, percentage*100., v, vmax);
}
}