]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/stats.c
stats: add kernel malloc and kernel draw allocation size graphs
[plan9front.git] / sys / src / cmd / stats.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <auth.h>
5 #include <fcall.h>
6 #include <draw.h>
7 #include <event.h>
8 #include <keyboard.h>
9
10 #define MAXNUM  10      /* maximum number of numbers on data line */
11
12 typedef struct Graph    Graph;
13 typedef struct Machine  Machine;
14
15 struct Graph
16 {
17         int             colindex;
18         Rectangle       r;
19         int             *data;
20         int             ndata;
21         char            *label;
22         void            (*newvalue)(Machine*, uvlong*, uvlong*, int);
23         void            (*update)(Graph*, uvlong, uvlong);
24         Machine         *mach;
25         int             overflow;
26         Image           *overtmp;
27 };
28
29 enum
30 {
31         /* old /dev/swap */
32         Mem             = 0,
33         Maxmem,
34         Swap,
35         Maxswap,
36
37         Kern,
38         Maxkern,
39         Draw,
40         Maxdraw,
41
42         /* /dev/sysstats */
43         Procno  = 0,
44         Context,
45         Interrupt,
46         Syscall,
47         Fault,
48         TLBfault,
49         TLBpurge,
50         Load,
51         Idle,
52         InIntr,
53         /* /net/ether0/stats */
54         In              = 0,
55         Link,
56         Out,
57         Err0,
58 };
59
60 struct Machine
61 {
62         char            *name;
63         char            *shortname;
64         int             remote;
65         int             statsfd;
66         int             swapfd;
67         int             etherfd;
68         int             ifstatsfd;
69         int             batteryfd;
70         int             bitsybatfd;
71         int             tempfd;
72         int             disable;
73
74         uvlong          devswap[8];
75         uvlong          devsysstat[10];
76         uvlong          prevsysstat[10];
77         int             nproc;
78         int             lgproc;
79         uvlong          netetherstats[8];
80         uvlong          prevetherstats[8];
81         uvlong          batterystats[2];
82         uvlong          netetherifstats[2];
83         uvlong          temp[10];
84
85         /* big enough to hold /dev/sysstat even with many processors */
86         char            buf[8*1024];
87         char            *bufp;
88         char            *ebufp;
89 };
90
91 enum
92 {
93         Mainproc,
94         Inputproc,
95         NPROC,
96 };
97
98 enum
99 {
100         Ncolor          = 6,
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 */
108 };
109
110 enum Menu2
111 {
112         Mbattery,
113         Mcontext,
114         Mether,
115         Methererr,
116         Metherin,
117         Metherout,
118         Mfault,
119         Midle,
120         Minintr,
121         Mintr,
122         Mload,
123         Mmem,
124         Mswap,
125         Mkern,
126         Mdraw,
127         Msyscall,
128         Mtlbmiss,
129         Mtlbpurge,
130         Msignal,
131         Mtemp,
132         Nmenu2,
133 };
134
135 char    *menu2str[Nmenu2+1] = {
136         "add  battery ",
137         "add  context ",
138         "add  ether   ",
139         "add  ethererr",
140         "add  etherin ",
141         "add  etherout",
142         "add  fault   ",
143         "add  idle    ",
144         "add  inintr  ",
145         "add  intr    ",
146         "add  load    ",
147         "add  mem     ",
148         "add  swap    ",
149         "add  kern    ",
150         "add  draw    ",
151         "add  syscall ",
152         "add  tlbmiss ",
153         "add  tlbpurge",
154         "add  802.11b ",
155         "add  temp    ",
156         nil,
157 };
158
159
160 void    contextval(Machine*, uvlong*, uvlong*, int),
161         etherval(Machine*, uvlong*, uvlong*, int),
162         ethererrval(Machine*, uvlong*, uvlong*, int),
163         etherinval(Machine*, uvlong*, uvlong*, int),
164         etheroutval(Machine*, uvlong*, uvlong*, int),
165         faultval(Machine*, uvlong*, uvlong*, int),
166         intrval(Machine*, uvlong*, uvlong*, int),
167         inintrval(Machine*, uvlong*, uvlong*, int),
168         loadval(Machine*, uvlong*, uvlong*, int),
169         idleval(Machine*, uvlong*, uvlong*, int),
170         memval(Machine*, uvlong*, uvlong*, int),
171         swapval(Machine*, uvlong*, uvlong*, int),
172         kernval(Machine*, uvlong*, uvlong*, int),
173         drawval(Machine*, uvlong*, uvlong*, int),
174         syscallval(Machine*, uvlong*, uvlong*, int),
175         tlbmissval(Machine*, uvlong*, uvlong*, int),
176         tlbpurgeval(Machine*, uvlong*, uvlong*, int),
177         batteryval(Machine*, uvlong*, uvlong*, int),
178         signalval(Machine*, uvlong*, uvlong*, int),
179         tempval(Machine*, uvlong*, uvlong*, int);
180
181 Menu    menu2 = {menu2str, nil};
182 int     present[Nmenu2];
183 void    (*newvaluefn[Nmenu2])(Machine*, uvlong*, uvlong*, int init) = {
184         batteryval,
185         contextval,
186         etherval,
187         ethererrval,
188         etherinval,
189         etheroutval,
190         faultval,
191         idleval,
192         inintrval,
193         intrval,
194         loadval,
195         memval,
196         swapval,
197         kernval,
198         drawval,
199         syscallval,
200         tlbmissval,
201         tlbpurgeval,
202         signalval,
203         tempval,
204 };
205
206 Image   *cols[Ncolor][3];
207 Graph   *graph;
208 Machine *mach;
209 char    *mysysname;
210 char    argchars[] = "8bcdeEfiIkmlnpstwz";
211 int     pids[NPROC];
212 int     parity; /* toggled to avoid patterns in textured background */
213 int     nmach;
214 int     ngraph; /* totaly number is ngraph*nmach */
215 double  scale = 1.0;
216 int     logscale = 0;
217 int     ylabels = 0;
218 int     oldsystem = 0;
219 int     sleeptime = 1000;
220
221 char    *procnames[NPROC] = {"main", "input"};
222
223 void
224 killall(char *s)
225 {
226         int i, pid;
227
228         pid = getpid();
229         for(i=0; i<NPROC; i++)
230                 if(pids[i] && pids[i]!=pid)
231                         postnote(PNPROC, pids[i], "kill");
232         exits(s);
233 }
234
235 void*
236 emalloc(ulong sz)
237 {
238         void *v;
239         v = malloc(sz);
240         if(v == nil) {
241                 fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
242                 killall("mem");
243         }
244         memset(v, 0, sz);
245         return v;
246 }
247
248 void*
249 erealloc(void *v, ulong sz)
250 {
251         v = realloc(v, sz);
252         if(v == nil) {
253                 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
254                 killall("mem");
255         }
256         return v;
257 }
258
259 char*
260 estrdup(char *s)
261 {
262         char *t;
263         if((t = strdup(s)) == nil) {
264                 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
265                 killall("mem");
266         }
267         return t;
268 }
269
270 void
271 mkcol(int i, int c0, int c1, int c2)
272 {
273         cols[i][0] = allocimagemix(display, c0, DWhite);
274         cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
275         cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
276 }
277
278 void
279 colinit(void)
280 {
281         /* Peach */
282         mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
283         /* Aqua */
284         mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
285         /* Yellow */
286         mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
287         /* Green */
288         mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
289         /* Blue */
290         mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
291         /* Grey */
292         cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
293         cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
294         cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
295 }
296
297 int
298 loadbuf(Machine *m, int *fd)
299 {
300         int n;
301
302
303         if(*fd < 0)
304                 return 0;
305         seek(*fd, 0, 0);
306         n = read(*fd, m->buf, sizeof m->buf-1);
307         if(n <= 0){
308                 close(*fd);
309                 *fd = -1;
310                 return 0;
311         }
312         m->bufp = m->buf;
313         m->ebufp = m->buf+n;
314         m->buf[n] = 0;
315         return 1;
316 }
317
318 void
319 label(Point p, int dy, char *text)
320 {
321         char *s;
322         Rune r[2];
323         int w, maxw, maxy;
324
325         p.x += Labspace;
326         maxy = p.y+dy;
327         maxw = 0;
328         r[1] = '\0';
329         for(s=text; *s; ){
330                 if(p.y+font->height-Ysqueeze > maxy)
331                         break;
332                 w = chartorune(r, s);
333                 s += w;
334                 w = runestringwidth(font, r);
335                 if(w > maxw)
336                         maxw = w;
337                 runestring(screen, p, display->black, ZP, font, r);
338                 p.y += font->height-Ysqueeze;
339         }
340 }
341
342 Point
343 paritypt(int x)
344 {
345         return Pt(x+parity, 0);
346 }
347
348 Point
349 datapoint(Graph *g, int x, uvlong v, uvlong vmax)
350 {
351         Point p;
352         double y;
353
354         p.x = x;
355         y = ((double)v)/(vmax*scale);
356         if(logscale){
357                 /*
358                  * Arrange scale to cover a factor of 1000.
359                  * vmax corresponds to the 100 mark.
360                  * 10*vmax is the top of the scale.
361                  */
362                 if(y <= 0.)
363                         y = 0;
364                 else{
365                         y = log10(y);
366                         /* 1 now corresponds to the top; -2 to the bottom; rescale */
367                         y = (y+2.)/3.;
368                 }
369         }
370         if(y >= 1.)
371                 y = 1;
372         if(y <= 0.)
373                 y = 0;
374         p.y = g->r.max.y - Dy(g->r)*y - Dot;
375         if(p.y < g->r.min.y)
376                 p.y = g->r.min.y;
377         if(p.y > g->r.max.y-Dot)
378                 p.y = g->r.max.y-Dot;
379         return p;
380 }
381
382 void
383 drawdatum(Graph *g, int x, uvlong prev, uvlong v, uvlong vmax)
384 {
385         int c;
386         Point p, q;
387
388         c = g->colindex;
389         p = datapoint(g, x, v, vmax);
390         q = datapoint(g, x, prev, vmax);
391         if(p.y < q.y){
392                 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
393                 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
394                 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
395         }else{
396                 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
397                 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
398                 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
399         }
400
401 }
402
403 void
404 redraw(Graph *g, uvlong vmax)
405 {
406         int i, c;
407
408         c = g->colindex;
409         draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
410         for(i=1; i<Dx(g->r); i++)
411                 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
412         drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
413         g->overflow = 0;
414 }
415
416 void
417 update1(Graph *g, uvlong v, uvlong vmax)
418 {
419         char buf[48];
420         int overflow;
421
422         if(g->overflow && g->overtmp!=nil)
423                 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
424         draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
425         drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
426         memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
427         g->data[0] = v;
428         g->overflow = 0;
429         if(logscale)
430                 overflow = (v>10*vmax*scale);
431         else
432                 overflow = (v>vmax*scale);
433         if(overflow && g->overtmp!=nil){
434                 g->overflow = 1;
435                 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
436                 sprint(buf, "%llud", v);
437                 string(screen, g->overtmp->r.min, display->black, ZP, font, buf);
438         }
439 }
440
441 /* read one line of text from buffer and process integers */
442 int
443 readnums(Machine *m, int n, uvlong *a, int spanlines)
444 {
445         int i;
446         char *p, *ep;
447
448         if(spanlines)
449                 ep = m->ebufp;
450         else
451                 for(ep=m->bufp; ep<m->ebufp; ep++)
452                         if(*ep == '\n')
453                                 break;
454         p = m->bufp;
455         for(i=0; i<n && p<ep; i++){
456                 while(p<ep && (!isascii(*p) || !isdigit(*p)) && *p!='-')
457                         p++;
458                 if(p == ep)
459                         break;
460                 a[i] = strtoull(p, &p, 10);
461         }
462         if(ep < m->ebufp)
463                 ep++;
464         m->bufp = ep;
465         return i == n;
466 }
467
468 /* Network on fd1, mount driver on fd0 */
469 static int
470 filter(int fd)
471 {
472         int p[2];
473
474         if(pipe(p) < 0){
475                 fprint(2, "stats: can't pipe: %r\n");
476                 killall("pipe");
477         }
478
479         switch(rfork(RFNOWAIT|RFPROC|RFFDG)) {
480         case -1:
481                 sysfatal("rfork record module");
482         case 0:
483                 dup(fd, 1);
484                 close(fd);
485                 dup(p[0], 0);
486                 close(p[0]);
487                 close(p[1]);
488                 execl("/bin/aux/fcall", "fcall", nil);
489                 fprint(2, "stats: can't exec fcall: %r\n");
490                 killall("fcall");
491         default:
492                 close(fd);
493                 close(p[0]);
494         }
495         return p[1];
496 }
497
498 /*
499  * 9fs
500  */
501 int
502 connect9fs(char *addr)
503 {
504         char dir[256], *na;
505         int fd;
506
507         fprint(2, "connect9fs...");
508         na = netmkaddr(addr, 0, "9fs");
509
510         fprint(2, "dial %s...", na);
511         if((fd = dial(na, 0, dir, 0)) < 0)
512                 return -1;
513
514         fprint(2, "dir %s...", dir);
515 //      if(strstr(dir, "tcp"))
516 //              fd = filter(fd);
517         return fd;
518 }
519
520 int
521 old9p(int fd)
522 {
523         int p[2];
524
525         if(pipe(p) < 0)
526                 return -1;
527
528         switch(rfork(RFPROC|RFFDG|RFNAMEG)) {
529         case -1:
530                 return -1;
531         case 0:
532                 if(fd != 1){
533                         dup(fd, 1);
534                         close(fd);
535                 }
536                 if(p[0] != 0){
537                         dup(p[0], 0);
538                         close(p[0]);
539                 }
540                 close(p[1]);
541                 if(0){
542                         fd = open("/sys/log/cpu", OWRITE);
543                         if(fd != 2){
544                                 dup(fd, 2);
545                                 close(fd);
546                         }
547                         execl("/bin/srvold9p", "srvold9p", "-ds", nil);
548                 } else
549                         execl("/bin/srvold9p", "srvold9p", "-s", nil);
550                 return -1;
551         default:
552                 close(fd);
553                 close(p[0]);
554         }
555         return p[1];
556 }
557
558
559 /*
560  * exportfs
561  */
562 int
563 connectexportfs(char *addr)
564 {
565         char buf[ERRMAX], dir[256], *na;
566         int fd, n;
567         char *tree;
568         AuthInfo *ai;
569
570         tree = "/";
571         na = netmkaddr(addr, 0, "exportfs");
572         if((fd = dial(na, 0, dir, 0)) < 0)
573                 return -1;
574
575         ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client");
576         if(ai == nil)
577                 return -1;
578         auth_freeAI(ai);
579
580         n = write(fd, tree, strlen(tree));
581         if(n < 0){
582                 close(fd);
583                 return -1;
584         }
585
586         strcpy(buf, "can't read tree");
587         n = read(fd, buf, sizeof buf - 1);
588         if(n!=2 || buf[0]!='O' || buf[1]!='K'){
589                 buf[sizeof buf - 1] = '\0';
590                 werrstr("bad remote tree: %s\n", buf);
591                 close(fd);
592                 return -1;
593         }
594
595 //      if(strstr(dir, "tcp"))
596 //              fd = filter(fd);
597
598         if(oldsystem)
599                 return old9p(fd);
600
601         return fd;
602 }
603
604 int
605 readswap(Machine *m, uvlong *a)
606 {
607         if(strstr(m->buf, "memory\n")){
608                 /* new /dev/swap - skip first 3 numbers */
609                 if(!readnums(m, 7, a, 1))
610                         return 0;
611                 a[0] = a[3];
612                 a[1] = a[4];
613                 a[2] = a[5];
614                 a[3] = a[6];
615
616                 a[4] = 0;
617                 a[5] = 0;
618                 if(m->bufp = strstr(m->buf, "kernel malloc")){
619                         while(m->bufp > m->buf && m->bufp[-1] != '\n')
620                                 m->bufp--;
621                         a[4] = strtoull(m->bufp, &m->bufp, 10);
622                         while(*m->bufp++ == '/')
623                                 a[5] = strtoull(m->bufp, &m->bufp, 10);
624                 }
625
626                 a[6] = 0;
627                 a[7] = 0;
628                 if(m->bufp = strstr(m->buf, "kernel draw")){
629                         while(m->bufp > m->buf && m->bufp[-1] != '\n')
630                                 m->bufp--;
631                         a[6] = strtoull(m->bufp, &m->bufp, 10);
632                         while(*m->bufp++ == '/')
633                                 a[7] = strtoull(m->bufp, &m->bufp, 10);
634                 }
635
636                 return 1;
637         }
638
639         a[4] = 0;
640         a[5] = 0;
641         a[6] = 0;
642         a[7] = 0;
643         return readnums(m, 4, a, 0);
644 }
645
646 char*
647 shortname(char *s)
648 {
649         char *p, *e;
650
651         p = estrdup(s);
652         e = strchr(p, '.');
653         if(e)
654                 *e = 0;
655         return p;
656 }
657
658 int
659 ilog10(uvlong j)
660 {
661         int i;
662
663         for(i = 0; j >= 10; i++)
664                 j /= 10;
665         return i;
666 }
667
668 int
669 initmach(Machine *m, char *name)
670 {
671         int n, fd;
672         uvlong a[MAXNUM];
673         char *p, mpt[256], buf[256];
674
675         p = strchr(name, '!');
676         if(p)
677                 p++;
678         else
679                 p = name;
680         m->name = estrdup(p);
681         m->shortname = shortname(p);
682         m->remote = (strcmp(p, mysysname) != 0);
683         if(m->remote == 0)
684                 strcpy(mpt, "");
685         else{
686                 snprint(mpt, sizeof mpt, "/n/%s", p);
687                 fd = connectexportfs(name);
688                 if(fd < 0){
689                         fprint(2, "can't connect to %s: %r\n", name);
690                         return 0;
691                 }
692                 /* BUG? need to use amount() now? */
693                 if(mount(fd, -1, mpt, MREPL, "") < 0){
694                         fprint(2, "stats: mount %s on %s failed (%r); trying /n/sid\n", name, mpt);
695                         strcpy(mpt, "/n/sid");
696                         if(mount(fd, -1, mpt, MREPL, "") < 0){
697                                 fprint(2, "stats: mount %s on %s failed: %r\n", name, mpt);
698                                 return 0;
699                         }
700                 }
701         }
702
703         snprint(buf, sizeof buf, "%s/dev/swap", mpt);
704         m->swapfd = open(buf, OREAD);
705         if(loadbuf(m, &m->swapfd) && readswap(m, a))
706                 memmove(m->devswap, a, sizeof m->devswap);
707
708         snprint(buf, sizeof buf, "%s/dev/sysstat", mpt);
709         m->statsfd = open(buf, OREAD);
710         if(loadbuf(m, &m->statsfd)){
711                 for(n=0; readnums(m, nelem(m->devsysstat), a, 0); n++)
712                         ;
713                 m->nproc = n;
714         }else
715                 m->nproc = 1;
716         m->lgproc = ilog10(m->nproc);
717
718         snprint(buf, sizeof buf, "%s/net/ether0/stats", mpt);
719         m->etherfd = open(buf, OREAD);
720         if(loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1))
721                 memmove(m->netetherstats, a, sizeof m->netetherstats);
722
723         snprint(buf, sizeof buf, "%s/net/ether0/ifstats", mpt);
724         m->ifstatsfd = open(buf, OREAD);
725         if(loadbuf(m, &m->ifstatsfd)){
726                 /* need to check that this is a wavelan interface */
727                 if(strncmp(m->buf, "Signal: ", 8) == 0 && readnums(m, nelem(m->netetherifstats), a, 1))
728                         memmove(m->netetherifstats, a, sizeof m->netetherifstats);
729         }
730
731         snprint(buf, sizeof buf, "%s/mnt/apm/battery", mpt);
732         m->batteryfd = open(buf, OREAD);
733         m->bitsybatfd = -1;
734         if(m->batteryfd >= 0){
735                 if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
736                         memmove(m->batterystats, a, sizeof(m->batterystats));
737         }else{
738                 snprint(buf, sizeof buf, "%s/dev/battery", mpt);
739                 m->bitsybatfd = open(buf, OREAD);
740                 if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
741                         memmove(m->batterystats, a, sizeof(m->batterystats));
742         }
743         snprint(buf, sizeof buf, "%s/dev/cputemp", mpt);
744         m->tempfd = open(buf, OREAD);
745         if(loadbuf(m, &m->tempfd))
746                 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
747                          m->temp[n] = a[0];
748         return 1;
749 }
750
751 jmp_buf catchalarm;
752
753 int
754 alarmed(void *a, char *s)
755 {
756         if(strcmp(s, "alarm") == 0)
757                 notejmp(a, catchalarm, 1);
758         return 0;
759 }
760
761 int
762 needswap(int init)
763 {
764         return init | present[Mmem] | present[Mswap] | present[Mkern] | present[Mdraw];
765 }
766
767
768 int
769 needstat(int init)
770 {
771         return init | present[Mcontext]  | present[Mfault] | present[Mintr] | present[Mload] | present[Midle] |
772                 present[Minintr] | present[Msyscall] | present[Mtlbmiss] | present[Mtlbpurge];
773 }
774
775
776 int
777 needether(int init)
778 {
779         return init | present[Mether] | present[Metherin] | present[Metherout] | present[Methererr];
780 }
781
782 int
783 needbattery(int init)
784 {
785         return init | present[Mbattery];
786 }
787
788 int
789 needsignal(int init)
790 {
791         return init | present[Msignal];
792 }
793
794 int
795 needtemp(int init)
796 {
797         return init | present[Mtemp];
798 }
799
800 void
801 readmach(Machine *m, int init)
802 {
803         int n, i;
804         uvlong a[nelem(m->devsysstat)];
805         char buf[32];
806
807         if(m->remote && (m->disable || setjmp(catchalarm))){
808                 if (m->disable++ >= 5)
809                         m->disable = 0; /* give it another chance */
810                 memmove(m->devsysstat, m->prevsysstat, sizeof m->devsysstat);
811                 memmove(m->netetherstats, m->prevetherstats, sizeof m->netetherstats);
812                 return;
813         }
814         snprint(buf, sizeof buf, "%s", m->name);
815         if (strcmp(m->name, buf) != 0){
816                 free(m->name);
817                 m->name = estrdup(buf);
818                 free(m->shortname);
819                 m->shortname = shortname(buf);
820                 if(display != nil)      /* else we're still initializing */
821                         eresized(0);
822         }
823         if(m->remote){
824                 atnotify(alarmed, 1);
825                 alarm(5000);
826         }
827         if(needswap(init) && loadbuf(m, &m->swapfd) && readswap(m, a))
828                 memmove(m->devswap, a, sizeof m->devswap);
829         if(needstat(init) && loadbuf(m, &m->statsfd)){
830                 memmove(m->prevsysstat, m->devsysstat, sizeof m->devsysstat);
831                 memset(m->devsysstat, 0, sizeof m->devsysstat);
832                 for(n=0; n<m->nproc && readnums(m, nelem(m->devsysstat), a, 0); n++)
833                         for(i=0; i<nelem(m->devsysstat); i++)
834                                 m->devsysstat[i] += a[i];
835         }
836         if(needether(init) && loadbuf(m, &m->etherfd) && readnums(m, nelem(m->netetherstats), a, 1)){
837                 memmove(m->prevetherstats, m->netetherstats, sizeof m->netetherstats);
838                 memmove(m->netetherstats, a, sizeof m->netetherstats);
839         }
840         if(needsignal(init) && loadbuf(m, &m->ifstatsfd) && strncmp(m->buf, "Signal: ", 8)==0 && readnums(m, nelem(m->netetherifstats), a, 1)){
841                 memmove(m->netetherifstats, a, sizeof m->netetherifstats);
842         }
843         if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
844                 memmove(m->batterystats, a, sizeof(m->batterystats));
845         if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
846                 memmove(m->batterystats, a, sizeof(m->batterystats));
847         if(needtemp(init) && loadbuf(m, &m->tempfd))
848                 for(n=0; n < nelem(m->temp) && readnums(m, 2, a, 0); n++)
849                          m->temp[n] = a[0];
850         if(m->remote){
851                 alarm(0);
852                 atnotify(alarmed, 0);
853         }
854 }
855
856 void
857 memval(Machine *m, uvlong *v, uvlong *vmax, int)
858 {
859         *v = m->devswap[Mem];
860         *vmax = m->devswap[Maxmem];
861         if(*vmax == 0)
862                 *vmax = 1;
863 }
864
865 void
866 swapval(Machine *m, uvlong *v, uvlong *vmax, int)
867 {
868         *v = m->devswap[Swap];
869         *vmax = m->devswap[Maxswap];
870         if(*vmax == 0)
871                 *vmax = 1;
872 }
873
874 void
875 kernval(Machine *m, uvlong *v, uvlong *vmax, int)
876 {
877         *v = m->devswap[Kern];
878         *vmax = m->devswap[Maxkern];
879         if(*vmax == 0)
880                 *vmax = 1;
881 }
882
883 void
884 drawval(Machine *m, uvlong *v, uvlong *vmax, int)
885 {
886         *v = m->devswap[Draw];
887         *vmax = m->devswap[Maxdraw];
888         if(*vmax == 0)
889                 *vmax = 1;
890 }
891
892 void
893 contextval(Machine *m, uvlong *v, uvlong *vmax, int init)
894 {
895         *v = (m->devsysstat[Context]-m->prevsysstat[Context])&0xffffffff;
896         *vmax = sleeptime*m->nproc;
897         if(init)
898                 *vmax = sleeptime;
899 }
900
901 /*
902  * bug: need to factor in HZ
903  */
904 void
905 intrval(Machine *m, uvlong *v, uvlong *vmax, int init)
906 {
907         *v = (m->devsysstat[Interrupt]-m->prevsysstat[Interrupt])&0xffffffff;
908         *vmax = sleeptime*m->nproc*10;
909         if(init)
910                 *vmax = sleeptime*10;
911 }
912
913 void
914 syscallval(Machine *m, uvlong *v, uvlong *vmax, int init)
915 {
916         *v = (m->devsysstat[Syscall]-m->prevsysstat[Syscall])&0xffffffff;
917         *vmax = sleeptime*m->nproc;
918         if(init)
919                 *vmax = sleeptime;
920 }
921
922 void
923 faultval(Machine *m, uvlong *v, uvlong *vmax, int init)
924 {
925         *v = (m->devsysstat[Fault]-m->prevsysstat[Fault])&0xffffffff;
926         *vmax = sleeptime*m->nproc;
927         if(init)
928                 *vmax = sleeptime;
929 }
930
931 void
932 tlbmissval(Machine *m, uvlong *v, uvlong *vmax, int init)
933 {
934         *v = (m->devsysstat[TLBfault]-m->prevsysstat[TLBfault])&0xffffffff;
935         *vmax = (sleeptime/1000)*10*m->nproc;
936         if(init)
937                 *vmax = (sleeptime/1000)*10;
938 }
939
940 void
941 tlbpurgeval(Machine *m, uvlong *v, uvlong *vmax, int init)
942 {
943         *v = (m->devsysstat[TLBpurge]-m->prevsysstat[TLBpurge])&0xffffffff;
944         *vmax = (sleeptime/1000)*10*m->nproc;
945         if(init)
946                 *vmax = (sleeptime/1000)*10;
947 }
948
949 void
950 loadval(Machine *m, uvlong *v, uvlong *vmax, int init)
951 {
952         *v = m->devsysstat[Load];
953         *vmax = 1000*m->nproc;
954         if(init)
955                 *vmax = 1000;
956 }
957
958 void
959 idleval(Machine *m, uvlong *v, uvlong *vmax, int)
960 {
961         *v = m->devsysstat[Idle]/m->nproc;
962         *vmax = 100;
963 }
964
965 void
966 inintrval(Machine *m, uvlong *v, uvlong *vmax, int)
967 {
968         *v = m->devsysstat[InIntr]/m->nproc;
969         *vmax = 100;
970 }
971
972 void
973 etherval(Machine *m, uvlong *v, uvlong *vmax, int init)
974 {
975         *v = m->netetherstats[In]-m->prevetherstats[In] + m->netetherstats[Out]-m->prevetherstats[Out];
976         *vmax = sleeptime*m->nproc;
977         if(init)
978                 *vmax = sleeptime;
979 }
980
981 void
982 etherinval(Machine *m, uvlong *v, uvlong *vmax, int init)
983 {
984         *v = m->netetherstats[In]-m->prevetherstats[In];
985         *vmax = sleeptime*m->nproc;
986         if(init)
987                 *vmax = sleeptime;
988 }
989
990 void
991 etheroutval(Machine *m, uvlong *v, uvlong *vmax, int init)
992 {
993         *v = m->netetherstats[Out]-m->prevetherstats[Out];
994         *vmax = sleeptime*m->nproc;
995         if(init)
996                 *vmax = sleeptime;
997 }
998
999 void
1000 ethererrval(Machine *m, uvlong *v, uvlong *vmax, int init)
1001 {
1002         int i;
1003
1004         *v = 0;
1005         for(i=Err0; i<nelem(m->netetherstats); i++)
1006                 *v += m->netetherstats[i];
1007         *vmax = (sleeptime/1000)*10*m->nproc;
1008         if(init)
1009                 *vmax = (sleeptime/1000)*10;
1010 }
1011
1012 void
1013 batteryval(Machine *m, uvlong *v, uvlong *vmax, int)
1014 {
1015         *v = m->batterystats[0];
1016         if(m->bitsybatfd >= 0)
1017                 *vmax = 184;            // at least on my bitsy...
1018         else
1019                 *vmax = 100;
1020 }
1021
1022 void
1023 signalval(Machine *m, uvlong *v, uvlong *vmax, int)
1024 {
1025         ulong l;
1026
1027         *vmax = sleeptime;
1028         l = m->netetherifstats[0];
1029         /*
1030          * Range is seen to be from about -45 (strong) to -95 (weak); rescale
1031          */
1032         if(l == 0){     /* probably not present */
1033                 *v = 0;
1034                 return;
1035         }
1036         *v = 20*(l+95);
1037 }
1038
1039 void
1040 tempval(Machine *m, uvlong *v, uvlong *vmax, int)
1041 {
1042         ulong l;
1043
1044         *vmax = sleeptime;
1045         l = m->temp[0];
1046         if(l == ~0 || l == 0)
1047                 *v = 0;
1048         else
1049                 *v = (l-20)*27;
1050 }
1051
1052 void
1053 usage(void)
1054 {
1055         fprint(2, "usage: stats [-O] [-S scale] [-LY] [-%s] [machine...]\n", argchars);
1056         exits("usage");
1057 }
1058
1059 void
1060 addgraph(int n)
1061 {
1062         Graph *g, *ograph;
1063         int i, j;
1064         static int nadd;
1065
1066         if(n > nelem(menu2str))
1067                 abort();
1068         /* avoid two adjacent graphs of same color */
1069         if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
1070                 nadd++;
1071         ograph = graph;
1072         graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
1073         for(i=0; i<nmach; i++)
1074                 for(j=0; j<ngraph; j++)
1075                         graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
1076         free(ograph);
1077         ngraph++;
1078         for(i=0; i<nmach; i++){
1079                 g = &graph[i*ngraph+(ngraph-1)];
1080                 memset(g, 0, sizeof(Graph));
1081                 g->label = menu2str[n]+Opwid;
1082                 g->newvalue = newvaluefn[n];
1083                 g->update = update1;    /* no other update functions yet */
1084                 g->mach = &mach[i];
1085                 g->colindex = nadd%Ncolor;
1086         }
1087         present[n] = 1;
1088         nadd++;
1089 }
1090
1091 void
1092 dropgraph(int which)
1093 {
1094         Graph *ograph;
1095         int i, j, n;
1096
1097         if(which > nelem(menu2str))
1098                 abort();
1099         /* convert n to index in graph table */
1100         n = -1;
1101         for(i=0; i<ngraph; i++)
1102                 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
1103                         n = i;
1104                         break;
1105                 }
1106         if(n < 0){
1107                 fprint(2, "stats: internal error can't drop graph\n");
1108                 killall("error");
1109         }
1110         ograph = graph;
1111         graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
1112         for(i=0; i<nmach; i++){
1113                 for(j=0; j<n; j++)
1114                         graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
1115                 free(ograph[i*ngraph+j].data);
1116                 freeimage(ograph[i*ngraph+j].overtmp);
1117                 for(j++; j<ngraph; j++)
1118                         graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
1119         }
1120         free(ograph);
1121         ngraph--;
1122         present[which] = 0;
1123 }
1124
1125 int
1126 addmachine(char *name)
1127 {
1128         if(ngraph > 0){
1129                 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
1130                 usage();
1131         }
1132         if(mach == nil)
1133                 nmach = 0;      /* a little dance to get us started with local machine by default */
1134         mach = erealloc(mach, (nmach+1)*sizeof(Machine));
1135         memset(mach+nmach, 0, sizeof(Machine));
1136         if (initmach(mach+nmach, name)){
1137                 nmach++;
1138                 return 1;
1139         } else
1140                 return 0;
1141 }
1142
1143 void
1144 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
1145 {
1146         int j;
1147         uvlong v, vmax;
1148
1149         g->newvalue(g->mach, &v, &vmax, 1);
1150         if(vmax == 0)
1151                 vmax = 1;
1152         if(logscale){
1153                 for(j=1; j<=2; j++)
1154                         sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
1155                 *np = 2;
1156         }else{
1157                 for(j=1; j<=3; j++)
1158                         sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
1159                 *np = 3;
1160         }
1161 }
1162
1163 int
1164 labelwidth(void)
1165 {
1166         int i, j, n, w, maxw;
1167         char strs[Nlab][Lablen];
1168
1169         maxw = 0;
1170         for(i=0; i<ngraph; i++){
1171                 /* choose value for rightmost graph */
1172                 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
1173                 for(j=0; j<n; j++){
1174                         w = stringwidth(font, strs[j]);
1175                         if(w > maxw)
1176                                 maxw = w;
1177                 }
1178         }
1179         return maxw;
1180 }
1181
1182 void
1183 resize(void)
1184 {
1185         int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
1186         Graph *g;
1187         Rectangle machr, r;
1188         uvlong v, vmax;
1189         char buf[128], labs[Nlab][Lablen];
1190
1191         draw(screen, screen->r, display->white, nil, ZP);
1192
1193         /* label left edge */
1194         x = screen->r.min.x;
1195         y = screen->r.min.y + Labspace+font->height+Labspace;
1196         dy = (screen->r.max.y - y)/ngraph;
1197         dx = Labspace+stringwidth(font, "0")+Labspace;
1198         startx = x+dx+1;
1199         starty = y;
1200         for(i=0; i<ngraph; i++,y+=dy){
1201                 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
1202                 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
1203                 label(Pt(x, y), dy, graph[i].label);
1204                 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
1205         }
1206
1207         /* label top edge */
1208         dx = (screen->r.max.x - startx)/nmach;
1209         for(x=startx, i=0; i<nmach; i++,x+=dx){
1210                 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
1211                 j = dx/stringwidth(font, "0");
1212                 n = mach[i].nproc;
1213                 if(n>1 && j>=1+3+mach[i].lgproc){       /* first char of name + (n) */
1214                         j -= 3+mach[i].lgproc;
1215                         if(j <= 0)
1216                                 j = 1;
1217                         snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].shortname, n);
1218                 }else
1219                         snprint(buf, sizeof buf, "%.*s", j, mach[i].shortname);
1220                 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, font, buf);
1221         }
1222
1223         maxx = screen->r.max.x;
1224
1225         /* label right, if requested */
1226         if(ylabels && dy>Nlab*(font->height+1)){
1227                 wid = labelwidth();
1228                 if(wid < (maxx-startx)-30){
1229                         /* else there's not enough room */
1230                         maxx -= 1+Lx+wid;
1231                         draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
1232                         y = starty;
1233                         for(j=0; j<ngraph; j++, y+=dy){
1234                                 /* choose value for rightmost graph */
1235                                 g = &graph[ngraph*(nmach-1)+j];
1236                                 labelstrs(g, labs, &nlab);
1237                                 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
1238                                 if(j == ngraph-1)
1239                                         r.max.y = screen->r.max.y;
1240                                 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
1241                                 for(k=0; k<nlab; k++){
1242                                         ly = y + (dy*(nlab-k)/(nlab+1));
1243                                         draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
1244                                         ly -= font->height/2;
1245                                         string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, font, labs[k]);
1246                                 }
1247                         }
1248                 }
1249         }
1250
1251         /* create graphs */
1252         for(i=0; i<nmach; i++){
1253                 machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
1254                 if(i < nmach-1)
1255                         machr.max.x = startx+(i+1)*dx - 1;
1256                 y = starty;
1257                 for(j=0; j<ngraph; j++, y+=dy){
1258                         g = &graph[i*ngraph+j];
1259                         /* allocate data */
1260                         ondata = g->ndata;
1261                         g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
1262                         g->data = erealloc(g->data, g->ndata*sizeof(ulong));
1263                         if(g->ndata > ondata)
1264                                 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
1265                         /* set geometry */
1266                         g->r = machr;
1267                         g->r.min.y = y;
1268                         g->r.max.y = y+dy - 1;
1269                         if(j == ngraph-1)
1270                                 g->r.max.y = screen->r.max.y;
1271                         draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
1272                         g->overflow = 0;
1273                         r = g->r;
1274                         r.max.y = r.min.y+font->height;
1275                         r.max.x = r.min.x+stringwidth(font, "999999999999");
1276                         freeimage(g->overtmp);
1277                         g->overtmp = nil;
1278                         if(r.max.x <= g->r.max.x)
1279                                 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
1280                         g->newvalue(g->mach, &v, &vmax, 0);
1281                         redraw(g, vmax);
1282                 }
1283         }
1284
1285         flushimage(display, 1);
1286 }
1287
1288 void
1289 eresized(int new)
1290 {
1291         lockdisplay(display);
1292         if(new && getwindow(display, Refnone) < 0) {
1293                 fprint(2, "stats: can't reattach to window\n");
1294                 killall("reattach");
1295         }
1296         resize();
1297         unlockdisplay(display);
1298 }
1299
1300 void
1301 inputproc(void)
1302 {
1303         Event e;
1304         int i;
1305
1306         for(;;){
1307                 switch(eread(Emouse|Ekeyboard, &e)){
1308                 case Emouse:
1309                         if(e.mouse.buttons == 4){
1310                                 lockdisplay(display);
1311                                 for(i=0; i<Nmenu2; i++)
1312                                         if(present[i])
1313                                                 memmove(menu2str[i], "drop ", Opwid);
1314                                         else
1315                                                 memmove(menu2str[i], "add  ", Opwid);
1316                                 i = emenuhit(3, &e.mouse, &menu2);
1317                                 if(i >= 0){
1318                                         if(!present[i])
1319                                                 addgraph(i);
1320                                         else if(ngraph > 1)
1321                                                 dropgraph(i);
1322                                         resize();
1323                                 }
1324                                 unlockdisplay(display);
1325                         }
1326                         break;
1327                 case Ekeyboard:
1328                         if(e.kbdc==Kdel || e.kbdc=='q')
1329                                 killall(nil);
1330                         break;
1331                 }
1332         }
1333 }
1334
1335 void
1336 startproc(void (*f)(void), int index)
1337 {
1338         int pid;
1339
1340         switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
1341         case -1:
1342                 fprint(2, "stats: fork failed: %r\n");
1343                 killall("fork failed");
1344         case 0:
1345                 f();
1346                 fprint(2, "stats: %s process exits\n", procnames[index]);
1347                 if(index >= 0)
1348                         killall("process died");
1349                 exits(nil);
1350         }
1351         if(index >= 0)
1352                 pids[index] = pid;
1353 }
1354
1355 void
1356 main(int argc, char *argv[])
1357 {
1358         int i, j;
1359         double secs;
1360         uvlong v, vmax, nargs;
1361         char args[100];
1362
1363         nmach = 1;
1364         mysysname = getenv("sysname");
1365         if(mysysname == nil){
1366                 fprint(2, "stats: can't find $sysname: %r\n");
1367                 exits("sysname");
1368         }
1369
1370         nargs = 0;
1371         ARGBEGIN{
1372         case 'T':
1373                 secs = atof(EARGF(usage()));
1374                 if(secs > 0)
1375                         sleeptime = 1000*secs;
1376                 break;
1377         case 'S':
1378                 scale = atof(EARGF(usage()));
1379                 if(scale <= 0)
1380                         usage();
1381                 break;
1382         case 'L':
1383                 logscale++;
1384                 break;
1385         case 'Y':
1386                 ylabels++;
1387                 break;
1388         case 'O':
1389                 oldsystem = 1;
1390                 break;
1391         default:
1392                 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
1393                         usage();
1394                 args[nargs++] = ARGC();
1395         }ARGEND
1396
1397         if(argc == 0){
1398                 mach = emalloc(nmach*sizeof(Machine));
1399                 initmach(&mach[0], mysysname);
1400                 readmach(&mach[0], 1);
1401         }else{
1402                 for(i=j=0; i<argc; i++){
1403                         if (addmachine(argv[i]))
1404                                 readmach(&mach[j++], 1);
1405                 }
1406                 if (j == 0)
1407                         exits("connect");
1408         }
1409
1410         for(i=0; i<nargs; i++)
1411         switch(args[i]){
1412         default:
1413                 fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
1414                 usage();
1415         case 'b':
1416                 addgraph(Mbattery);
1417                 break;
1418         case 'c':
1419                 addgraph(Mcontext);
1420                 break;
1421         case 'e':
1422                 addgraph(Mether);
1423                 break;
1424         case 'E':
1425                 addgraph(Metherin);
1426                 addgraph(Metherout);
1427                 break;
1428         case 'f':
1429                 addgraph(Mfault);
1430                 break;
1431         case 'i':
1432                 addgraph(Mintr);
1433                 break;
1434         case 'I':
1435                 addgraph(Mload);
1436                 addgraph(Midle);
1437                 addgraph(Minintr);
1438                 break;
1439         case 'l':
1440                 addgraph(Mload);
1441                 break;
1442         case 'm':
1443                 addgraph(Mmem);
1444                 break;
1445         case 'n':
1446                 addgraph(Metherin);
1447                 addgraph(Metherout);
1448                 addgraph(Methererr);
1449                 break;
1450         case 'p':
1451                 addgraph(Mtlbpurge);
1452                 break;
1453         case 's':
1454                 addgraph(Msyscall);
1455                 break;
1456         case 't':
1457                 addgraph(Mtlbmiss);
1458                 addgraph(Mtlbpurge);
1459                 break;
1460         case '8':
1461                 addgraph(Msignal);
1462                 break;
1463         case 'w':
1464                 addgraph(Mswap);
1465                 break;
1466         case 'k':
1467                 addgraph(Mkern);
1468                 break;
1469         case 'd':
1470                 addgraph(Mdraw);
1471                 break;
1472         case 'z':
1473                 addgraph(Mtemp);
1474                 break;
1475         }
1476
1477         if(ngraph == 0)
1478                 addgraph(Mload);
1479
1480         for(i=0; i<nmach; i++)
1481                 for(j=0; j<ngraph; j++)
1482                         graph[i*ngraph+j].mach = &mach[i];
1483
1484         if(initdraw(nil, nil, "stats") < 0){
1485                 fprint(2, "stats: initdraw failed: %r\n");
1486                 exits("initdraw");
1487         }
1488         colinit();
1489         einit(Emouse|Ekeyboard);
1490         startproc(inputproc, Inputproc);
1491         pids[Mainproc] = getpid();
1492         display->locking = 1;   /* tell library we're using the display lock */
1493
1494         resize();
1495
1496         unlockdisplay(display); /* display is still locked from initdraw() */
1497         for(;;){
1498                 for(i=0; i<nmach; i++)
1499                         readmach(&mach[i], 0);
1500                 lockdisplay(display);
1501                 parity = 1-parity;
1502                 for(i=0; i<nmach*ngraph; i++){
1503                         graph[i].newvalue(graph[i].mach, &v, &vmax, 0);
1504                         graph[i].update(&graph[i], v, vmax);
1505                 }
1506                 flushimage(display, 1);
1507                 unlockdisplay(display);
1508                 sleep(sleeptime);
1509         }
1510 }