diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/stats.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/stats.c')
-rwxr-xr-x | sys/src/cmd/stats.c | 1439 |
1 files changed, 1439 insertions, 0 deletions
diff --git a/sys/src/cmd/stats.c b/sys/src/cmd/stats.c new file mode 100755 index 000000000..71c408ff5 --- /dev/null +++ b/sys/src/cmd/stats.c @@ -0,0 +1,1439 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <auth.h> +#include <fcall.h> +#include <draw.h> +#include <event.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*, uvlong*, uvlong*, int); + void (*update)(Graph*, uvlong, uvlong); + Machine *mach; + int overflow; + Image *overtmp; +}; + +enum +{ + /* old /dev/swap */ + Mem = 0, + Maxmem, + Swap, + Maxswap, + + /* /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; + + uvlong devswap[4]; + uvlong devsysstat[10]; + uvlong prevsysstat[10]; + int nproc; + int lgproc; + uvlong netetherstats[8]; + uvlong prevetherstats[8]; + uvlong batterystats[2]; + uvlong netetherifstats[2]; + uvlong temp[10]; + + /* big enough to hold /dev/sysstat even with many processors */ + char buf[8*1024]; + char *bufp; + char *ebufp; +}; + +enum +{ + Mainproc, + Mouseproc, + 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, + Msyscall, + Mtlbmiss, + Mtlbpurge, + Msignal, + Mtemp, + Nmenu2, +}; + +char *menu2str[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 syscall ", + "add tlbmiss ", + "add tlbpurge", + "add 802.11b ", + "add temp ", + nil, +}; + + +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), + syscallval(Machine*, uvlong*, uvlong*, int), + tlbmissval(Machine*, uvlong*, uvlong*, int), + tlbpurgeval(Machine*, uvlong*, uvlong*, int), + batteryval(Machine*, uvlong*, uvlong*, int), + signalval(Machine*, uvlong*, uvlong*, int), + tempval(Machine*, uvlong*, uvlong*, int); + +Menu menu2 = {menu2str, nil}; +int present[Nmenu2]; +void (*newvaluefn[Nmenu2])(Machine*, uvlong*, uvlong*, int init) = { + batteryval, + contextval, + etherval, + ethererrval, + etherinval, + etheroutval, + faultval, + idleval, + inintrval, + intrval, + loadval, + memval, + swapval, + syscallval, + tlbmissval, + tlbpurgeval, + signalval, + tempval, +}; + +Image *cols[Ncolor][3]; +Graph *graph; +Machine *mach; +Font *mediumfont; +char *mysysname; +char argchars[] = "8bceEfiImlnpstwz"; +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 oldsystem = 0; +int sleeptime = 1000; + +char *procnames[NPROC] = {"main", "mouse"}; + +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(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; +} + +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) +{ + mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font"); + if(mediumfont == nil) + mediumfont = font; + + /* 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+mediumfont->height-Ysqueeze > maxy) + break; + w = chartorune(r, s); + s += w; + w = runestringwidth(mediumfont, r); + if(w > maxw) + maxw = w; + runestring(screen, p, display->black, ZP, mediumfont, r); + p.y += mediumfont->height-Ysqueeze; + } +} + +Point +paritypt(int x) +{ + return Pt(x+parity, 0); +} + +Point +datapoint(Graph *g, int x, uvlong v, uvlong 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.; + } + } + 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, uvlong prev, uvlong v, uvlong 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, uvlong 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, uvlong v, uvlong 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, mediumfont, buf); + } +} + +/* 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; +} + +/* Network on fd1, mount driver on fd0 */ +static int +filter(int fd) +{ + int p[2]; + + if(pipe(p) < 0){ + fprint(2, "stats: can't pipe: %r\n"); + killall("pipe"); + } + + switch(rfork(RFNOWAIT|RFPROC|RFFDG)) { + case -1: + sysfatal("rfork record module"); + case 0: + dup(fd, 1); + close(fd); + dup(p[0], 0); + close(p[0]); + close(p[1]); + execl("/bin/aux/fcall", "fcall", nil); + fprint(2, "stats: can't exec fcall: %r\n"); + killall("fcall"); + default: + close(fd); + close(p[0]); + } + return p[1]; +} + +/* + * 9fs + */ +int +connect9fs(char *addr) +{ + char dir[256], *na; + int fd; + + fprint(2, "connect9fs..."); + na = netmkaddr(addr, 0, "9fs"); + + fprint(2, "dial %s...", na); + if((fd = dial(na, 0, dir, 0)) < 0) + return -1; + + fprint(2, "dir %s...", dir); +// if(strstr(dir, "tcp")) +// fd = filter(fd); + return fd; +} + +int +old9p(int fd) +{ + int p[2]; + + if(pipe(p) < 0) + return -1; + + switch(rfork(RFPROC|RFFDG|RFNAMEG)) { + case -1: + return -1; + case 0: + if(fd != 1){ + dup(fd, 1); + close(fd); + } + if(p[0] != 0){ + dup(p[0], 0); + close(p[0]); + } + close(p[1]); + if(0){ + fd = open("/sys/log/cpu", OWRITE); + if(fd != 2){ + dup(fd, 2); + close(fd); + } + execl("/bin/srvold9p", "srvold9p", "-ds", nil); + } else + execl("/bin/srvold9p", "srvold9p", "-s", nil); + return -1; + default: + close(fd); + close(p[0]); + } + return p[1]; +} + + +/* + * exportfs + */ +int +connectexportfs(char *addr) +{ + char buf[ERRMAX], dir[256], *na; + int fd, n; + char *tree; + AuthInfo *ai; + + tree = "/"; + na = netmkaddr(addr, 0, "exportfs"); + if((fd = dial(na, 0, dir, 0)) < 0) + return -1; + + ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client"); + if(ai == nil) + return -1; + + n = write(fd, tree, strlen(tree)); + if(n < 0){ + close(fd); + return -1; + } + + strcpy(buf, "can't read tree"); + n = read(fd, buf, sizeof buf - 1); + if(n!=2 || buf[0]!='O' || buf[1]!='K'){ + buf[sizeof buf - 1] = '\0'; + werrstr("bad remote tree: %s\n", buf); + close(fd); + return -1; + } + +// if(strstr(dir, "tcp")) +// fd = filter(fd); + + if(oldsystem) + return old9p(fd); + + return fd; +} + +int +readswap(Machine *m, uvlong *a) +{ + if(strstr(m->buf, "memory\n")){ + /* new /dev/swap - skip first 3 numbers */ + if(!readnums(m, 7, a, 1)) + return 0; + a[0] = a[3]; + a[1] = a[4]; + a[2] = a[5]; + a[3] = a[6]; + return 1; + } + return readnums(m, nelem(m->devswap), 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, fd; + uvlong 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{ + snprint(mpt, sizeof mpt, "/n/%s", p); + fd = connectexportfs(name); + if(fd < 0){ + fprint(2, "can't connect to %s: %r\n", name); + return 0; + } + /* BUG? need to use amount() now? */ + if(mount(fd, -1, mpt, MREPL, "") < 0){ + fprint(2, "stats: mount %s on %s failed (%r); trying /n/sid\n", name, mpt); + strcpy(mpt, "/n/sid"); + if(mount(fd, -1, mpt, MREPL, "") < 0){ + fprint(2, "stats: mount %s on %s failed: %r\n", name, mpt); + return 0; + } + } + } + + 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); + else{ + m->devswap[Maxswap] = 100; + m->devswap[Maxmem] = 100; + } + + 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; + +void +alarmed(void *a, char *s) +{ + if(strcmp(s, "alarm") == 0) + notejmp(a, catchalarm, 1); + noted(NDFLT); +} + +int +needswap(int init) +{ + return init | present[Mmem] | present[Mswap]; +} + + +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; + 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(display != nil) /* else we're still initializing */ + eresized(0); + } + if(m->remote){ + notify(alarmed); + 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); + notify(nil); + } +} + +void +memval(Machine *m, uvlong *v, uvlong *vmax, int) +{ + *v = m->devswap[Mem]; + *vmax = m->devswap[Maxmem]; +} + +void +swapval(Machine *m, uvlong *v, uvlong *vmax, int) +{ + *v = m->devswap[Swap]; + *vmax = m->devswap[Maxswap]; +} + +void +contextval(Machine *m, uvlong *v, uvlong *vmax, int init) +{ + *v = m->devsysstat[Context]-m->prevsysstat[Context]; + *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]; + *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]; + *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]; + *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]; + *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]; + *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 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, uvlong *v, uvlong *vmax, int init) +{ + *v = m->netetherstats[In]-m->prevetherstats[In]; + *vmax = sleeptime*m->nproc; + if(init) + *vmax = sleeptime; +} + +void +etheroutval(Machine *m, uvlong *v, uvlong *vmax, int init) +{ + *v = m->netetherstats[Out]-m->prevetherstats[Out]; + *vmax = sleeptime*m->nproc; + if(init) + *vmax = sleeptime; +} + +void +ethererrval(Machine *m, uvlong *v, uvlong *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, uvlong *v, uvlong *vmax, int) +{ + *v = m->batterystats[0]; + if(m->bitsybatfd >= 0) + *vmax = 184; // at least on my bitsy... + else + *vmax = 100; +} + +void +signalval(Machine *m, uvlong *v, uvlong *vmax, int) +{ + ulong 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, uvlong *v, uvlong *vmax, int) +{ + ulong 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 = menu2str[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; + uvlong v, vmax; + + g->newvalue(g->mach, &v, &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(mediumfont, 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; + uvlong 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+mediumfont->height+Labspace; + dy = (screen->r.max.y - y)/ngraph; + dx = Labspace+stringwidth(mediumfont, "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(mediumfont, "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, mediumfont, buf); + } + + maxx = screen->r.max.x; + + /* label right, if requested */ + if(ylabels && dy>Nlab*(mediumfont->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 -= mediumfont->height/2; + string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, 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(ulong)); + if(g->ndata > ondata) + memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong)); + /* 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+mediumfont->height; + r.max.x = r.min.x+stringwidth(mediumfont, "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 +mouseproc(void) +{ + Mouse mouse; + int i; + + for(;;){ + mouse = emouse(); + if(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, &mouse, &menu2); + if(i >= 0){ + if(!present[i]) + addgraph(i); + else if(ngraph > 1) + dropgraph(i); + resize(); + } + unlockdisplay(display); + } + } +} + +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; + uvlong v, vmax, nargs; + char args[100]; + + nmach = 1; + mysysname = getenv("sysname"); + if(mysysname == nil){ + fprint(2, "stats: can't find $sysname: %r\n"); + exits("sysname"); + } + mysysname = estrdup(mysysname); + + 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': + oldsystem = 1; + 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{ + 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 '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"); + } + colinit(); + einit(Emouse); + notify(nil); + startproc(mouseproc, Mouseproc); + pids[Mainproc] = getpid(); + display->locking = 1; /* tell library we're using the display lock */ + + 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); + } +} |