8 #define MAXNUM 10 /* maximum number of numbers on data line */
10 typedef struct Graph Graph;
11 typedef struct Machine Machine;
20 void (*newvalue)(Machine*, uvlong*, uvlong*, int);
21 void (*update)(Graph*, uvlong, uvlong);
51 /* /net/ether0/stats */
73 uvlong devsysstat[10];
74 uvlong prevsysstat[10];
77 uvlong netetherstats[8];
78 uvlong prevetherstats[8];
79 uvlong batterystats[2];
80 uvlong netetherifstats[2];
83 /* big enough to hold /dev/sysstat even with many processors */
99 Ysqueeze = 2, /* vertical squeezing of label text */
100 Labspace = 2, /* room around label */
101 Dot = 2, /* height of dot */
102 Opwid = 5, /* strlen("add ") or strlen("drop ") */
103 Nlab = 3, /* max number of labels on y axis */
104 Lablen = 16, /* max length of label */
105 Lx = 4, /* label tick length */
133 char *menu2str[Nmenu2+1] = {
158 void contextval(Machine*, uvlong*, uvlong*, int),
159 etherval(Machine*, uvlong*, uvlong*, int),
160 ethererrval(Machine*, uvlong*, uvlong*, int),
161 etherinval(Machine*, uvlong*, uvlong*, int),
162 etheroutval(Machine*, uvlong*, uvlong*, int),
163 faultval(Machine*, uvlong*, uvlong*, int),
164 intrval(Machine*, uvlong*, uvlong*, int),
165 inintrval(Machine*, uvlong*, uvlong*, int),
166 loadval(Machine*, uvlong*, uvlong*, int),
167 idleval(Machine*, uvlong*, uvlong*, int),
168 memval(Machine*, uvlong*, uvlong*, int),
169 swapval(Machine*, uvlong*, uvlong*, int),
170 kernval(Machine*, uvlong*, uvlong*, int),
171 drawval(Machine*, uvlong*, uvlong*, int),
172 syscallval(Machine*, uvlong*, uvlong*, int),
173 tlbmissval(Machine*, uvlong*, uvlong*, int),
174 tlbpurgeval(Machine*, uvlong*, uvlong*, int),
175 batteryval(Machine*, uvlong*, uvlong*, int),
176 signalval(Machine*, uvlong*, uvlong*, int),
177 tempval(Machine*, uvlong*, uvlong*, int);
179 Menu menu2 = {menu2str, nil};
181 void (*newvaluefn[Nmenu2])(Machine*, uvlong*, uvlong*, int init) = {
204 Image *cols[Ncolor][3];
208 char argchars[] = "8bcdeEfiIkmlnpstwz";
210 int parity; /* toggled to avoid patterns in textured background */
212 int ngraph; /* totaly number is ngraph*nmach */
216 int sleeptime = 1000;
218 char *procnames[NPROC] = {"main", "input"};
226 for(i=0; i<NPROC; i++)
227 if(pids[i] && pids[i]!=pid)
228 postnote(PNPROC, pids[i], "kill");
238 fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
246 erealloc(void *v, ulong sz)
250 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
260 if((t = strdup(s)) == nil) {
261 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
268 mkcol(int i, int c0, int c1, int c2)
270 cols[i][0] = allocimagemix(display, c0, DWhite);
271 cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
272 cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
279 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
281 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
283 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
285 mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
287 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
289 cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
290 cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
291 cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
295 loadbuf(Machine *m, int *fd)
303 n = read(*fd, m->buf, sizeof m->buf-1);
316 label(Point p, int dy, char *text)
327 if(p.y+font->height-Ysqueeze > maxy)
329 w = chartorune(r, s);
331 w = runestringwidth(font, r);
334 runestring(screen, p, display->black, ZP, font, r);
335 p.y += font->height-Ysqueeze;
342 return Pt(x+parity, 0);
346 datapoint(Graph *g, int x, uvlong v, uvlong vmax)
352 y = ((double)v)/(vmax*scale);
355 * Arrange scale to cover a factor of 1000.
356 * vmax corresponds to the 100 mark.
357 * 10*vmax is the top of the scale.
363 /* 1 now corresponds to the top; -2 to the bottom; rescale */
371 p.y = g->r.max.y - Dy(g->r)*y - Dot;
374 if(p.y > g->r.max.y-Dot)
375 p.y = g->r.max.y-Dot;
380 drawdatum(Graph *g, int x, uvlong prev, uvlong v, uvlong vmax)
386 p = datapoint(g, x, v, vmax);
387 q = datapoint(g, x, prev, vmax);
389 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
390 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
391 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
393 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
394 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
395 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
401 redraw(Graph *g, uvlong vmax)
406 draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
407 for(i=1; i<Dx(g->r); i++)
408 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
409 drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
414 update1(Graph *g, uvlong v, uvlong vmax)
419 if(g->overflow && g->overtmp!=nil)
420 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
421 draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
422 drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
423 memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
427 overflow = (v>10*vmax*scale);
429 overflow = (v>vmax*scale);
430 if(overflow && g->overtmp!=nil){
432 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
433 sprint(buf, "%llud", v);
434 string(screen, g->overtmp->r.min, display->black, ZP, font, buf);
438 /* read one line of text from buffer and process integers */
440 readnums(Machine *m, int n, uvlong *a, int spanlines)
448 for(ep=m->bufp; ep<m->ebufp; ep++)
452 for(i=0; i<n && p<ep; i++){
453 while(p<ep && (!isascii(*p) || !isdigit(*p)) && *p!='-')
457 a[i] = strtoull(p, &p, 10);
466 readswap(Machine *m, uvlong *a)
468 if(strstr(m->buf, "memory\n")){
469 /* new /dev/swap - skip first 3 numbers */
470 if(!readnums(m, 7, a, 1))
479 if(m->bufp = strstr(m->buf, "kernel malloc")){
480 while(m->bufp > m->buf && m->bufp[-1] != '\n')
482 a[4] = strtoull(m->bufp, &m->bufp, 10);
483 while(*m->bufp++ == '/')
484 a[5] = strtoull(m->bufp, &m->bufp, 10);
489 if(m->bufp = strstr(m->buf, "kernel draw")){
490 while(m->bufp > m->buf && m->bufp[-1] != '\n')
492 a[6] = strtoull(m->bufp, &m->bufp, 10);
493 while(*m->bufp++ == '/')
494 a[7] = strtoull(m->bufp, &m->bufp, 10);
504 return readnums(m, 4, a, 0);
524 for(i = 0; j >= 10; i++)
530 initmach(Machine *m, char *name)
534 char *p, mpt[256], buf[256];
536 p = strchr(name, '!');
541 m->name = estrdup(p);
542 m->shortname = shortname(p);
543 m->remote = (strcmp(p, mysysname) != 0);
550 snprint(mpt, sizeof mpt, "/n/%s", p);
551 snprint(buf, sizeof buf, "rimport %q / %q || import %q / %q", name, mpt, name, mpt);
556 fprint(2, "can't fork: %r\n");
559 execl("/bin/rc", "rc", "-c", buf, nil);
560 fprint(2, "can't exec: %r\n");
564 if(w == nil || w->pid != pid || w->msg[0] != '\0'){
571 snprint(buf, sizeof buf, "%s/dev/swap", mpt);
572 m->swapfd = open(buf, OREAD);
573 if(loadbuf(m, &m->swapfd) && readswap(m, a))
574 memmove(m->devswap, a, sizeof m->devswap);
576 snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
577 m->statsfd = open(buf, OREAD);
578 if(loadbuf(m, &m->statsfd)){
579 for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
584 m->lgproc = ilog10(m->nproc);
586 snprint(buf, sizeof buf, "%s/net/ether0/stats", mpt);
587 m->etherfd = open(buf, OREAD);
588 if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
589 memmove(m->netetherstats, a, sizeof m->netetherstats);
591 snprint(buf, sizeof buf, "%s/net/ether0/ifstats", mpt);
592 m->ifstatsfd = open(buf, OREAD);
593 if(loadbuf(m, &m->ifstatsfd)){
594 /* need to check that this is a wavelan interface */
595 if(strncmp(m->buf, "Signal: ", 8) == 0 && readnums(m, nelem(m->netetherifstats), a, 1))
596 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
599 snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
600 m->batteryfd = open(buf, OREAD);
601 if(m->batteryfd < 0){
602 snprint(buf, sizeof buf, "%s/mnt/acpi/battery", mpt);
603 m->batteryfd = open(buf, OREAD);
606 if(m->batteryfd >= 0){
607 if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
608 memmove(m->batterystats, a, sizeof(m->batterystats));
610 snprint(buf, sizeof buf, "%s/dev/battery", mpt);
611 m->bitsybatfd = open(buf, OREAD);
612 if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
613 memmove(m->batterystats, a, sizeof(m->batterystats));
615 snprint(buf, sizeof buf, "%s/dev/cputemp", mpt);
616 m->tempfd = open(buf, OREAD);
618 snprint(buf, sizeof buf, "%s/mnt/acpi/cputemp", mpt);
619 m->tempfd = open(buf, OREAD);
621 if(loadbuf(m, &m->tempfd))
622 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
630 alarmed(void *a, char *s)
632 if(strcmp(s, "alarm") == 0)
633 notejmp(a, catchalarm, 1);
640 return init | present[Mmem] | present[Mswap] | present[Mkern] | present[Mdraw];
647 return init | present[Mcontext] | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] |
648 present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
655 return init | present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
659 needbattery(int init)
661 return init | present[Mbattery];
667 return init | present[Msignal];
673 return init | present[Mtemp];
677 readmach(Machine *m, int init)
680 uvlong a[nelem(m->devsysstat)];
683 if(m->remote && (m->disable || setjmp(catchalarm))){
684 if (m->disable++ >= 5)
685 m->disable = 0; /* give it another chance */
686 memmove(m->devsysstat, m->prevsysstat, sizeof m->devsysstat);
687 memmove(m->netetherstats, m->prevetherstats, sizeof m->netetherstats);
690 snprint(buf, sizeof buf, "%s", m->name);
691 if (strcmp(m->name, buf) != 0){
693 m->name = estrdup(buf);
695 m->shortname = shortname(buf);
696 if(display != nil) /* else we're still initializing */
700 atnotify(alarmed, 1);
703 if(needswap(init) && loadbuf(m, &m->swapfd) && readswap(m, a))
704 memmove(m->devswap, a, sizeof m->devswap);
705 if(needstat(init) && loadbuf(m, &m->statsfd)){
706 memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
707 memset(m->devsysstat, 0, sizeof m->devsysstat);
708 for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
709 for(i=0; i<nelem(m->devsysstat); i++)
710 m->devsysstat[i] += a[i];
712 if(needether(init) && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
713 memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
714 memmove(m->netetherstats, a, sizeof m->netetherstats);
716 if(needsignal(init) && loadbuf(m, &m->ifstatsfd) && strncmp(m->buf, "Signal: ", 8)==0 && readnums(m, nelem(m->netetherifstats), a, 1)){
717 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
719 if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
720 memmove(m->batterystats, a, sizeof(m->batterystats));
721 if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
722 memmove(m->batterystats, a, sizeof(m->batterystats));
723 if(needtemp(init) && loadbuf(m, &m->tempfd))
724 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
728 atnotify(alarmed, 0);
733 memval(Machine *m, uvlong *v, uvlong *vmax, int)
735 *v = m->devswap[Mem];
736 *vmax = m->devswap[Maxmem];
742 swapval(Machine *m, uvlong *v, uvlong *vmax, int)
744 *v = m->devswap[Swap];
745 *vmax = m->devswap[Maxswap];
751 kernval(Machine *m, uvlong *v, uvlong *vmax, int)
753 *v = m->devswap[Kern];
754 *vmax = m->devswap[Maxkern];
760 drawval(Machine *m, uvlong *v, uvlong *vmax, int)
762 *v = m->devswap[Draw];
763 *vmax = m->devswap[Maxdraw];
769 contextval(Machine *m, uvlong *v, uvlong *vmax, int init)
771 *v = (m->devsysstat[Context]-m->prevsysstat[Context])&0xffffffff;
772 *vmax = sleeptime*m->nproc;
778 * bug: need to factor in HZ
781 intrval(Machine *m, uvlong *v, uvlong *vmax, int init)
783 *v = (m->devsysstat[Interrupt]-m->prevsysstat[Interrupt])&0xffffffff;
784 *vmax = sleeptime*m->nproc*10;
786 *vmax = sleeptime*10;
790 syscallval(Machine *m, uvlong *v, uvlong *vmax, int init)
792 *v = (m->devsysstat[Syscall]-m->prevsysstat[Syscall])&0xffffffff;
793 *vmax = sleeptime*m->nproc;
799 faultval(Machine *m, uvlong *v, uvlong *vmax, int init)
801 *v = (m->devsysstat[Fault]-m->prevsysstat[Fault])&0xffffffff;
802 *vmax = sleeptime*m->nproc;
808 tlbmissval(Machine *m, uvlong *v, uvlong *vmax, int init)
810 *v = (m->devsysstat[TLBfault]-m->prevsysstat[TLBfault])&0xffffffff;
811 *vmax = (sleeptime/1000)*10*m->nproc;
813 *vmax = (sleeptime/1000)*10;
817 tlbpurgeval(Machine *m, uvlong *v, uvlong *vmax, int init)
819 *v = (m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge])&0xffffffff;
820 *vmax = (sleeptime/1000)*10*m->nproc;
822 *vmax = (sleeptime/1000)*10;
826 loadval(Machine *m, uvlong *v, uvlong *vmax, int init)
828 *v = m->devsysstat[Load];
829 *vmax = 1000*m->nproc;
835 idleval(Machine *m, uvlong *v, uvlong *vmax, int)
837 *v = m->devsysstat[Idle]/m->nproc;
842 inintrval(Machine *m, uvlong *v, uvlong *vmax, int)
844 *v = m->devsysstat[InIntr]/m->nproc;
849 etherval(Machine *m, uvlong *v, uvlong *vmax, int init)
851 *v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
852 *vmax = sleeptime*m->nproc;
858 etherinval(Machine *m, uvlong *v, uvlong *vmax, int init)
860 *v = m->netetherstats[In]-m->prevetherstats[In];
861 *vmax = sleeptime*m->nproc;
867 etheroutval(Machine *m, uvlong *v, uvlong *vmax, int init)
869 *v = m->netetherstats[Out]-m->prevetherstats[Out];
870 *vmax = sleeptime*m->nproc;
876 ethererrval(Machine *m, uvlong *v, uvlong *vmax, int init)
881 for(i=Err0; i<nelem(m->netetherstats); i++)
882 *v += m->netetherstats[i];
883 *vmax = (sleeptime/1000)*10*m->nproc;
885 *vmax = (sleeptime/1000)*10;
889 batteryval(Machine *m, uvlong *v, uvlong *vmax, int)
891 *v = m->batterystats[0];
892 if(m->bitsybatfd >= 0)
893 *vmax = 184; // at least on my bitsy...
899 signalval(Machine *m, uvlong *v, uvlong *vmax, int)
904 l = m->netetherifstats[0];
906 * Range is seen to be from about -45 (strong) to -95 (weak); rescale
908 if(l == 0){ /* probably not present */
916 tempval(Machine *m, uvlong *v, uvlong *vmax, int)
922 if(l == ~0 || l == 0)
931 fprint(2, "usage: stats [-O] [-S scale] [-LY] [-%s] [machine...]\n", argchars);
942 if(n > nelem(menu2str))
944 /* avoid two adjacent graphs of same color */
945 if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
948 graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
949 for(i=0; i<nmach; i++)
950 for(j=0; j<ngraph; j++)
951 graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
954 for(i=0; i<nmach; i++){
955 g = &graph[i*ngraph+(ngraph-1)];
956 memset(g, 0, sizeof(Graph));
957 g->label = menu2str[n]+Opwid;
958 g->newvalue = newvaluefn[n];
959 g->update = update1; /* no other update functions yet */
961 g->colindex = nadd%Ncolor;
973 if(which > nelem(menu2str))
975 /* convert n to index in graph table */
977 for(i=0; i<ngraph; i++)
978 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
983 fprint(2, "stats: internal error can't drop graph\n");
987 graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
988 for(i=0; i<nmach; i++){
990 graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
991 free(ograph[i*ngraph+j].data);
992 freeimage(ograph[i*ngraph+j].overtmp);
993 for(j++; j<ngraph; j++)
994 graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
1002 addmachine(char *name)
1005 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
1009 nmach = 0; /* a little dance to get us started with local machine by default */
1010 mach = erealloc(mach, (nmach+1)*sizeof(Machine));
1011 memset(mach+nmach, 0, sizeof(Machine));
1012 if (initmach(mach+nmach, name)){
1020 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
1025 g->newvalue(g->mach, &v, &vmax, 1);
1030 sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
1034 sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
1042 int i, j, n, w, maxw;
1043 char strs[Nlab][Lablen];
1046 for(i=0; i<ngraph; i++){
1047 /* choose value for rightmost graph */
1048 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
1050 w = stringwidth(font, strs[j]);
1061 int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
1065 char buf[128], labs[Nlab][Lablen];
1067 draw(screen, screen->r, display->white, nil, ZP);
1069 /* label left edge */
1070 x = screen->r.min.x;
1071 y = screen->r.min.y + Labspace+font->height+Labspace;
1072 dy = (screen->r.max.y - y)/ngraph;
1073 dx = Labspace+stringwidth(font, "0")+Labspace;
1076 for(i=0; i<ngraph; i++,y+=dy){
1077 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
1078 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
1079 label(Pt(x, y), dy, graph[i].label);
1080 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
1083 /* label top edge */
1084 dx = (screen->r.max.x - startx)/nmach;
1085 for(x=startx, i=0; i<nmach; i++,x+=dx){
1086 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
1087 j = dx/stringwidth(font, "0");
1089 if(n>1 && j>=1+3+mach[i].lgproc){ /* first char of name + (n) */
1090 j -= 3+mach[i].lgproc;
1093 snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].shortname, n);
1095 snprint(buf, sizeof buf, "%.*s", j, mach[i].shortname);
1096 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, font, buf);
1099 maxx = screen->r.max.x;
1101 /* label right, if requested */
1102 if(ylabels && dy>Nlab*(font->height+1)){
1104 if(wid < (maxx-startx)-30){
1105 /* else there's not enough room */
1107 draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
1109 for(j=0; j<ngraph; j++, y+=dy){
1110 /* choose value for rightmost graph */
1111 g = &graph[ngraph*(nmach-1)+j];
1112 labelstrs(g, labs, &nlab);
1113 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
1115 r.max.y = screen->r.max.y;
1116 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
1117 for(k=0; k<nlab; k++){
1118 ly = y + (dy*(nlab-k)/(nlab+1));
1119 draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
1120 ly -= font->height/2;
1121 string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, font, labs[k]);
1128 for(i=0; i<nmach; i++){
1129 machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
1131 machr.max.x = startx+(i+1)*dx - 1;
1133 for(j=0; j<ngraph; j++, y+=dy){
1134 g = &graph[i*ngraph+j];
1137 g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
1138 g->data = erealloc(g->data, g->ndata*sizeof(ulong));
1139 if(g->ndata > ondata)
1140 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
1144 g->r.max.y = y+dy - 1;
1146 g->r.max.y = screen->r.max.y;
1147 draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
1150 r.max.y = r.min.y+font->height;
1151 r.max.x = r.min.x+stringwidth(font, "999999999999");
1152 freeimage(g->overtmp);
1154 if(r.max.x <= g->r.max.x)
1155 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
1156 g->newvalue(g->mach, &v, &vmax, 0);
1161 flushimage(display, 1);
1167 lockdisplay(display);
1168 if(new && getwindow(display, Refnone) < 0) {
1169 fprint(2, "stats: can't reattach to window\n");
1170 killall("reattach");
1173 unlockdisplay(display);
1183 switch(eread(Emouse|Ekeyboard, &e)){
1185 if(e.mouse.buttons == 4){
1186 lockdisplay(display);
1187 for(i=0; i<Nmenu2; i++)
1189 memmove(menu2str[i], "drop ", Opwid);
1191 memmove(menu2str[i], "add ", Opwid);
1192 i = emenuhit(3, &e.mouse, &menu2);
1200 unlockdisplay(display);
1204 if(e.kbdc==Kdel || e.kbdc=='q')
1212 startproc(void (*f)(void), int index)
1216 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
1218 fprint(2, "stats: fork failed: %r\n");
1219 killall("fork failed");
1222 fprint(2, "stats: %s process exits\n", procnames[index]);
1224 killall("process died");
1232 main(int argc, char *argv[])
1236 uvlong v, vmax, nargs;
1242 mysysname = getenv("sysname");
1243 if(mysysname == nil){
1244 fprint(2, "stats: can't find $sysname: %r\n");
1251 secs = atof(EARGF(usage()));
1253 sleeptime = 1000*secs;
1256 scale = atof(EARGF(usage()));
1269 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
1271 args[nargs++] = ARGC();
1275 mach = emalloc(nmach*sizeof(Machine));
1276 initmach(&mach[0], mysysname);
1277 readmach(&mach[0], 1);
1280 for(i=j=0; i<argc; i++){
1281 if (addmachine(argv[i]))
1282 readmach(&mach[j++], 1);
1288 for(i=0; i<nargs; i++)
1291 fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
1304 addgraph(Metherout);
1325 addgraph(Metherout);
1326 addgraph(Methererr);
1329 addgraph(Mtlbpurge);
1336 addgraph(Mtlbpurge);
1358 for(i=0; i<nmach; i++)
1359 for(j=0; j<ngraph; j++)
1360 graph[i*ngraph+j].mach = &mach[i];
1362 if(initdraw(nil, nil, "stats") < 0){
1363 fprint(2, "stats: initdraw failed: %r\n");
1366 display->locking = 1; /* tell library we're using the display lock */
1368 einit(Emouse|Ekeyboard);
1369 startproc(inputproc, Inputproc);
1370 pids[Mainproc] = getpid();
1374 unlockdisplay(display); /* display is still locked from initdraw() */
1376 for(i=0; i<nmach; i++)
1377 readmach(&mach[i], 0);
1378 lockdisplay(display);
1380 for(i=0; i<nmach*ngraph; i++){
1381 graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
1382 graph[i].update(&graph[i], v, vmax);
1384 flushimage(display, 1);
1385 unlockdisplay(display);