#include #include #include #include #include #include #include #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*, ulong*, ulong*, int); void (*update)(Graph*, ulong, ulong); Machine *mach; int overflow; Image *overtmp; }; 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, }; 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; }; 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, 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 ", nil, }; void contextval(Machine*, ulong*, ulong*, int), etherval(Machine*, ulong*, ulong*, int), ethererrval(Machine*, ulong*, ulong*, int), etherinval(Machine*, ulong*, ulong*, int), etheroutval(Machine*, ulong*, ulong*, int), faultval(Machine*, ulong*, ulong*, int), intrval(Machine*, ulong*, ulong*, int), inintrval(Machine*, ulong*, ulong*, int), loadval(Machine*, ulong*, ulong*, int), idleval(Machine*, ulong*, ulong*, int), memval(Machine*, ulong*, ulong*, int), swapval(Machine*, ulong*, ulong*, int), syscallval(Machine*, ulong*, ulong*, int), tlbmissval(Machine*, ulong*, ulong*, int), tlbpurgeval(Machine*, ulong*, ulong*, int), batteryval(Machine*, ulong*, ulong*, int), signalval(Machine*, ulong*, ulong*, int); Menu menu2 = {menu2str, nil}; int present[Nmenu2]; void (*newvaluefn[Nmenu2])(Machine*, ulong*, ulong*, int init) = { batteryval, contextval, etherval, ethererrval, etherinval, etheroutval, faultval, idleval, inintrval, intrval, loadval, memval, swapval, syscallval, tlbmissval, tlbpurgeval, signalval, }; Image *cols[Ncolor][3]; Graph *graph; Machine *mach; Font *mediumfont; char *mysysname; char argchars[] = "8bceEfiImlnpstw"; 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; ibuf, sizeof m->buf); if(n <= 0){ close(*fd); *fd = -1; return 0; } m->bufp = m->buf; m->ebufp = m->buf+n; 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, ulong v, ulong 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, ulong prev, ulong v, ulong 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, int vmax) { int i, c; c = g->colindex; draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x)); for(i=1; ir); 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, ulong v, ulong vmax) { char buf[32]; 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, "%ld", 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, 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; } /* 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", 0); 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", 0); } else execl("/bin/srvold9p", "srvold9p", "-s", 0); 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 initmach(Machine *m, 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, 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) && 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; } 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]; } void readmach(Machine *m, int init) { int n, i; ulong a[8]; 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); if(display != nil) /* else we're still initializing */ eresized(0); } if(m->remote){ notify(alarmed); alarm(5000); } if(needswap(init) && loadbuf(m, &m->swapfd) && readnums(m, nelem(m->devswap), a, 0)) 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; nnproc && readnums(m, nelem(m->devsysstat), a, 0); n++) for(i=0; idevsysstat); 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(m->remote){ alarm(0); notify(nil); } } 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 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; ilabel = 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 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; ulong 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 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; ulong 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; ir.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; ir.max.y), display->black, nil, ZP); j = dx/stringwidth(mediumfont, "0"); n = mach[i].nproc; if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */ j -= 3+(n>10)+(n>100); if(j <= 0) j = 1; snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n); }else snprint(buf, sizeof buf, "%.*s", j, mach[i].name); 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; jr.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; kblack, nil, ZP); ly -= mediumfont->height/2; string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, labs[k]); } } } } /* create graphs */ for(i=0; ir.max.y); if(i < nmach-1) machr.max.x = startx+(i+1)*dx - 1; y = starty; for(j=0; jndata; 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, "9999999"); 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= 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; char *s; ulong 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': s = ARGF(); if(s == nil) usage(); i = atoi(s); if(i > 0) sleeptime = 1000*i; break; case 'S': s = ARGF(); if(s == nil) usage(); scale = atof(s); 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; ilocking = 1; /* tell library we're using the display lock */ resize(); unlockdisplay(display); /* display is still locked from initdraw() */ for(;;){ for(i=0; i