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);
53 /* /net/ether0/stats */
75 uvlong devsysstat[10];
76 uvlong prevsysstat[10];
79 uvlong netetherstats[8];
80 uvlong prevetherstats[8];
81 uvlong batterystats[2];
82 uvlong netetherifstats[2];
85 /* big enough to hold /dev/sysstat even with many processors */
101 Ysqueeze = 2, /* vertical squeezing of label text */
102 Labspace = 2, /* room around label */
103 Dot = 2, /* height of dot */
104 Opwid = 5, /* strlen("add ") or strlen("drop ") */
105 Nlab = 3, /* max number of labels on y axis */
106 Lablen = 16, /* max length of label */
107 Lx = 4, /* label tick length */
136 char *menu2str[Nmenu2+1] = {
162 void contextval(Machine*, uvlong*, uvlong*, int),
163 etherval(Machine*, uvlong*, uvlong*, int),
164 ethererrval(Machine*, uvlong*, uvlong*, int),
165 etherinval(Machine*, uvlong*, uvlong*, int),
166 etheroutval(Machine*, uvlong*, uvlong*, int),
167 faultval(Machine*, uvlong*, uvlong*, int),
168 intrval(Machine*, uvlong*, uvlong*, int),
169 inintrval(Machine*, uvlong*, uvlong*, int),
170 loadval(Machine*, uvlong*, uvlong*, int),
171 idleval(Machine*, uvlong*, uvlong*, int),
172 memval(Machine*, uvlong*, uvlong*, int),
173 swapval(Machine*, uvlong*, uvlong*, int),
174 reclaimval(Machine*, uvlong*, uvlong*, int),
175 kernval(Machine*, uvlong*, uvlong*, int),
176 drawval(Machine*, uvlong*, uvlong*, int),
177 syscallval(Machine*, uvlong*, uvlong*, int),
178 tlbmissval(Machine*, uvlong*, uvlong*, int),
179 tlbpurgeval(Machine*, uvlong*, uvlong*, int),
180 batteryval(Machine*, uvlong*, uvlong*, int),
181 signalval(Machine*, uvlong*, uvlong*, int),
182 tempval(Machine*, uvlong*, uvlong*, int);
184 Menu menu2 = {menu2str, nil};
186 void (*newvaluefn[Nmenu2])(Machine*, uvlong*, uvlong*, int init) = {
210 Image *cols[Ncolor][3];
214 char argchars[] = "8bcdeEfiIkmlnprstwz";
216 int parity; /* toggled to avoid patterns in textured background */
218 int ngraph; /* totaly number is ngraph*nmach */
222 int sleeptime = 1000;
224 char *procnames[NPROC] = {"main", "input"};
232 for(i=0; i<NPROC; i++)
233 if(pids[i] && pids[i]!=pid)
234 postnote(PNPROC, pids[i], "kill");
244 fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
252 erealloc(void *v, ulong sz)
256 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
266 if((t = strdup(s)) == nil) {
267 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
274 mkcol(int i, int c0, int c1, int c2)
276 cols[i][0] = allocimagemix(display, c0, DWhite);
277 cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
278 cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
285 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
287 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
289 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
291 mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
293 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
295 cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
296 cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
297 cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
301 loadbuf(Machine *m, int *fd)
309 n = read(*fd, m->buf, sizeof m->buf-1);
322 label(Point p, int dy, char *text)
333 if(p.y+font->height-Ysqueeze > maxy)
335 w = chartorune(r, s);
337 w = runestringwidth(font, r);
340 runestring(screen, p, display->black, ZP, font, r);
341 p.y += font->height-Ysqueeze;
348 return Pt(x+parity, 0);
352 datapoint(Graph *g, int x, uvlong v, uvlong vmax)
358 y = ((double)v)/(vmax*scale);
361 * Arrange scale to cover a factor of 1000.
362 * vmax corresponds to the 100 mark.
363 * 10*vmax is the top of the scale.
369 /* 1 now corresponds to the top; -2 to the bottom; rescale */
377 p.y = g->r.max.y - Dy(g->r)*y - Dot;
380 if(p.y > g->r.max.y-Dot)
381 p.y = g->r.max.y-Dot;
386 drawdatum(Graph *g, int x, uvlong prev, uvlong v, uvlong vmax)
392 p = datapoint(g, x, v, vmax);
393 q = datapoint(g, x, prev, vmax);
395 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
396 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
397 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
399 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
400 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
401 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
407 redraw(Graph *g, uvlong vmax)
412 draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
413 for(i=1; i<Dx(g->r); i++)
414 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
415 drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
420 update1(Graph *g, uvlong v, uvlong vmax)
425 if(g->overflow && g->overtmp!=nil)
426 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
427 draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
428 drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
429 memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
433 overflow = (v>10*vmax*scale);
435 overflow = (v>vmax*scale);
436 if(overflow && g->overtmp!=nil){
438 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
439 sprint(buf, "%llud", v);
440 string(screen, g->overtmp->r.min, display->black, ZP, font, buf);
444 /* read one line of text from buffer and process integers */
446 readnums(Machine *m, int n, uvlong *a, int spanlines)
454 for(ep=m->bufp; ep<m->ebufp; ep++)
458 for(i=0; i<n && p<ep; i++){
459 while(p<ep && (!isascii(*p) || !isdigit(*p)) && *p!='-')
463 a[i] = strtoull(p, &p, 10);
472 readswap(Machine *m, uvlong *a)
476 if(strstr(m->buf, "memory\n")){
477 /* new /dev/swap - skip first 3 numbers */
478 if(!readnums(m, 7, a, 1))
488 if(m->bufp = strstr(m->buf, "reclaim")){
489 while(m->bufp > m->buf && m->bufp[-1] != '\n')
491 a[Reclaim] = strtoull(m->bufp, &m->bufp, 10);
492 while(*m->bufp++ == '/')
493 a[Maxreclaim] = strtoull(m->bufp, &m->bufp, 10);
498 if(m->bufp = strstr(m->buf, "kernel malloc")){
499 while(m->bufp > m->buf && m->bufp[-1] != '\n')
501 a[Kern] = strtoull(m->bufp, &m->bufp, 10);
502 while(*m->bufp++ == '/')
503 a[Maxkern] = strtoull(m->bufp, &m->bufp, 10);
508 if(m->bufp = strstr(m->buf, "kernel draw")){
509 while(m->bufp > m->buf && m->bufp[-1] != '\n')
511 a[Draw] = strtoull(m->bufp, &m->bufp, 10);
512 while(*m->bufp++ == '/')
513 a[Maxdraw] = strtoull(m->bufp, &m->bufp, 10);
526 return readnums(m, 4, a, 0);
546 for(i = 0; j >= 10; i++)
552 initmach(Machine *m, char *name)
556 char *p, mpt[256], buf[256];
558 p = strchr(name, '!');
563 m->name = estrdup(p);
564 m->shortname = shortname(p);
565 m->remote = (strcmp(p, mysysname) != 0);
572 snprint(mpt, sizeof mpt, "/n/%s", p);
577 fprint(2, "can't fork: %r\n");
580 execl("/bin/rimport", "rimport", name, "/", mpt, nil);
581 fprint(2, "can't exec: %r\n");
585 if(w == nil || w->pid != pid || w->msg[0] != '\0'){
592 snprint(buf, sizeof buf, "%s/dev/swap", mpt);
593 m->swapfd = open(buf, OREAD);
594 if(loadbuf(m, &m->swapfd) && readswap(m, a))
595 memmove(m->devswap, a, sizeof m->devswap);
597 snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
598 m->statsfd = open(buf, OREAD);
599 if(loadbuf(m, &m->statsfd)){
600 for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
605 m->lgproc = ilog10(m->nproc);
607 snprint(buf, sizeof buf, "%s/net/ether0/stats", mpt);
608 m->etherfd = open(buf, OREAD);
609 if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
610 memmove(m->netetherstats, a, sizeof m->netetherstats);
612 snprint(buf, sizeof buf, "%s/net/ether0/ifstats", mpt);
613 m->ifstatsfd = open(buf, OREAD);
614 if(loadbuf(m, &m->ifstatsfd)){
615 /* need to check that this is a wavelan interface */
616 if(strncmp(m->buf, "Signal: ", 8) == 0 && readnums(m, nelem(m->netetherifstats), a, 1))
617 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
620 snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
621 m->batteryfd = open(buf, OREAD);
622 if(m->batteryfd < 0){
623 snprint(buf, sizeof buf, "%s/mnt/acpi/battery", mpt);
624 m->batteryfd = open(buf, OREAD);
627 if(m->batteryfd >= 0){
628 if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
629 memmove(m->batterystats, a, sizeof(m->batterystats));
631 snprint(buf, sizeof buf, "%s/dev/battery", mpt);
632 m->bitsybatfd = open(buf, OREAD);
633 if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
634 memmove(m->batterystats, a, sizeof(m->batterystats));
636 snprint(buf, sizeof buf, "%s/dev/cputemp", mpt);
637 m->tempfd = open(buf, OREAD);
639 snprint(buf, sizeof buf, "%s/mnt/acpi/cputemp", mpt);
640 m->tempfd = open(buf, OREAD);
642 if(loadbuf(m, &m->tempfd))
643 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
651 alarmed(void *a, char *s)
653 if(strcmp(s, "alarm") == 0)
654 notejmp(a, catchalarm, 1);
661 return init | present[Mmem] | present[Mswap] | present[Mreclaim] | present[Mkern] | present[Mdraw];
668 return init | present[Mcontext] | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] |
669 present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
676 return init | present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
680 needbattery(int init)
682 return init | present[Mbattery];
688 return init | present[Msignal];
694 return init | present[Mtemp];
698 readmach(Machine *m, int init)
701 uvlong a[nelem(m->devsysstat)];
704 if(m->remote && (m->disable || setjmp(catchalarm))){
705 if (m->disable++ >= 5)
706 m->disable = 0; /* give it another chance */
707 memmove(m->devsysstat, m->prevsysstat, sizeof m->devsysstat);
708 memmove(m->netetherstats, m->prevetherstats, sizeof m->netetherstats);
711 snprint(buf, sizeof buf, "%s", m->name);
712 if (strcmp(m->name, buf) != 0){
714 m->name = estrdup(buf);
716 m->shortname = shortname(buf);
717 if(display != nil) /* else we're still initializing */
721 atnotify(alarmed, 1);
724 if(needswap(init) && loadbuf(m, &m->swapfd) && readswap(m, a))
725 memmove(m->devswap, a, sizeof m->devswap);
726 if(needstat(init) && loadbuf(m, &m->statsfd)){
727 memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
728 memset(m->devsysstat, 0, sizeof m->devsysstat);
729 for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
730 for(i=0; i<nelem(m->devsysstat); i++)
731 m->devsysstat[i] += a[i];
733 if(needether(init) && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
734 memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
735 memmove(m->netetherstats, a, sizeof m->netetherstats);
737 if(needsignal(init) && loadbuf(m, &m->ifstatsfd) && strncmp(m->buf, "Signal: ", 8)==0 && readnums(m, nelem(m->netetherifstats), a, 1)){
738 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
740 if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
741 memmove(m->batterystats, a, sizeof(m->batterystats));
742 if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
743 memmove(m->batterystats, a, sizeof(m->batterystats));
744 if(needtemp(init) && loadbuf(m, &m->tempfd))
745 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
749 atnotify(alarmed, 0);
754 memval(Machine *m, uvlong *v, uvlong *vmax, int)
756 *v = m->devswap[Mem];
757 *vmax = m->devswap[Maxmem];
763 swapval(Machine *m, uvlong *v, uvlong *vmax, int)
765 *v = m->devswap[Swap];
766 *vmax = m->devswap[Maxswap];
772 reclaimval(Machine *m, uvlong *v, uvlong *vmax, int)
774 *v = m->devswap[Reclaim];
775 *vmax = m->devswap[Maxreclaim];
781 kernval(Machine *m, uvlong *v, uvlong *vmax, int)
783 *v = m->devswap[Kern];
784 *vmax = m->devswap[Maxkern];
790 drawval(Machine *m, uvlong *v, uvlong *vmax, int)
792 *v = m->devswap[Draw];
793 *vmax = m->devswap[Maxdraw];
799 contextval(Machine *m, uvlong *v, uvlong *vmax, int init)
801 *v = (m->devsysstat[Context]-m->prevsysstat[Context])&0xffffffff;
802 *vmax = sleeptime*m->nproc;
808 * bug: need to factor in HZ
811 intrval(Machine *m, uvlong *v, uvlong *vmax, int init)
813 *v = (m->devsysstat[Interrupt]-m->prevsysstat[Interrupt])&0xffffffff;
814 *vmax = sleeptime*m->nproc*10;
816 *vmax = sleeptime*10;
820 syscallval(Machine *m, uvlong *v, uvlong *vmax, int init)
822 *v = (m->devsysstat[Syscall]-m->prevsysstat[Syscall])&0xffffffff;
823 *vmax = sleeptime*m->nproc;
829 faultval(Machine *m, uvlong *v, uvlong *vmax, int init)
831 *v = (m->devsysstat[Fault]-m->prevsysstat[Fault])&0xffffffff;
832 *vmax = sleeptime*m->nproc;
838 tlbmissval(Machine *m, uvlong *v, uvlong *vmax, int init)
840 *v = (m->devsysstat[TLBfault]-m->prevsysstat[TLBfault])&0xffffffff;
841 *vmax = (sleeptime/1000)*10*m->nproc;
843 *vmax = (sleeptime/1000)*10;
847 tlbpurgeval(Machine *m, uvlong *v, uvlong *vmax, int init)
849 *v = (m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge])&0xffffffff;
850 *vmax = (sleeptime/1000)*10*m->nproc;
852 *vmax = (sleeptime/1000)*10;
856 loadval(Machine *m, uvlong *v, uvlong *vmax, int init)
858 *v = m->devsysstat[Load];
859 *vmax = 1000*m->nproc;
865 idleval(Machine *m, uvlong *v, uvlong *vmax, int)
867 *v = m->devsysstat[Idle]/m->nproc;
872 inintrval(Machine *m, uvlong *v, uvlong *vmax, int)
874 *v = m->devsysstat[InIntr]/m->nproc;
879 etherval(Machine *m, uvlong *v, uvlong *vmax, int)
881 *v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
886 etherinval(Machine *m, uvlong *v, uvlong *vmax, int)
888 *v = m->netetherstats[In]-m->prevetherstats[In];
893 etheroutval(Machine *m, uvlong *v, uvlong *vmax, int)
895 *v = m->netetherstats[Out]-m->prevetherstats[Out];
900 ethererrval(Machine *m, uvlong *v, uvlong *vmax, int)
905 for(i=Err0; i<nelem(m->netetherstats); i++)
906 *v += m->netetherstats[i]-m->prevetherstats[i];
907 *vmax = (sleeptime/1000)*10;
911 batteryval(Machine *m, uvlong *v, uvlong *vmax, int)
913 *v = m->batterystats[0];
914 if(m->bitsybatfd >= 0)
915 *vmax = 184; // at least on my bitsy...
921 signalval(Machine *m, uvlong *v, uvlong *vmax, int)
926 l = m->netetherifstats[0];
928 * Range is seen to be from about -45 (strong) to -95 (weak); rescale
930 if(l == 0){ /* probably not present */
938 tempval(Machine *m, uvlong *v, uvlong *vmax, int)
944 if(l == ~0 || l == 0)
953 fprint(2, "usage: stats [-O] [-S scale] [-LY] [-%s] [machine...]\n", argchars);
964 if(n > nelem(menu2str))
966 /* avoid two adjacent graphs of same color */
967 if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
970 graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
971 for(i=0; i<nmach; i++)
972 for(j=0; j<ngraph; j++)
973 graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
976 for(i=0; i<nmach; i++){
977 g = &graph[i*ngraph+(ngraph-1)];
978 memset(g, 0, sizeof(Graph));
979 g->label = menu2str[n]+Opwid;
980 g->newvalue = newvaluefn[n];
981 g->update = update1; /* no other update functions yet */
983 g->colindex = nadd%Ncolor;
995 if(which > nelem(menu2str))
997 /* convert n to index in graph table */
999 for(i=0; i<ngraph; i++)
1000 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
1005 fprint(2, "stats: internal error can't drop graph\n");
1009 graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
1010 for(i=0; i<nmach; i++){
1012 graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
1013 free(ograph[i*ngraph+j].data);
1014 freeimage(ograph[i*ngraph+j].overtmp);
1015 for(j++; j<ngraph; j++)
1016 graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
1024 addmachine(char *name)
1027 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
1031 nmach = 0; /* a little dance to get us started with local machine by default */
1032 mach = erealloc(mach, (nmach+1)*sizeof(Machine));
1033 memset(mach+nmach, 0, sizeof(Machine));
1034 if (initmach(mach+nmach, name)){
1042 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
1047 g->newvalue(g->mach, &v, &vmax, 1);
1052 sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
1056 sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
1064 int i, j, n, w, maxw;
1065 char strs[Nlab][Lablen];
1068 for(i=0; i<ngraph; i++){
1069 /* choose value for rightmost graph */
1070 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
1072 w = stringwidth(font, strs[j]);
1083 int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
1087 char buf[128], labs[Nlab][Lablen];
1089 draw(screen, screen->r, display->white, nil, ZP);
1091 /* label left edge */
1092 x = screen->r.min.x;
1093 y = screen->r.min.y + Labspace+font->height+Labspace;
1094 dy = (screen->r.max.y - y)/ngraph;
1095 dx = Labspace+stringwidth(font, "0")+Labspace;
1098 for(i=0; i<ngraph; i++,y+=dy){
1099 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
1100 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
1101 label(Pt(x, y), dy, graph[i].label);
1102 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
1105 /* label top edge */
1106 dx = (screen->r.max.x - startx)/nmach;
1107 for(x=startx, i=0; i<nmach; i++,x+=dx){
1108 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
1109 j = dx/stringwidth(font, "0");
1111 if(n>1 && j>=1+3+mach[i].lgproc){ /* first char of name + (n) */
1112 j -= 3+mach[i].lgproc;
1115 snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].shortname, n);
1117 snprint(buf, sizeof buf, "%.*s", j, mach[i].shortname);
1118 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, font, buf);
1121 maxx = screen->r.max.x;
1123 /* label right, if requested */
1124 if(ylabels && dy>Nlab*(font->height+1)){
1127 /* else there's not enough room */
1129 draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
1131 for(j=0; j<ngraph; j++, y+=dy){
1132 /* choose value for rightmost graph */
1133 g = &graph[ngraph*(nmach-1)+j];
1134 labelstrs(g, labs, &nlab);
1135 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
1137 r.max.y = screen->r.max.y;
1138 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
1139 for(k=0; k<nlab; k++){
1140 ly = y + (dy*(nlab-k)/(nlab+1));
1141 draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
1142 ly -= font->height/2;
1143 string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, font, labs[k]);
1150 for(i=0; i<nmach; i++){
1151 machr = Rect(startx+i*dx, starty, startx+(i+1)*dx - 1, screen->r.max.y);
1155 for(j=0; j<ngraph; j++, y+=dy){
1156 g = &graph[i*ngraph+j];
1159 g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
1160 g->data = erealloc(g->data, g->ndata*sizeof(ulong));
1161 if(g->ndata > ondata)
1162 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
1166 g->r.max.y = y+dy - 1;
1168 g->r.max.y = screen->r.max.y;
1169 draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
1172 r.max.y = r.min.y+font->height;
1173 r.max.x = r.min.x+stringwidth(font, "999999999999");
1174 freeimage(g->overtmp);
1176 if(r.max.x <= g->r.max.x)
1177 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
1178 g->newvalue(g->mach, &v, &vmax, 0);
1185 flushimage(display, 1);
1191 lockdisplay(display);
1192 if(new && getwindow(display, Refnone) < 0) {
1193 fprint(2, "stats: can't reattach to window\n");
1194 killall("reattach");
1197 unlockdisplay(display);
1207 switch(eread(Emouse|Ekeyboard, &e)){
1209 if(e.mouse.buttons == 4){
1210 lockdisplay(display);
1211 for(i=0; i<Nmenu2; i++)
1213 memmove(menu2str[i], "drop ", Opwid);
1215 memmove(menu2str[i], "add ", Opwid);
1216 i = emenuhit(3, &e.mouse, &menu2);
1224 unlockdisplay(display);
1228 if(e.kbdc==Kdel || e.kbdc=='q')
1236 startproc(void (*f)(void), int index)
1240 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
1242 fprint(2, "stats: fork failed: %r\n");
1243 killall("fork failed");
1246 fprint(2, "stats: %s process exits\n", procnames[index]);
1248 killall("process died");
1256 main(int argc, char *argv[])
1260 uvlong v, vmax, nargs;
1266 mysysname = getenv("sysname");
1267 if(mysysname == nil){
1268 fprint(2, "stats: can't find $sysname: %r\n");
1275 secs = atof(EARGF(usage()));
1277 sleeptime = 1000*secs;
1280 scale = atof(EARGF(usage()));
1293 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
1295 args[nargs++] = ARGC();
1299 mach = emalloc(nmach*sizeof(Machine));
1300 initmach(&mach[0], mysysname);
1301 readmach(&mach[0], 1);
1304 for(i=j=0; i<argc; i++){
1305 if (addmachine(argv[i]))
1306 readmach(&mach[j++], 1);
1312 for(i=0; i<nargs; i++)
1315 fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
1328 addgraph(Metherout);
1349 addgraph(Metherout);
1350 addgraph(Methererr);
1353 addgraph(Mtlbpurge);
1363 addgraph(Mtlbpurge);
1385 for(i=0; i<nmach; i++)
1386 for(j=0; j<ngraph; j++)
1387 graph[i*ngraph+j].mach = &mach[i];
1389 if(initdraw(nil, nil, "stats") < 0){
1390 fprint(2, "stats: initdraw failed: %r\n");
1393 display->locking = 1; /* tell library we're using the display lock */
1395 einit(Emouse|Ekeyboard);
1396 startproc(inputproc, Inputproc);
1397 pids[Mainproc] = getpid();
1401 unlockdisplay(display); /* display is still locked from initdraw() */
1403 for(i=0; i<nmach; i++)
1404 readmach(&mach[i], 0);
1405 lockdisplay(display);
1407 for(i=0; i<nmach*ngraph; i++){
1408 graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
1411 graph[i].update(&graph[i], v, vmax);
1413 flushimage(display, 1);
1414 unlockdisplay(display);