jehanne/sys/src/cmd/stats.c

1387 lines
28 KiB
C

#include <u.h>
#include <lib9.h>
#include <envvars.h>
#include <ctype.h>
#include <draw.h>
#include <event.h>
#include <keyboard.h>
#define MAXNUM 10 /* maximum number of numbers on data line */
typedef struct Graph Graph;
typedef struct Machine Machine;
struct Graph
{
int colindex;
Rectangle r;
int *data;
int ndata;
char *label;
void (*newvalue)(Machine*, uint64_t*, uint64_t*, int);
void (*update)(Graph*, uint64_t, uint64_t);
Machine *mach;
int overflow;
Image *overtmp;
};
enum
{
/* old /dev/swap */
Mem = 0,
Maxmem,
Swap,
Maxswap,
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;
int ifstatsfd;
int batteryfd;
int bitsybatfd;
int tempfd;
int disable;
uint64_t devswap[8];
uint64_t devsysstat[10];
uint64_t prevsysstat[10];
int nproc;
int lgproc;
uint64_t netetherstats[8];
uint64_t prevetherstats[8];
uint64_t batterystats[2];
uint64_t netetherifstats[2];
uint64_t temp[10];
/* big enough to hold /dev/sysstat even with many processors */
char buf[8*1024];
char *bufp;
char *ebufp;
};
enum
{
Mainproc,
Inputproc,
NPROC,
};
enum
{
Ncolor = 6,
Ysqueeze = 2, /* vertical squeezing of label text */
Labspace = 2, /* room around label */
Dot = 2, /* height of dot */
Opwid = 5, /* strlen("add ") or strlen("drop ") */
Nlab = 3, /* max number of labels on y axis */
Lablen = 16, /* max length of label */
Lx = 4, /* label tick length */
};
enum Menu2
{
Mbattery,
Mcontext,
Mether,
Methererr,
Metherin,
Metherout,
Mfault,
Midle,
Minintr,
Mintr,
Mload,
Mmem,
Mswap,
Mkern,
Mdraw,
Msyscall,
Mtlbmiss,
Mtlbpurge,
Msignal,
Mtemp,
Nmenu2,
};
char *menu2strtpl[Nmenu2+1] = {
"add battery ",
"add context ",
"add ether ",
"add ethererr",
"add etherin ",
"add etherout",
"add fault ",
"add idle ",
"add inintr ",
"add intr ",
"add load ",
"add mem ",
"add swap ",
"add kern ",
"add draw ",
"add syscall ",
"add tlbmiss ",
"add tlbpurge",
"add 802.11b ",
"add temp ",
nil,
};
char *menu2str[Nmenu2+1];
void contextval(Machine*, uint64_t*, uint64_t*, int),
etherval(Machine*, uint64_t*, uint64_t*, int),
ethererrval(Machine*, uint64_t*, uint64_t*, int),
etherinval(Machine*, uint64_t*, uint64_t*, int),
etheroutval(Machine*, uint64_t*, uint64_t*, int),
faultval(Machine*, uint64_t*, uint64_t*, int),
intrval(Machine*, uint64_t*, uint64_t*, int),
inintrval(Machine*, uint64_t*, uint64_t*, int),
loadval(Machine*, uint64_t*, uint64_t*, int),
idleval(Machine*, uint64_t*, uint64_t*, int),
memval(Machine*, uint64_t*, uint64_t*, int),
swapval(Machine*, uint64_t*, uint64_t*, int),
kernval(Machine*, uint64_t*, uint64_t*, int),
drawval(Machine*, uint64_t*, uint64_t*, int),
syscallval(Machine*, uint64_t*, uint64_t*, int),
tlbmissval(Machine*, uint64_t*, uint64_t*, int),
tlbpurgeval(Machine*, uint64_t*, uint64_t*, int),
batteryval(Machine*, uint64_t*, uint64_t*, int),
signalval(Machine*, uint64_t*, uint64_t*, int),
tempval(Machine*, uint64_t*, uint64_t*, int);
Menu menu2 = {menu2str, nil};
int present[Nmenu2];
void (*newvaluefn[Nmenu2])(Machine*, uint64_t*, uint64_t*, int init) = {
batteryval,
contextval,
etherval,
ethererrval,
etherinval,
etheroutval,
faultval,
idleval,
inintrval,
intrval,
loadval,
memval,
swapval,
kernval,
drawval,
syscallval,
tlbmissval,
tlbpurgeval,
signalval,
tempval,
};
Image *cols[Ncolor][3];
Graph *graph;
Machine *mach;
char *mysysname;
char argchars[] = "8bcdeEfiIkmlnpstwz";
int pids[NPROC];
int parity; /* toggled to avoid patterns in textured background */
int nmach;
int ngraph; /* totaly number is ngraph*nmach */
double scale = 1.0;
int logscale = 0;
int ylabels = 0;
int sleeptime = 1000;
char *procnames[NPROC] = {"main", "input"};
void
killall(char *s)
{
int i, pid;
pid = getpid();
for(i=0; i<NPROC; i++)
if(pids[i] && pids[i]!=pid)
postnote(PNPROC, pids[i], "kill");
exits(s);
}
void*
emalloc(uint32_t 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, uint32_t 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;
}
void
mkcol(int i, int c0, int c1, int c2)
{
cols[i][0] = allocimagemix(display, c0, DWhite);
cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
}
void
colinit(void)
{
/* Peach */
mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
/* Aqua */
mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
/* Yellow */
mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
/* Green */
mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
/* Blue */
mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
/* Grey */
cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
}
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;
}
void
label(Point p, int dy, char *text)
{
char *s;
Rune r[2];
int w, maxw, maxy;
p.x += Labspace;
maxy = p.y+dy;
maxw = 0;
r[1] = '\0';
for(s=text; *s; ){
if(p.y+font->height-Ysqueeze > maxy)
break;
w = chartorune(r, s);
s += w;
w = runestringwidth(font, r);
if(w > maxw)
maxw = w;
runestring(screen, p, display->black, ZP, font, r);
p.y += font->height-Ysqueeze;
}
}
Point
paritypt(int x)
{
return Pt(x+parity, 0);
}
Point
datapoint(Graph *g, int x, uint64_t v, uint64_t vmax)
{
Point p;
double y;
p.x = x;
y = ((double)v)/(vmax*scale);
if(logscale){
/*
* Arrange scale to cover a factor of 1000.
* vmax corresponds to the 100 mark.
* 10*vmax is the top of the scale.
*/
if(y <= 0.)
y = 0;
else{
y = log10(y);
/* 1 now corresponds to the top; -2 to the bottom; rescale */
y = (y+2.)/3.;
}
}
if(y >= 1.)
y = 1;
if(y <= 0.)
y = 0;
p.y = g->r.max.y - Dy(g->r)*y - Dot;
if(p.y < g->r.min.y)
p.y = g->r.min.y;
if(p.y > g->r.max.y-Dot)
p.y = g->r.max.y-Dot;
return p;
}
void
drawdatum(Graph *g, int x, uint64_t prev, uint64_t v, uint64_t vmax)
{
int c;
Point p, q;
c = g->colindex;
p = datapoint(g, x, v, vmax);
q = datapoint(g, x, prev, vmax);
if(p.y < q.y){
draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
}else{
draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
}
}
void
redraw(Graph *g, uint64_t vmax)
{
int i, c;
c = g->colindex;
draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
for(i=1; i<Dx(g->r); i++)
drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
g->overflow = 0;
}
void
update1(Graph *g, uint64_t v, uint64_t vmax)
{
char buf[48];
int overflow;
if(g->overflow && g->overtmp!=nil)
draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
g->data[0] = v;
g->overflow = 0;
if(logscale)
overflow = (v>10*vmax*scale);
else
overflow = (v>vmax*scale);
if(overflow && g->overtmp!=nil){
g->overflow = 1;
draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
sprint(buf, "%llud", v);
string(screen, g->overtmp->r.min, display->black, ZP, font, buf);
}
}
/* read one line of text from buffer and process integers */
int
readnums(Machine *m, int n, uint64_t *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, uint64_t *a)
{
if(strstr(m->buf, "memory\n")){
/* new /dev/swap - skip first 3 numbers */
if(!readnums(m, 5, a, 1))
return 0;
a[0] = a[3];
a[1] = a[4];
a[2] = a[5];
a[3] = a[6];
a[4] = 0;
a[5] = 0;
if(m->bufp = strstr(m->buf, "kernel malloc")){
while(m->bufp > m->buf && m->bufp[-1] != '\n')
m->bufp--;
a[4] = strtoull(m->bufp, &m->bufp, 10);
while(*m->bufp++ == '/')
a[5] = strtoull(m->bufp, &m->bufp, 10);
}
a[6] = 0;
a[7] = 0;
if(m->bufp = strstr(m->buf, "kernel draw")){
while(m->bufp > m->buf && m->bufp[-1] != '\n')
m->bufp--;
a[6] = strtoull(m->bufp, &m->bufp, 10);
while(*m->bufp++ == '/')
a[7] = strtoull(m->bufp, &m->bufp, 10);
}
return 1;
}
a[4] = 0;
a[5] = 0;
a[6] = 0;
a[7] = 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(uint64_t j)
{
int i;
for(i = 0; j >= 10; i++)
j /= 10;
return i;
}
int
initmach(Machine *m, char *name)
{
int n;
uint64_t a[MAXNUM];
char *p, mpt[256], buf[256];
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);
snprint(buf, sizeof buf, "rimport %q / %q || import %q / %q", name, mpt, name, mpt);
pid = fork();
switch(pid){
case -1:
fprint(2, "can't fork: %r\n");
return 0;
case 0:
execl("/cmd/rc", "rc", "-c", buf, 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);
snprint(buf, sizeof buf, "%s/net/ether0/stats", mpt);
m->etherfd = open(buf, OREAD);
if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
memmove(m->netetherstats, a, sizeof m->netetherstats);
snprint(buf, sizeof buf, "%s/net/ether0/ifstats", mpt);
m->ifstatsfd = open(buf, OREAD);
if(loadbuf(m, &m->ifstatsfd)){
/* need to check that this is a wavelan interface */
if(strncmp(m->buf, "Signal: ", 8) == 0 && readnums(m, nelem(m->netetherifstats), a, 1))
memmove(m->netetherifstats, a, sizeof m->netetherifstats);
}
snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
m->batteryfd = open(buf, OREAD);
m->bitsybatfd = -1;
if(m->batteryfd >= 0){
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(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[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)
{
return init | present[Mbattery];
}
int
needsignal(int init)
{
return init | present[Msignal];
}
int
needtemp(int init)
{
return init | present[Mtemp];
}
void
readmach(Machine *m, int init)
{
int n, i;
uint64_t 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(display != nil) /* else we're still initializing */
eresized(0);
}
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++)
for(i=0; i<nelem(m->devsysstat); i++)
m->devsysstat[i] += a[i];
}
if(needether(init) && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
memmove(m->netetherstats, a, sizeof m->netetherstats);
}
if(needsignal(init) && loadbuf(m, &m->ifstatsfd) && strncmp(m->buf, "Signal: ", 8)==0 && readnums(m, nelem(m->netetherifstats), a, 1)){
memmove(m->netetherifstats, a, sizeof m->netetherifstats);
}
if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
memmove(m->batterystats, a, sizeof(m->batterystats));
if(needbattery(init) && 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, uint64_t *v, uint64_t *vmax, int _)
{
*v = m->devswap[Mem];
*vmax = m->devswap[Maxmem];
if(*vmax == 0)
*vmax = 1;
}
void
swapval(Machine *m, uint64_t *v, uint64_t *vmax, int _)
{
*v = m->devswap[Swap];
*vmax = m->devswap[Maxswap];
if(*vmax == 0)
*vmax = 1;
}
void
kernval(Machine *m, uint64_t *v, uint64_t *vmax, int _)
{
*v = m->devswap[Kern];
*vmax = m->devswap[Maxkern];
if(*vmax == 0)
*vmax = 1;
}
void
drawval(Machine *m, uint64_t *v, uint64_t *vmax, int _)
{
*v = m->devswap[Draw];
*vmax = m->devswap[Maxdraw];
if(*vmax == 0)
*vmax = 1;
}
void
contextval(Machine *m, uint64_t *v, uint64_t *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, uint64_t *v, uint64_t *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, uint64_t *v, uint64_t *vmax, int init)
{
*v = (m->devsysstat[Syscall]-m->prevsysstat[Syscall])&0xffffffff;
*vmax = sleeptime*m->nproc;
if(init)
*vmax = sleeptime;
}
void
faultval(Machine *m, uint64_t *v, uint64_t *vmax, int init)
{
*v = (m->devsysstat[Fault]-m->prevsysstat[Fault])&0xffffffff;
*vmax = sleeptime*m->nproc;
if(init)
*vmax = sleeptime;
}
void
tlbmissval(Machine *m, uint64_t *v, uint64_t *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, uint64_t *v, uint64_t *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, uint64_t *v, uint64_t *vmax, int init)
{
*v = m->devsysstat[Load];
*vmax = 1000*m->nproc;
if(init)
*vmax = 1000;
}
void
idleval(Machine *m, uint64_t *v, uint64_t *vmax, int _)
{
*v = m->devsysstat[Idle]/m->nproc;
*vmax = 100;
}
void
inintrval(Machine *m, uint64_t *v, uint64_t *vmax, int _)
{
*v = m->devsysstat[InIntr]/m->nproc;
*vmax = 100;
}
void
etherval(Machine *m, uint64_t *v, uint64_t *vmax, int init)
{
*v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
*vmax = sleeptime*m->nproc;
if(init)
*vmax = sleeptime;
}
void
etherinval(Machine *m, uint64_t *v, uint64_t *vmax, int init)
{
*v = m->netetherstats[In]-m->prevetherstats[In];
*vmax = sleeptime*m->nproc;
if(init)
*vmax = sleeptime;
}
void
etheroutval(Machine *m, uint64_t *v, uint64_t *vmax, int init)
{
*v = m->netetherstats[Out]-m->prevetherstats[Out];
*vmax = sleeptime*m->nproc;
if(init)
*vmax = sleeptime;
}
void
ethererrval(Machine *m, uint64_t *v, uint64_t *vmax, int init)
{
int i;
*v = 0;
for(i=Err0; i<nelem(m->netetherstats); i++)
*v += m->netetherstats[i];
*vmax = (sleeptime/1000)*10*m->nproc;
if(init)
*vmax = (sleeptime/1000)*10;
}
void
batteryval(Machine *m, uint64_t *v, uint64_t *vmax, int _)
{
*v = m->batterystats[0];
if(m->bitsybatfd >= 0)
*vmax = 184; // at least on my bitsy...
else
*vmax = 100;
}
void
signalval(Machine *m, uint64_t *v, uint64_t *vmax, int _)
{
uint32_t l;
*vmax = sleeptime;
l = m->netetherifstats[0];
/*
* Range is seen to be from about -45 (strong) to -95 (weak); rescale
*/
if(l == 0){ /* probably not present */
*v = 0;
return;
}
*v = 20*(l+95);
}
void
tempval(Machine *m, uint64_t *v, uint64_t *vmax, int _)
{
uint32_t l;
*vmax = sleeptime;
l = m->temp[0];
if(l == ~0 || l == 0)
*v = 0;
else
*v = (l-20)*27;
}
void
usage(void)
{
fprint(2, "usage: stats [-O] [-S scale] [-LY] [-%s] [machine...]\n", argchars);
exits("usage");
}
void
addgraph(int n)
{
Graph *g, *ograph;
int i, j;
static int nadd;
if(n > nelem(menu2str))
abort();
/* avoid two adjacent graphs of same color */
if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
nadd++;
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 = menu2strtpl[n]+Opwid;
g->newvalue = newvaluefn[n];
g->update = update1; /* no other update functions yet */
g->mach = &mach[i];
g->colindex = nadd%Ncolor;
}
present[n] = 1;
nadd++;
}
void
dropgraph(int which)
{
Graph *ograph;
int i, j, n;
if(which > nelem(menu2str))
abort();
/* convert n to index in graph table */
n = -1;
for(i=0; i<ngraph; i++)
if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
n = i;
break;
}
if(n < 0){
fprint(2, "stats: internal error can't drop graph\n");
killall("error");
}
ograph = graph;
graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
for(i=0; i<nmach; i++){
for(j=0; j<n; j++)
graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
free(ograph[i*ngraph+j].data);
freeimage(ograph[i*ngraph+j].overtmp);
for(j++; j<ngraph; j++)
graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
}
free(ograph);
ngraph--;
present[which] = 0;
}
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
labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
{
int j;
uint64_t v, vmax;
g->newvalue(g->mach, &v, &vmax, 1);
if(vmax == 0)
vmax = 1;
if(logscale){
for(j=1; j<=2; j++)
sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
*np = 2;
}else{
for(j=1; j<=3; j++)
sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
*np = 3;
}
}
int
labelwidth(void)
{
int i, j, n, w, maxw;
char strs[Nlab][Lablen];
maxw = 0;
for(i=0; i<ngraph; i++){
/* choose value for rightmost graph */
labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
for(j=0; j<n; j++){
w = stringwidth(font, strs[j]);
if(w > maxw)
maxw = w;
}
}
return maxw;
}
void
resize(void)
{
int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
Graph *g;
Rectangle machr, r;
uint64_t v, vmax;
char buf[128], labs[Nlab][Lablen];
draw(screen, screen->r, display->white, nil, ZP);
/* label left edge */
x = screen->r.min.x;
y = screen->r.min.y + Labspace+font->height+Labspace;
dy = (screen->r.max.y - y)/ngraph;
dx = Labspace+stringwidth(font, "0")+Labspace;
startx = x+dx+1;
starty = y;
for(i=0; i<ngraph; i++,y+=dy){
draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
label(Pt(x, y), dy, graph[i].label);
draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
}
/* label top edge */
dx = (screen->r.max.x - startx)/nmach;
for(x=startx, i=0; i<nmach; i++,x+=dx){
draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
j = dx/stringwidth(font, "0");
n = mach[i].nproc;
if(n>1 && j>=1+3+mach[i].lgproc){ /* first char of name + (n) */
j -= 3+mach[i].lgproc;
if(j <= 0)
j = 1;
snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].shortname, n);
}else
snprint(buf, sizeof buf, "%.*s", j, mach[i].shortname);
string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, font, buf);
}
maxx = screen->r.max.x;
/* label right, if requested */
if(ylabels && dy>Nlab*(font->height+1)){
wid = labelwidth();
if(wid < (maxx-startx)-30){
/* else there's not enough room */
maxx -= 1+Lx+wid;
draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
y = starty;
for(j=0; j<ngraph; j++, y+=dy){
/* choose value for rightmost graph */
g = &graph[ngraph*(nmach-1)+j];
labelstrs(g, labs, &nlab);
r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
if(j == ngraph-1)
r.max.y = screen->r.max.y;
draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
for(k=0; k<nlab; k++){
ly = y + (dy*(nlab-k)/(nlab+1));
draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
ly -= font->height/2;
string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, font, labs[k]);
}
}
}
}
/* create graphs */
for(i=0; i<nmach; i++){
machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
if(i < nmach-1)
machr.max.x = startx+(i+1)*dx - 1;
y = starty;
for(j=0; j<ngraph; j++, y+=dy){
g = &graph[i*ngraph+j];
/* allocate data */
ondata = g->ndata;
g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
g->data = erealloc(g->data, g->ndata*sizeof(uint32_t));
if(g->ndata > ondata)
memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(uint32_t));
/* set geometry */
g->r = machr;
g->r.min.y = y;
g->r.max.y = y+dy - 1;
if(j == ngraph-1)
g->r.max.y = screen->r.max.y;
draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
g->overflow = 0;
r = g->r;
r.max.y = r.min.y+font->height;
r.max.x = r.min.x+stringwidth(font, "999999999999");
freeimage(g->overtmp);
g->overtmp = nil;
if(r.max.x <= g->r.max.x)
g->overtmp = allocimage(display, r, screen->chan, 0, -1);
g->newvalue(g->mach, &v, &vmax, 0);
redraw(g, vmax);
}
}
flushimage(display, 1);
}
void
eresized(int new)
{
lockdisplay(display);
if(new && getwindow(display, Refnone) < 0) {
fprint(2, "stats: can't reattach to window\n");
killall("reattach");
}
resize();
unlockdisplay(display);
}
void
inputproc(void)
{
Event e;
int i;
for(;;){
switch(eread(Emouse|Ekeyboard, &e)){
case Emouse:
if(e.mouse.buttons == 4){
lockdisplay(display);
for(i=0; i<Nmenu2; i++)
if(present[i])
memmove(menu2str[i], "drop ", Opwid);
else
memmove(menu2str[i], "add ", Opwid);
i = emenuhit(3, &e.mouse, &menu2);
if(i >= 0){
if(!present[i])
addgraph(i);
else if(ngraph > 1)
dropgraph(i);
resize();
}
unlockdisplay(display);
}
break;
case Ekeyboard:
if(e.kbdc==Kdel || e.kbdc=='q')
killall(nil);
break;
}
}
}
void
startproc(void (*f)(void), int index)
{
int pid;
switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
case -1:
fprint(2, "stats: fork failed: %r\n");
killall("fork failed");
case 0:
f();
fprint(2, "stats: %s process exits\n", procnames[index]);
if(index >= 0)
killall("process died");
exits(nil);
}
if(index >= 0)
pids[index] = pid;
}
void
main(int argc, char *argv[])
{
int i, j;
double secs;
uint64_t v, vmax, nargs;
char args[100];
for(i=0; i<Nmenu2; i++){
menu2str[i] = malloc(strlen(menu2strtpl[i]) + 1);
strcpy(menu2str[i], menu2strtpl[i]);
}
quotefmtinstall();
nmach = 1;
mysysname = getenv(ENV_SYSNAME);
if(mysysname == nil){
fprint(2, "stats: can't find $" ENV_SYSNAME ": %r\n");
exits("sysname");
}
nargs = 0;
ARGBEGIN{
case 'T':
secs = atof(EARGF(usage()));
if(secs > 0)
sleeptime = 1000*secs;
break;
case 'S':
scale = atof(EARGF(usage()));
if(scale <= 0)
usage();
break;
case 'L':
logscale++;
break;
case 'Y':
ylabels++;
break;
case 'O':
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 's':
addgraph(Msyscall);
break;
case 't':
addgraph(Mtlbmiss);
addgraph(Mtlbpurge);
break;
case '8':
addgraph(Msignal);
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];
if(initdraw(nil, nil, "stats") < 0){
fprint(2, "stats: initdraw failed: %r\n");
exits("initdraw");
}
display->locking = 1; /* tell library we're using the display lock */
colinit();
einit(Emouse|Ekeyboard);
startproc(inputproc, Inputproc);
pids[Mainproc] = getpid();
resize();
unlockdisplay(display); /* display is still locked from initdraw() */
for(;;){
for(i=0; i<nmach; i++)
readmach(&mach[i], 0);
lockdisplay(display);
parity = 1-parity;
for(i=0; i<nmach*ngraph; i++){
graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
graph[i].update(&graph[i], v, vmax);
}
flushimage(display, 1);
unlockdisplay(display);
sleep(sleeptime);
}
}