#include #include #include #include #include #include #include #include #include typedef struct Plot Plot; typedef struct Machine Machine; enum { Mbattery, Mcontext, Mether, Methererr, Metherin, Metherout, Mfault, Midle, Minintr, Mintr, Mload, Mmem, Mswap, Msyscall, Mtlbmiss, Mtlbpurge, Msignal, Mend, }; enum { /* /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, MAXNUM = 10, Dot = 2, /* height of dot */ }; struct Plot { int *data; int ndata; char *label; int overflow; void (*newvalue)(Machine*, ulong*, ulong*, int); Panel* graph; }; struct Machine { char *name; int remote; int statsfd; int swapfd; int etherfd; int ifstatsfd; int batteryfd; int bitsybatfd; int disable; ulong devswap[4]; ulong devsysstat[10]; ulong prevsysstat[10]; int nproc; ulong netetherstats[8]; ulong prevetherstats[8]; ulong batterystats[2]; ulong netetherifstats[2]; char buf[1024]; char *bufp; char *ebufp; }; char *mname[Mend+1] = { "battery", "context", "ether", "ethererr", "etherin", "etherout", "fault", "idle", "inintr", "intr", "load", "mem", "swap", "syscall", "tlbmiss", "tlbpurge", "802.11b", nil, }; int present[Mend]; Plot *plot[20]; int nplot; int sleeptime; char* machine; Panel* grow; Machine *m; int vsize = 40; int hsize = 100; void memval(Machine *m, ulong *v, ulong *vmax, int) { *v = m->devswap[Mem]; *vmax = m->devswap[Maxmem]; } void swapval(Machine *m, ulong *v, ulong *vmax, int) { *v = m->devswap[Swap]; *vmax = m->devswap[Maxswap]; } void contextval(Machine *m, ulong *v, ulong *vmax, int init) { *v = m->devsysstat[Context]-m->prevsysstat[Context]; *vmax = sleeptime*m->nproc; if(init) *vmax = sleeptime; } void intrval(Machine *m, ulong *v, ulong *vmax, int init) { *v = m->devsysstat[Interrupt]-m->prevsysstat[Interrupt]; *vmax = sleeptime*m->nproc; if(init) *vmax = sleeptime; } void syscallval(Machine *m, ulong *v, ulong *vmax, int init) { *v = m->devsysstat[Syscall]-m->prevsysstat[Syscall]; *vmax = sleeptime*m->nproc; if(init) *vmax = sleeptime; } void faultval(Machine *m, ulong *v, ulong *vmax, int init) { *v = m->devsysstat[Fault]-m->prevsysstat[Fault]; *vmax = sleeptime*m->nproc; if(init) *vmax = sleeptime; } void tlbmissval(Machine *m, ulong *v, ulong *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, ulong *v, ulong *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, ulong *v, ulong *vmax, int init) { *v = m->devsysstat[Load]; *vmax = 1000*m->nproc; if(init) *vmax = 1000; } void idleval(Machine *m, ulong *v, ulong *vmax, int) { *v = m->devsysstat[Idle]/m->nproc; *vmax = 100; } void inintrval(Machine *m, ulong *v, ulong *vmax, int) { *v = m->devsysstat[InIntr]/m->nproc; *vmax = 100; } void etherval(Machine *m, ulong *v, ulong *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, ulong *v, ulong *vmax, int init) { *v = m->netetherstats[In]-m->prevetherstats[In]; *vmax = sleeptime*m->nproc; if(init) *vmax = sleeptime; } void etheroutval(Machine *m, ulong *v, ulong *vmax, int init) { *v = m->netetherstats[Out]-m->prevetherstats[Out]; *vmax = sleeptime*m->nproc; if(init) *vmax = sleeptime; } void ethererrval(Machine *m, ulong *v, ulong *vmax, int init) { int i; *v = 0; for(i=Err0; inetetherstats); i++) *v += m->netetherstats[i]; *vmax = (sleeptime/1000)*10*m->nproc; if(init) *vmax = (sleeptime/1000)*10; } void batteryval(Machine *m, ulong *v, ulong *vmax, int) { *v = m->batterystats[0]; if(m->bitsybatfd >= 0) *vmax = 184; // at least on my bitsy... else *vmax = 100; } void signalval(Machine *m, ulong *v, ulong *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 (*newvaluefn[Mend])(Machine*, ulong*, ulong*, int init) = { batteryval, contextval, etherval, ethererrval, etherinval, etheroutval, faultval, idleval, inintrval, intrval, loadval, memval, swapval, syscallval, tlbmissval, tlbpurgeval, signalval, }; int needswap(void) { return present[Mmem] | present[Mswap]; } int needstat(void) { return present[Mcontext] | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] | present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge]; } int needether(void) { return present[Mether] | present[Metherin] | present[Metherout] | present[Methererr]; } int needbattery(void) { return present[Mbattery]; } int needsignal(void) { return present[Msignal]; } 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, nil, "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; } return fd; } 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); if(n <= 0){ close(*fd); *fd = -1; return 0; } m->bufp = m->buf; m->ebufp = m->buf+n; return 1; } int readnums(Machine* m, int n, ulong *a, int spanlines) { int i; char *p, *ep; if(spanlines) ep = m->ebufp; else for(ep=m->bufp; epebufp; ep++) if(*ep == '\n') break; p = m->bufp; for(i=0; iebufp) ep++; m->bufp = ep; return i == n; } int initmach(char *name) { int n, fd; ulong a[MAXNUM]; char *p, mpt[256], buf[256]; p = strchr(name, '!'); if(p) p++; else p = name; m->name = estrdup(p); m->remote = (strcmp(p, sysname()) != 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) return 0; } seprint(buf, buf+sizeof(buf), "%s/dev/swap", mpt); m->swapfd = open(buf, OREAD); if(loadbuf(m, &m->swapfd) && readnums(m, nelem(m->devswap), a, 0)) memmove(m->devswap, a, sizeof m->devswap); else m->devswap[Maxmem] = m->devswap[Maxswap] = 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; 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)); } return 1; } void readmach(void) { int n, i; ulong a[8]; char buf[32]; snprint(buf, sizeof buf, "%s", m->name); if (strcmp(m->name, buf) != 0){ free(m->name); m->name = estrdup(buf); } if(needswap() && loadbuf(m, &m->swapfd) && readnums(m, nelem(m->devswap), a, 0)) memmove(m->devswap, a, sizeof m->devswap); if(needstat() && loadbuf(m, &m->statsfd)){ memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat); memset(m->devsysstat, 0, sizeof m->devsysstat); for(n=0; nnproc && readnums(m, nelem(m->devsysstat), a, 0); n++) for(i=0; idevsysstat); i++) m->devsysstat[i] += a[i]; } if(needether() && 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() && 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() && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0)) memmove(m->batterystats, a, sizeof(m->batterystats)); if(needbattery() && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0)) memmove(m->batterystats, a, sizeof(m->batterystats)); } Plot* newplot(int n) { Plot *g; static int nadd; Panel* w; Panel* wl; char txt[20]; char gname[20]; assert(n < nelem(mname)); /* avoid two adjacent plots of same color */ g = emalloc(sizeof(Plot)); memset(g, 0, sizeof(Plot)); g->label = mname[n]; g->data = malloc(hsize*sizeof(ulong)); g->ndata = hsize; memset(g->data, 0, hsize*sizeof(ulong)); seprint(txt, txt+sizeof(txt), "%s %s", machine, mname[n]); txt[13]=0; seprint(gname, gname+sizeof(gname), "col:%s", mname[n]); w = createsubpanel(grow, gname); seprint(gname, gname+sizeof(gname), "label:%s", mname[n]); wl = createsubpanel(w, gname); //graphctl(wl, "font S"); openpanel(wl, OWRITE|OTRUNC); writepanel(wl, txt, strlen(txt)); closepanel(wl); seprint(gname, gname+sizeof(gname), "draw:%s", mname[n]); g->graph = createsubpanel(w, gname); openpanelctl(w); panelctl(w, "notag"); closepanelctl(w); g->newvalue = newvaluefn[n]; present[n] = 1; plot[nplot++] = g; return g; } void update(Plot *g, ulong v, ulong vmax) { static char plot[32*1024]; char* s; double y; int d; int i; memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0])); g->data[0] = v; s = seprint(plot, plot+sizeof(plot), "rect %d %d %d %d grey\n", 0, 0, hsize+1, vsize+1); for (i = 0; i < g->ndata; i++){ y = ((double)g->data[i])/(vmax*1); d = vsize * y; if (d < 0) d = 0; if (d > vsize) d = vsize; d = vsize - d; s = seprint(s, plot+sizeof(plot), "line %d %d %d %d 0 red\n", hsize - i, vsize, hsize - i, d); } openpanel(g->graph, OWRITE|OTRUNC); writepanel(g->graph, plot, strlen(plot)); closepanel(g->graph); } void usage(void) { fprint(2, "usage: %s [-8bceEfiImlnpstw] [machine]\n", argv0); exits("usage"); } void omerogone(void) { fprint(2, "ostats: graphgone\n"); sysfatal("graphgone"); } void threadmain(int argc, char* argv[]) { ulong v, vmax, nargs; char* s; int i; char args[20]; char argchars[] = "8bceEfiImlnpstw"; extern int graphdebug; grow = createpanel("ostats", "row", nil); if (grow == nil) sysfatal("graphinit: %r\n"); sleeptime = 2000; nargs = 0; ARGBEGIN{ case 'd': omerodebug = 1; break; case 'T': s = EARGF(usage()); i = atoi(s); if(i > 2) sleeptime = 1000*i; break; default: if(nargs>=sizeof args || strchr(argchars, ARGC())==nil) usage(); args[nargs++] = ARGC(); }ARGEND; if (argc == 1){ machine = argv[0]; argc--; } else machine = sysname(); if (argc != 0) usage(); m = malloc(sizeof(Machine)); memset(m, 0, sizeof(Machine)); initmach(machine); for(i=0; inewvalue(m, &v, &vmax, 0); update(plot[i], v, vmax); } sleep(sleeptime); } }