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); } }