9 #define MAXNUM 10 /* maximum number of numbers on data line */
11 typedef struct Graph Graph;
12 typedef struct Machine Machine;
21 void (*newvalue)(Machine*, uvlong*, uvlong*, int);
22 void (*update)(Graph*, uvlong, uvlong);
47 /* /net/ether0/stats */
69 uvlong devsysstat[10];
70 uvlong prevsysstat[10];
73 uvlong netetherstats[8];
74 uvlong prevetherstats[8];
75 uvlong batterystats[2];
76 uvlong netetherifstats[2];
79 /* big enough to hold /dev/sysstat even with many processors */
95 Ysqueeze = 2, /* vertical squeezing of label text */
96 Labspace = 2, /* room around label */
97 Dot = 2, /* height of dot */
98 Opwid = 5, /* strlen("add ") or strlen("drop ") */
99 Nlab = 3, /* max number of labels on y axis */
100 Lablen = 16, /* max length of label */
101 Lx = 4, /* label tick length */
127 char *menu2str[Nmenu2+1] = {
150 void contextval(Machine*, uvlong*, uvlong*, int),
151 etherval(Machine*, uvlong*, uvlong*, int),
152 ethererrval(Machine*, uvlong*, uvlong*, int),
153 etherinval(Machine*, uvlong*, uvlong*, int),
154 etheroutval(Machine*, uvlong*, uvlong*, int),
155 faultval(Machine*, uvlong*, uvlong*, int),
156 intrval(Machine*, uvlong*, uvlong*, int),
157 inintrval(Machine*, uvlong*, uvlong*, int),
158 loadval(Machine*, uvlong*, uvlong*, int),
159 idleval(Machine*, uvlong*, uvlong*, int),
160 memval(Machine*, uvlong*, uvlong*, int),
161 swapval(Machine*, uvlong*, uvlong*, int),
162 syscallval(Machine*, uvlong*, uvlong*, int),
163 tlbmissval(Machine*, uvlong*, uvlong*, int),
164 tlbpurgeval(Machine*, uvlong*, uvlong*, int),
165 batteryval(Machine*, uvlong*, uvlong*, int),
166 signalval(Machine*, uvlong*, uvlong*, int),
167 tempval(Machine*, uvlong*, uvlong*, int);
169 Menu menu2 = {menu2str, nil};
171 void (*newvaluefn[Nmenu2])(Machine*, uvlong*, uvlong*, int init) = {
192 Image *cols[Ncolor][3];
197 char argchars[] = "8bceEfiImlnpstwz";
199 int parity; /* toggled to avoid patterns in textured background */
201 int ngraph; /* totaly number is ngraph*nmach */
206 int sleeptime = 1000;
208 char *procnames[NPROC] = {"main", "mouse"};
216 for(i=0; i<NPROC; i++)
217 if(pids[i] && pids[i]!=pid)
218 postnote(PNPROC, pids[i], "kill");
228 fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
236 erealloc(void *v, ulong sz)
240 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
250 if((t = strdup(s)) == nil) {
251 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
258 mkcol(int i, int c0, int c1, int c2)
260 cols[i][0] = allocimagemix(display, c0, DWhite);
261 cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
262 cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
268 mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
269 if(mediumfont == nil)
273 mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
275 mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
277 mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
279 mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
281 mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
283 cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
284 cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
285 cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
289 loadbuf(Machine *m, int *fd)
297 n = read(*fd, m->buf, sizeof m->buf-1);
310 label(Point p, int dy, char *text)
321 if(p.y+mediumfont->height-Ysqueeze > maxy)
323 w = chartorune(r, s);
325 w = runestringwidth(mediumfont, r);
328 runestring(screen, p, display->black, ZP, mediumfont, r);
329 p.y += mediumfont->height-Ysqueeze;
336 return Pt(x+parity, 0);
340 datapoint(Graph *g, int x, uvlong v, uvlong vmax)
346 y = ((double)v)/(vmax*scale);
349 * Arrange scale to cover a factor of 1000.
350 * vmax corresponds to the 100 mark.
351 * 10*vmax is the top of the scale.
357 /* 1 now corresponds to the top; -2 to the bottom; rescale */
361 p.y = g->r.max.y - Dy(g->r)*y - Dot;
364 if(p.y > g->r.max.y-Dot)
365 p.y = g->r.max.y-Dot;
370 drawdatum(Graph *g, int x, uvlong prev, uvlong v, uvlong vmax)
376 p = datapoint(g, x, v, vmax);
377 q = datapoint(g, x, prev, vmax);
379 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
380 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
381 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
383 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
384 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
385 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
391 redraw(Graph *g, uvlong vmax)
396 draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
397 for(i=1; i<Dx(g->r); i++)
398 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
399 drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
404 update1(Graph *g, uvlong v, uvlong vmax)
409 if(g->overflow && g->overtmp!=nil)
410 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
411 draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
412 drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
413 memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
417 overflow = (v>10*vmax*scale);
419 overflow = (v>vmax*scale);
420 if(overflow && g->overtmp!=nil){
422 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
423 sprint(buf, "%llud", v);
424 string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, buf);
428 /* read one line of text from buffer and process integers */
430 readnums(Machine *m, int n, uvlong *a, int spanlines)
438 for(ep=m->bufp; ep<m->ebufp; ep++)
442 for(i=0; i<n && p<ep; i++){
443 while(p<ep && (!isascii(*p) || !isdigit(*p)) && *p!='-')
447 a[i] = strtoull(p, &p, 10);
455 /* Network on fd1, mount driver on fd0 */
462 fprint(2, "stats: can't pipe: %r\n");
466 switch(rfork(RFNOWAIT|RFPROC|RFFDG)) {
468 sysfatal("rfork record module");
475 execl("/bin/aux/fcall", "fcall", nil);
476 fprint(2, "stats: can't exec fcall: %r\n");
489 connect9fs(char *addr)
494 fprint(2, "connect9fs...");
495 na = netmkaddr(addr, 0, "9fs");
497 fprint(2, "dial %s...", na);
498 if((fd = dial(na, 0, dir, 0)) < 0)
501 fprint(2, "dir %s...", dir);
502 // if(strstr(dir, "tcp"))
515 switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
529 fd = open("/sys/log/cpu", OWRITE);
534 execl("/bin/srvold9p", "srvold9p", "-ds", nil);
536 execl("/bin/srvold9p", "srvold9p", "-s", nil);
550 connectexportfs(char *addr)
552 char buf[ERRMAX], dir[256], *na;
558 na = netmkaddr(addr, 0, "exportfs");
559 if((fd = dial(na, 0, dir, 0)) < 0)
562 ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client");
566 n = write(fd, tree, strlen(tree));
572 strcpy(buf, "can't read tree");
573 n = read(fd, buf, sizeof buf - 1);
574 if(n!=2 || buf[0]!='O' || buf[1]!='K'){
575 buf[sizeof buf - 1] = '\0';
576 werrstr("bad remote tree: %s\n", buf);
581 // if(strstr(dir, "tcp"))
591 readswap(Machine *m, uvlong *a)
593 if(strstr(m->buf, "memory\n")){
594 /* new /dev/swap - skip first 3 numbers */
595 if(!readnums(m, 7, a, 1))
603 return readnums(m, nelem(m->devswap), a, 0);
623 for(i = 0; j >= 10; i++)
629 initmach(Machine *m, char *name)
633 char *p, mpt[256], buf[256];
635 p = strchr(name, '!');
640 m->name = estrdup(p);
641 m->shortname = shortname(p);
642 m->remote = (strcmp(p, mysysname) != 0);
646 snprint(mpt, sizeof mpt, "/n/%s", p);
647 fd = connectexportfs(name);
649 fprint(2, "can't connect to %s: %r\n", name);
652 /* BUG? need to use amount() now? */
653 if(mount(fd, -1, mpt, MREPL, "") < 0){
654 fprint(2, "stats: mount %s on %s failed (%r); trying /n/sid\n", name, mpt);
655 strcpy(mpt, "/n/sid");
656 if(mount(fd, -1, mpt, MREPL, "") < 0){
657 fprint(2, "stats: mount %s on %s failed: %r\n", name, mpt);
663 snprint(buf, sizeof buf, "%s/dev/swap", mpt);
664 m->swapfd = open(buf, OREAD);
665 if(loadbuf(m, &m->swapfd) && readswap(m, a))
666 memmove(m->devswap, a, sizeof m->devswap);
668 m->devswap[Maxswap] = 100;
669 m->devswap[Maxmem] = 100;
672 snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
673 m->statsfd = open(buf, OREAD);
674 if(loadbuf(m, &m->statsfd)){
675 for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
680 m->lgproc = ilog10(m->nproc);
682 snprint(buf, sizeof buf, "%s/net/ether0/stats", mpt);
683 m->etherfd = open(buf, OREAD);
684 if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
685 memmove(m->netetherstats, a, sizeof m->netetherstats);
687 snprint(buf, sizeof buf, "%s/net/ether0/ifstats", mpt);
688 m->ifstatsfd = open(buf, OREAD);
689 if(loadbuf(m, &m->ifstatsfd)){
690 /* need to check that this is a wavelan interface */
691 if(strncmp(m->buf, "Signal: ", 8) == 0 && readnums(m, nelem(m->netetherifstats), a, 1))
692 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
695 snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
696 m->batteryfd = open(buf, OREAD);
698 if(m->batteryfd >= 0){
699 if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
700 memmove(m->batterystats, a, sizeof(m->batterystats));
702 snprint(buf, sizeof buf, "%s/dev/battery", mpt);
703 m->bitsybatfd = open(buf, OREAD);
704 if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
705 memmove(m->batterystats, a, sizeof(m->batterystats));
707 snprint(buf, sizeof buf, "%s/dev/cputemp", mpt);
708 m->tempfd = open(buf, OREAD);
709 if(loadbuf(m, &m->tempfd))
710 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
718 alarmed(void *a, char *s)
720 if(strcmp(s, "alarm") == 0)
721 notejmp(a, catchalarm, 1);
728 return init | present[Mmem] | present[Mswap];
735 return init | present[Mcontext] | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] |
736 present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
743 return init | present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
747 needbattery(int init)
749 return init | present[Mbattery];
755 return init | present[Msignal];
761 return init | present[Mtemp];
765 readmach(Machine *m, int init)
768 uvlong a[nelem(m->devsysstat)];
771 if(m->remote && (m->disable || setjmp(catchalarm))){
772 if (m->disable++ >= 5)
773 m->disable = 0; /* give it another chance */
774 memmove(m->devsysstat, m->prevsysstat, sizeof m->devsysstat);
775 memmove(m->netetherstats, m->prevetherstats, sizeof m->netetherstats);
778 snprint(buf, sizeof buf, "%s", m->name);
779 if (strcmp(m->name, buf) != 0){
781 m->name = estrdup(buf);
783 m->shortname = shortname(buf);
784 if(display != nil) /* else we're still initializing */
791 if(needswap(init) && loadbuf(m, &m->swapfd) && readswap(m, a))
792 memmove(m->devswap, a, sizeof m->devswap);
793 if(needstat(init) && loadbuf(m, &m->statsfd)){
794 memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
795 memset(m->devsysstat, 0, sizeof m->devsysstat);
796 for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
797 for(i=0; i<nelem(m->devsysstat); i++)
798 m->devsysstat[i] += a[i];
800 if(needether(init) && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
801 memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
802 memmove(m->netetherstats, a, sizeof m->netetherstats);
804 if(needsignal(init) && loadbuf(m, &m->ifstatsfd) && strncmp(m->buf, "Signal: ", 8)==0 && readnums(m, nelem(m->netetherifstats), a, 1)){
805 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
807 if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
808 memmove(m->batterystats, a, sizeof(m->batterystats));
809 if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
810 memmove(m->batterystats, a, sizeof(m->batterystats));
811 if(needtemp(init) && loadbuf(m, &m->tempfd))
812 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
821 memval(Machine *m, uvlong *v, uvlong *vmax, int)
823 *v = m->devswap[Mem];
824 *vmax = m->devswap[Maxmem];
828 swapval(Machine *m, uvlong *v, uvlong *vmax, int)
830 *v = m->devswap[Swap];
831 *vmax = m->devswap[Maxswap];
835 contextval(Machine *m, uvlong *v, uvlong *vmax, int init)
837 *v = m->devsysstat[Context]-m->prevsysstat[Context];
838 *vmax = sleeptime*m->nproc;
844 * bug: need to factor in HZ
847 intrval(Machine *m, uvlong *v, uvlong *vmax, int init)
849 *v = m->devsysstat[Interrupt]-m->prevsysstat[Interrupt];
850 *vmax = sleeptime*m->nproc*10;
852 *vmax = sleeptime*10;
856 syscallval(Machine *m, uvlong *v, uvlong *vmax, int init)
858 *v = m->devsysstat[Syscall]-m->prevsysstat[Syscall];
859 *vmax = sleeptime*m->nproc;
865 faultval(Machine *m, uvlong *v, uvlong *vmax, int init)
867 *v = m->devsysstat[Fault]-m->prevsysstat[Fault];
868 *vmax = sleeptime*m->nproc;
874 tlbmissval(Machine *m, uvlong *v, uvlong *vmax, int init)
876 *v = m->devsysstat[TLBfault]-m->prevsysstat[TLBfault];
877 *vmax = (sleeptime/1000)*10*m->nproc;
879 *vmax = (sleeptime/1000)*10;
883 tlbpurgeval(Machine *m, uvlong *v, uvlong *vmax, int init)
885 *v = m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge];
886 *vmax = (sleeptime/1000)*10*m->nproc;
888 *vmax = (sleeptime/1000)*10;
892 loadval(Machine *m, uvlong *v, uvlong *vmax, int init)
894 *v = m->devsysstat[Load];
895 *vmax = 1000*m->nproc;
901 idleval(Machine *m, uvlong *v, uvlong *vmax, int)
903 *v = m->devsysstat[Idle]/m->nproc;
908 inintrval(Machine *m, uvlong *v, uvlong *vmax, int)
910 *v = m->devsysstat[InIntr]/m->nproc;
915 etherval(Machine *m, uvlong *v, uvlong *vmax, int init)
917 *v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
918 *vmax = sleeptime*m->nproc;
924 etherinval(Machine *m, uvlong *v, uvlong *vmax, int init)
926 *v = m->netetherstats[In]-m->prevetherstats[In];
927 *vmax = sleeptime*m->nproc;
933 etheroutval(Machine *m, uvlong *v, uvlong *vmax, int init)
935 *v = m->netetherstats[Out]-m->prevetherstats[Out];
936 *vmax = sleeptime*m->nproc;
942 ethererrval(Machine *m, uvlong *v, uvlong *vmax, int init)
947 for(i=Err0; i<nelem(m->netetherstats); i++)
948 *v += m->netetherstats[i];
949 *vmax = (sleeptime/1000)*10*m->nproc;
951 *vmax = (sleeptime/1000)*10;
955 batteryval(Machine *m, uvlong *v, uvlong *vmax, int)
957 *v = m->batterystats[0];
958 if(m->bitsybatfd >= 0)
959 *vmax = 184; // at least on my bitsy...
965 signalval(Machine *m, uvlong *v, uvlong *vmax, int)
970 l = m->netetherifstats[0];
972 * Range is seen to be from about -45 (strong) to -95 (weak); rescale
974 if(l == 0){ /* probably not present */
982 tempval(Machine *m, uvlong *v, uvlong *vmax, int)
988 if(l == ~0 || l == 0)
997 fprint(2, "usage: stats [-O] [-S scale] [-LY] [-%s] [machine...]\n", argchars);
1008 if(n > nelem(menu2str))
1010 /* avoid two adjacent graphs of same color */
1011 if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
1014 graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
1015 for(i=0; i<nmach; i++)
1016 for(j=0; j<ngraph; j++)
1017 graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
1020 for(i=0; i<nmach; i++){
1021 g = &graph[i*ngraph+(ngraph-1)];
1022 memset(g, 0, sizeof(Graph));
1023 g->label = menu2str[n]+Opwid;
1024 g->newvalue = newvaluefn[n];
1025 g->update = update1; /* no other update functions yet */
1027 g->colindex = nadd%Ncolor;
1034 dropgraph(int which)
1039 if(which > nelem(menu2str))
1041 /* convert n to index in graph table */
1043 for(i=0; i<ngraph; i++)
1044 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
1049 fprint(2, "stats: internal error can't drop graph\n");
1053 graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
1054 for(i=0; i<nmach; i++){
1056 graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
1057 free(ograph[i*ngraph+j].data);
1058 freeimage(ograph[i*ngraph+j].overtmp);
1059 for(j++; j<ngraph; j++)
1060 graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
1068 addmachine(char *name)
1071 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
1075 nmach = 0; /* a little dance to get us started with local machine by default */
1076 mach = erealloc(mach, (nmach+1)*sizeof(Machine));
1077 memset(mach+nmach, 0, sizeof(Machine));
1078 if (initmach(mach+nmach, name)){
1086 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
1091 g->newvalue(g->mach, &v, &vmax, 1);
1094 sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
1098 sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
1106 int i, j, n, w, maxw;
1107 char strs[Nlab][Lablen];
1110 for(i=0; i<ngraph; i++){
1111 /* choose value for rightmost graph */
1112 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
1114 w = stringwidth(mediumfont, strs[j]);
1125 int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
1129 char buf[128], labs[Nlab][Lablen];
1131 draw(screen, screen->r, display->white, nil, ZP);
1133 /* label left edge */
1134 x = screen->r.min.x;
1135 y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
1136 dy = (screen->r.max.y - y)/ngraph;
1137 dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
1140 for(i=0; i<ngraph; i++,y+=dy){
1141 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
1142 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
1143 label(Pt(x, y), dy, graph[i].label);
1144 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
1147 /* label top edge */
1148 dx = (screen->r.max.x - startx)/nmach;
1149 for(x=startx, i=0; i<nmach; i++,x+=dx){
1150 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
1151 j = dx/stringwidth(mediumfont, "0");
1153 if(n>1 && j>=1+3+mach[i].lgproc){ /* first char of name + (n) */
1154 j -= 3+mach[i].lgproc;
1157 snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].shortname, n);
1159 snprint(buf, sizeof buf, "%.*s", j, mach[i].shortname);
1160 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, mediumfont, buf);
1163 maxx = screen->r.max.x;
1165 /* label right, if requested */
1166 if(ylabels && dy>Nlab*(mediumfont->height+1)){
1168 if(wid < (maxx-startx)-30){
1169 /* else there's not enough room */
1171 draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
1173 for(j=0; j<ngraph; j++, y+=dy){
1174 /* choose value for rightmost graph */
1175 g = &graph[ngraph*(nmach-1)+j];
1176 labelstrs(g, labs, &nlab);
1177 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
1179 r.max.y = screen->r.max.y;
1180 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
1181 for(k=0; k<nlab; k++){
1182 ly = y + (dy*(nlab-k)/(nlab+1));
1183 draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
1184 ly -= mediumfont->height/2;
1185 string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, labs[k]);
1192 for(i=0; i<nmach; i++){
1193 machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
1195 machr.max.x = startx+(i+1)*dx - 1;
1197 for(j=0; j<ngraph; j++, y+=dy){
1198 g = &graph[i*ngraph+j];
1201 g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
1202 g->data = erealloc(g->data, g->ndata*sizeof(ulong));
1203 if(g->ndata > ondata)
1204 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
1208 g->r.max.y = y+dy - 1;
1210 g->r.max.y = screen->r.max.y;
1211 draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
1214 r.max.y = r.min.y+mediumfont->height;
1215 r.max.x = r.min.x+stringwidth(mediumfont, "999999999999");
1216 freeimage(g->overtmp);
1218 if(r.max.x <= g->r.max.x)
1219 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
1220 g->newvalue(g->mach, &v, &vmax, 0);
1225 flushimage(display, 1);
1231 lockdisplay(display);
1232 if(new && getwindow(display, Refnone) < 0) {
1233 fprint(2, "stats: can't reattach to window\n");
1234 killall("reattach");
1237 unlockdisplay(display);
1248 if(mouse.buttons == 4){
1249 lockdisplay(display);
1250 for(i=0; i<Nmenu2; i++)
1252 memmove(menu2str[i], "drop ", Opwid);
1254 memmove(menu2str[i], "add ", Opwid);
1255 i = emenuhit(3, &mouse, &menu2);
1263 unlockdisplay(display);
1269 startproc(void (*f)(void), int index)
1273 switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
1275 fprint(2, "stats: fork failed: %r\n");
1276 killall("fork failed");
1279 fprint(2, "stats: %s process exits\n", procnames[index]);
1281 killall("process died");
1289 main(int argc, char *argv[])
1293 uvlong v, vmax, nargs;
1297 mysysname = getenv("sysname");
1298 if(mysysname == nil){
1299 fprint(2, "stats: can't find $sysname: %r\n");
1302 mysysname = estrdup(mysysname);
1307 secs = atof(EARGF(usage()));
1309 sleeptime = 1000*secs;
1312 scale = atof(EARGF(usage()));
1326 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
1328 args[nargs++] = ARGC();
1332 mach = emalloc(nmach*sizeof(Machine));
1333 initmach(&mach[0], mysysname);
1334 readmach(&mach[0], 1);
1336 for(i=j=0; i<argc; i++){
1337 if (addmachine(argv[i]))
1338 readmach(&mach[j++], 1);
1344 for(i=0; i<nargs; i++)
1347 fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
1360 addgraph(Metherout);
1381 addgraph(Metherout);
1382 addgraph(Methererr);
1385 addgraph(Mtlbpurge);
1392 addgraph(Mtlbpurge);
1408 for(i=0; i<nmach; i++)
1409 for(j=0; j<ngraph; j++)
1410 graph[i*ngraph+j].mach = &mach[i];
1412 if(initdraw(nil, nil, "stats") < 0){
1413 fprint(2, "stats: initdraw failed: %r\n");
1419 startproc(mouseproc, Mouseproc);
1420 pids[Mainproc] = getpid();
1421 display->locking = 1; /* tell library we're using the display lock */
1425 unlockdisplay(display); /* display is still locked from initdraw() */
1427 for(i=0; i<nmach; i++)
1428 readmach(&mach[i], 0);
1429 lockdisplay(display);
1431 for(i=0; i<nmach*ngraph; i++){
1432 graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
1433 graph[i].update(&graph[i], v, vmax);
1435 flushimage(display, 1);
1436 unlockdisplay(display);