aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/draw/stats.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2004-04-23 05:14:58 +0000
committerrsc <devnull@localhost>2004-04-23 05:14:58 +0000
commite21fee604e3b53fd8c57ac3817e6d829f62d6ee3 (patch)
tree80147a5d4d886cce392c7e18f5ecb9eaaeb2359d /src/cmd/draw/stats.c
parentf0f4401f0cfc654646bdf21849627ebcbd5d82b5 (diff)
downloadplan9port-e21fee604e3b53fd8c57ac3817e6d829f62d6ee3.tar.gz
plan9port-e21fee604e3b53fd8c57ac3817e6d829f62d6ee3.tar.bz2
plan9port-e21fee604e3b53fd8c57ac3817e6d829f62d6ee3.zip
move these here
Diffstat (limited to 'src/cmd/draw/stats.c')
-rw-r--r--src/cmd/draw/stats.c884
1 files changed, 884 insertions, 0 deletions
diff --git a/src/cmd/draw/stats.c b/src/cmd/draw/stats.c
new file mode 100644
index 00000000..07d20ad4
--- /dev/null
+++ b/src/cmd/draw/stats.c
@@ -0,0 +1,884 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <auth.h>
+#include <fcall.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <keyboard.h>
+
+typedef struct Graph Graph;
+typedef struct Machine Machine;
+
+enum
+{
+ Ncolor = 6,
+ Ysqueeze = 2, /* vertical squeezing of label text */
+ Labspace = 2, /* room around label */
+ Dot = 2, /* height of dot */
+ Opwid = 5, /* strlen("add ") or strlen("drop ") */
+ Nlab = 3, /* max number of labels on y axis */
+ Lablen = 16, /* max length of label */
+ Lx = 4, /* label tick length */
+
+ STACK = 8192,
+ XSTACK = 32768,
+};
+
+enum
+{
+ Vbattery,
+ Vcontext,
+ Vcpu,
+ Vether,
+ Vethererr,
+ Vetherin,
+ Vetherout,
+ Vfault,
+ Vfork,
+ Vidle,
+ Vintr,
+ Vload,
+ Vmem,
+ Vswap,
+ Vsys,
+ Vsyscall,
+ Vuser,
+ Nvalue,
+};
+
+char*
+labels[Nvalue] =
+{
+ "battery",
+ "context",
+ "cpu",
+ "ether",
+ "ethererr",
+ "etherin",
+ "etherout",
+ "fault",
+ "fork",
+ "idle",
+ "intr",
+ "load",
+ "mem",
+ "swap",
+ "sys",
+ "syscall",
+ "user",
+};
+
+struct Graph
+{
+ int colindex;
+ Rectangle r;
+ int *data;
+ int ndata;
+ char *label;
+ int value;
+ void (*update)(Graph*, ulong, ulong);
+ Machine *mach;
+ int overflow;
+ Image *overtmp;
+ ulong vmax;
+};
+
+struct Machine
+{
+ char *name;
+ int fd;
+ int pid;
+ int dead;
+ int absolute[Nvalue];
+ ulong last[Nvalue];
+ ulong val[Nvalue][2];
+};
+
+char *menu2str[Nvalue+1];
+char xmenu2str[Nvalue+1][40];
+
+Menu menu2 = {menu2str, nil};
+int present[Nvalue];
+Image *cols[Ncolor][3];
+Graph *graph;
+Machine *mach;
+Font *mediumfont;
+char *mysysname;
+char argchars[] = "bceEfiIlmnsw";
+int pids[1024];
+int parity; /* toggled to avoid patterns in textured background */
+int nmach;
+int ngraph; /* totaly number is ngraph*nmach */
+double scale = 1.0;
+int logscale = 0;
+int ylabels = 0;
+int oldsystem = 0;
+int sleeptime = 1000;
+int changedvmax;
+
+Mousectl *mc;
+Keyboardctl *kc;
+
+void
+killall(char *s)
+{
+ int i;
+
+ for(i=0; i<nmach; i++)
+ if(mach[i].pid)
+ postnote(PNPROC, mach[i].pid, "kill");
+ exits(s);
+}
+
+void*
+emalloc(ulong sz)
+{
+ void *v;
+ v = malloc(sz);
+ if(v == nil) {
+ fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
+ killall("mem");
+ }
+ memset(v, 0, sz);
+ return v;
+}
+
+void*
+erealloc(void *v, ulong sz)
+{
+ v = realloc(v, sz);
+ if(v == nil) {
+ fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
+ killall("mem");
+ }
+ return v;
+}
+
+char*
+estrdup(char *s)
+{
+ char *t;
+ if((t = strdup(s)) == nil) {
+ fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
+ killall("mem");
+ }
+ return t;
+}
+
+void
+mkcol(int i, int c0, int c1, int c2)
+{
+ cols[i][0] = allocimagemix(display, c0, DWhite);
+ cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
+ cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
+}
+
+void
+colinit(void)
+{
+ mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
+ if(mediumfont == nil)
+ mediumfont = font;
+
+ /* Peach */
+ mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
+ /* Aqua */
+ mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
+ /* Yellow */
+ mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
+ /* Green */
+ mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
+ /* Blue */
+ mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
+ /* Grey */
+ cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
+ cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
+ cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
+}
+
+void
+label(Point p, int dy, char *text)
+{
+ char *s;
+ Rune r[2];
+ int w, maxw, maxy;
+
+ p.x += Labspace;
+ maxy = p.y+dy;
+ maxw = 0;
+ r[1] = '\0';
+ for(s=text; *s; ){
+ if(p.y+mediumfont->height-Ysqueeze > maxy)
+ break;
+ w = chartorune(r, s);
+ s += w;
+ w = runestringwidth(mediumfont, r);
+ if(w > maxw)
+ maxw = w;
+ runestring(screen, p, display->black, ZP, mediumfont, r);
+ p.y += mediumfont->height-Ysqueeze;
+ }
+}
+
+Point
+paritypt(int x)
+{
+ return Pt(x+parity, 0);
+}
+
+Point
+datapoint(Graph *g, int x, ulong v, ulong vmax)
+{
+ Point p;
+ double y;
+
+ p.x = x;
+ y = ((double)v)/(vmax*scale);
+ if(logscale){
+ /*
+ * Arrange scale to cover a factor of 1000.
+ * vmax corresponds to the 100 mark.
+ * 10*vmax is the top of the scale.
+ */
+ if(y <= 0.)
+ y = 0;
+ else{
+ y = log10(y);
+ /* 1 now corresponds to the top; -2 to the bottom; rescale */
+ y = (y+2.)/3.;
+ }
+ }
+ p.y = g->r.max.y - Dy(g->r)*y - Dot;
+ if(p.y < g->r.min.y)
+ p.y = g->r.min.y;
+ if(p.y > g->r.max.y-Dot)
+ p.y = g->r.max.y-Dot;
+ return p;
+}
+
+void
+drawdatum(Graph *g, int x, ulong prev, ulong v, ulong vmax)
+{
+ int c;
+ Point p, q;
+
+ c = g->colindex;
+ p = datapoint(g, x, v, vmax);
+ q = datapoint(g, x, prev, vmax);
+ if(p.y < q.y){
+ draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
+ draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
+ draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
+ }else{
+ draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
+ draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
+ draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
+ }
+
+}
+
+void
+redraw(Graph *g, int vmax)
+{
+ int i, c;
+
+ if(vmax != g->vmax){
+ g->vmax = vmax;
+ changedvmax = 1;
+ }
+ c = g->colindex;
+ draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
+ for(i=1; i<Dx(g->r); i++)
+ drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
+ drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
+ g->overflow = 0;
+}
+
+void
+update1(Graph *g, ulong v, ulong vmax)
+{
+ char buf[32];
+ int overflow;
+
+ if(vmax != g->vmax){
+ g->vmax = vmax;
+ changedvmax = 1;
+ }
+ if(g->overflow && g->overtmp!=nil)
+ draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
+ draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
+ drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
+ memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
+ g->data[0] = v;
+ g->overflow = 0;
+ if(logscale)
+ overflow = (v>10*vmax*scale);
+ else
+ overflow = (v>vmax*scale);
+ if(overflow && g->overtmp!=nil){
+ g->overflow = 1;
+ draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
+ sprint(buf, "%ld", v);
+ string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, buf);
+ }
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: stats [-O] [-S scale] [-LY] [-%s] [machine...]\n", argchars);
+ exits("usage");
+}
+
+void
+addgraph(int n)
+{
+ Graph *g, *ograph;
+ int i, j;
+ static int nadd;
+
+ if(n > Nvalue)
+ abort();
+ /* avoid two adjacent graphs of same color */
+ if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
+ nadd++;
+ ograph = graph;
+ graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
+ for(i=0; i<nmach; i++)
+ for(j=0; j<ngraph; j++)
+ graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
+ free(ograph);
+ ngraph++;
+ for(i=0; i<nmach; i++){
+ g = &graph[i*ngraph+(ngraph-1)];
+ memset(g, 0, sizeof(Graph));
+ g->value = n;
+ g->label = menu2str[n]+Opwid;
+ g->update = update1; /* no other update functions yet */
+ g->mach = &mach[i];
+ g->colindex = nadd%Ncolor;
+ }
+ present[n] = 1;
+ nadd++;
+}
+
+void
+dropgraph(int which)
+{
+ Graph *ograph;
+ int i, j, n;
+
+ if(which > nelem(menu2str))
+ abort();
+ /* convert n to index in graph table */
+ n = -1;
+ for(i=0; i<ngraph; i++)
+ if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
+ n = i;
+ break;
+ }
+ if(n < 0){
+ fprint(2, "stats: internal error can't drop graph\n");
+ killall("error");
+ }
+ ograph = graph;
+ graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
+ for(i=0; i<nmach; i++){
+ for(j=0; j<n; j++)
+ graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
+ free(ograph[i*ngraph+j].data);
+ freeimage(ograph[i*ngraph+j].overtmp);
+ for(j++; j<ngraph; j++)
+ graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
+ }
+ free(ograph);
+ ngraph--;
+ present[which] = 0;
+}
+
+int initmach(Machine*, char*);
+
+int
+addmachine(char *name)
+{
+ if(ngraph > 0){
+ fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
+ usage();
+ }
+ if(mach == nil)
+ nmach = 0; /* a little dance to get us started with local machine by default */
+ mach = erealloc(mach, (nmach+1)*sizeof(Machine));
+ memset(mach+nmach, 0, sizeof(Machine));
+ if (initmach(mach+nmach, name)){
+ nmach++;
+ return 1;
+ } else
+ return 0;
+}
+
+void
+newvalue(Machine *m, int i, ulong *v, ulong *vmax)
+{
+ ulong now;
+
+ if(m->absolute[i]){
+ *v = m->val[i][0];
+ *vmax = m->val[i][1];
+ }else{
+ now = m->val[i][0];
+ *v = (vlong)((now - m->last[i])*sleeptime)/1000;
+ m->last[i] = now;
+ *vmax = m->val[i][1];
+ }
+ if(*vmax == 0)
+ *vmax = 1;
+}
+
+void
+labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
+{
+ int j;
+ ulong vmax;
+
+ vmax = g->vmax;
+ if(logscale){
+ for(j=1; j<=2; j++)
+ sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
+ *np = 2;
+ }else{
+ for(j=1; j<=3; j++)
+ sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
+ *np = 3;
+ }
+}
+
+int
+labelwidth(void)
+{
+ int i, j, n, w, maxw;
+ char strs[Nlab][Lablen];
+
+ maxw = 0;
+ for(i=0; i<ngraph; i++){
+ /* choose value for rightmost graph */
+ labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
+ for(j=0; j<n; j++){
+ w = stringwidth(mediumfont, strs[j]);
+ if(w > maxw)
+ maxw = w;
+ }
+ }
+ return maxw;
+}
+
+void
+resize(void)
+{
+ int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
+ Graph *g;
+ Rectangle machr, r;
+ ulong v, vmax;
+ char buf[128], labs[Nlab][Lablen];
+
+ draw(screen, screen->r, display->white, nil, ZP);
+
+ /* label left edge */
+ x = screen->r.min.x;
+ y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
+ dy = (screen->r.max.y - y)/ngraph;
+ dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
+ startx = x+dx+1;
+ starty = y;
+ for(i=0; i<ngraph; i++,y+=dy){
+ draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
+ draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
+ label(Pt(x, y), dy, graph[i].label);
+ draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
+ }
+
+ /* label top edge */
+ dx = (screen->r.max.x - startx)/nmach;
+ for(x=startx, i=0; i<nmach; i++,x+=dx){
+ draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
+ j = dx/stringwidth(mediumfont, "0");
+ // n = mach[i].nproc;
+ n = 1;
+ if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */
+ j -= 3+(n>10)+(n>100);
+ if(j <= 0)
+ j = 1;
+ snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n);
+ }else
+ snprint(buf, sizeof buf, "%.*s", j, mach[i].name);
+ string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, mediumfont, buf);
+ }
+
+ maxx = screen->r.max.x;
+
+ /* label right, if requested */
+ if(ylabels && dy>Nlab*(mediumfont->height+1)){
+ wid = labelwidth();
+ if(wid < (maxx-startx)-30){
+ /* else there's not enough room */
+ maxx -= 1+Lx+wid;
+ draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
+ y = starty;
+ for(j=0; j<ngraph; j++, y+=dy){
+ /* choose value for rightmost graph */
+ g = &graph[ngraph*(nmach-1)+j];
+ labelstrs(g, labs, &nlab);
+ r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
+ if(j == ngraph-1)
+ r.max.y = screen->r.max.y;
+ draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
+ for(k=0; k<nlab; k++){
+ ly = y + (dy*(nlab-k)/(nlab+1));
+ draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
+ ly -= mediumfont->height/2;
+ string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, labs[k]);
+ }
+ }
+ }
+ }
+
+ /* create graphs */
+ for(i=0; i<nmach; i++){
+ machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
+ if(i < nmach-1)
+ machr.max.x = startx+(i+1)*dx - 1;
+ y = starty;
+ for(j=0; j<ngraph; j++, y+=dy){
+ g = &graph[i*ngraph+j];
+ /* allocate data */
+ ondata = g->ndata;
+ g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
+ g->data = erealloc(g->data, g->ndata*sizeof(ulong));
+ if(g->ndata > ondata)
+ memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
+ /* set geometry */
+ g->r = machr;
+ g->r.min.y = y;
+ g->r.max.y = y+dy - 1;
+ if(j == ngraph-1)
+ g->r.max.y = screen->r.max.y;
+ draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
+ g->overflow = 0;
+ r = g->r;
+ r.max.y = r.min.y+mediumfont->height;
+ r.max.x = r.min.x+stringwidth(mediumfont, "9999999");
+ freeimage(g->overtmp);
+ g->overtmp = nil;
+ if(r.max.x <= g->r.max.x)
+ g->overtmp = allocimage(display, r, screen->chan, 0, -1);
+ newvalue(g->mach, g->value, &v, &vmax);
+ redraw(g, vmax);
+ }
+ }
+
+ flushimage(display, 1);
+}
+
+void
+eresized(int new)
+{
+ lockdisplay(display);
+ if(new && getwindow(display, Refnone) < 0) {
+ fprint(2, "stats: can't reattach to window\n");
+ killall("reattach");
+ }
+ resize();
+ unlockdisplay(display);
+}
+
+void
+mousethread(void *v)
+{
+ Mouse m;
+ int i;
+
+ USED(v);
+
+ while(readmouse(mc) == 0){
+ m = mc->m;
+ if(m.buttons == 4){
+ for(i=0; i<Nvalue; i++)
+ if(present[i])
+ memmove(menu2str[i], "drop ", Opwid);
+ else
+ memmove(menu2str[i], "add ", Opwid);
+ lockdisplay(display);
+ i = menuhit(3, mc, &menu2, nil);
+ if(i >= 0){
+ if(!present[i])
+ addgraph(i);
+ else if(ngraph > 1)
+ dropgraph(i);
+ resize();
+ }
+ unlockdisplay(display);
+ }
+ }
+}
+
+void
+resizethread(void *v)
+{
+ USED(v);
+
+ while(recv(mc->resizec, 0) == 1){
+ lockdisplay(display);
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("attach to window: %r");
+ resize();
+ unlockdisplay(display);
+ }
+}
+
+void
+keyboardthread(void *v)
+{
+ Rune r;
+
+ while(recv(kc->c, &r) == 1)
+ if(r == 0x7F || r == 'q')
+ killall("quit");
+}
+
+void machthread(void*);
+
+void
+threadmain(int argc, char *argv[])
+{
+ int i, j;
+ char *s;
+ ulong v, vmax, nargs;
+ char args[100];
+
+ nmach = 1;
+ mysysname = sysname();
+ if(mysysname == nil){
+ fprint(2, "stats: can't find sysname: %r\n");
+ exits("sysname");
+ }
+
+ nargs = 0;
+ ARGBEGIN{
+ case 'T':
+ s = ARGF();
+ if(s == nil)
+ usage();
+ i = atoi(s);
+ if(i > 0)
+ sleeptime = 1000*i;
+ break;
+ case 'S':
+ s = ARGF();
+ if(s == nil)
+ usage();
+ scale = atof(s);
+ if(scale <= 0.)
+ usage();
+ break;
+ case 'L':
+ logscale++;
+ break;
+ case 'Y':
+ ylabels++;
+ break;
+ case 'O':
+ oldsystem = 1;
+ break;
+ default:
+ if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
+ usage();
+ args[nargs++] = ARGC();
+ }ARGEND
+
+ for(i=0; i<Nvalue; i++){
+ menu2str[i] = xmenu2str[i];
+ snprint(xmenu2str[i], sizeof xmenu2str[i], "add %s", labels[i]);
+ }
+
+ if(argc == 0){
+ mach = emalloc(nmach*sizeof(Machine));
+ initmach(&mach[0], mysysname);
+ }else{
+ for(i=j=0; i<argc; i++)
+ addmachine(argv[i]);
+ }
+
+ for(i=0; i<nmach; i++)
+ threadcreate(machthread, &mach[i], STACK);
+
+ for(i=0; i<nargs; i++)
+ switch(args[i]){
+ default:
+ fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
+ usage();
+ case 'b':
+ addgraph(Vbattery);
+ break;
+ case 'c':
+ addgraph(Vcontext);
+ break;
+ case 'e':
+ addgraph(Vether);
+ break;
+ case 'E':
+ addgraph(Vetherin);
+ addgraph(Vetherout);
+ break;
+ case 'f':
+ addgraph(Vfault);
+ break;
+ case 'i':
+ addgraph(Vintr);
+ break;
+ case 'I':
+ addgraph(Vload);
+ addgraph(Vidle);
+ break;
+ case 'l':
+ addgraph(Vload);
+ break;
+ case 'm':
+ addgraph(Vmem);
+ break;
+ case 'n':
+ addgraph(Vetherin);
+ addgraph(Vetherout);
+ addgraph(Vethererr);
+ break;
+ case 's':
+ addgraph(Vsyscall);
+ break;
+ case 'w':
+ addgraph(Vswap);
+ break;
+ }
+
+ if(ngraph == 0)
+ addgraph(Vload);
+
+ for(i=0; i<nmach; i++)
+ for(j=0; j<ngraph; j++)
+ graph[i*ngraph+j].mach = &mach[i];
+
+ if(initdraw(nil, nil, "stats") < 0)
+ sysfatal("initdraw: %r");
+ colinit();
+ if((mc = initmouse(nil, nil)) == nil)
+ sysfatal("initmouse: %r");
+ if((kc = initkeyboard(nil)) == nil)
+ sysfatal("initkeyboard: %r");
+
+ display->locking = 1;
+ threadcreate(keyboardthread, nil, XSTACK);
+ threadcreate(mousethread, nil, XSTACK);
+ threadcreate(resizethread, nil, XSTACK);
+
+ resize();
+ unlockdisplay(display);
+
+ for(;;){
+ parity = 1-parity;
+ lockdisplay(display);
+ for(i=0; i<nmach*ngraph; i++){
+ newvalue(graph[i].mach, graph[i].value, &v, &vmax);
+ graph[i].update(&graph[i], v, vmax);
+ }
+ if(changedvmax){
+ changedvmax = 0;
+ resize();
+ }
+ flushimage(display, 1);
+ unlockdisplay(display);
+ threadsleep(sleeptime);
+ }
+}
+
+void
+machthread(void *v)
+{
+ char buf[256], *f[4], *p;
+ int i, n, t;
+ Machine *m;
+
+ m = v;
+ t = 0;
+ for(;;){
+ n = threadread(m->fd, buf+t, sizeof buf-t);
+ m->dead = 0;
+ if(n <= 0)
+ break;
+ t += n;
+ while((p = memchr(buf, '\n', t)) != nil){
+ *p++ = 0;
+ n = tokenize(buf, f, nelem(f));
+ if(n >= 3){
+ for(i=0; i<Nvalue; i++){
+ if(strcmp(labels[i], f[0]) == 0){
+ if(*f[1] == '='){
+ m->absolute[i] = 1;
+ f[1]++;
+ }
+ m->val[i][0] = strtoul(f[1], 0, 0);
+ m->val[i][1] = strtoul(f[2], 0, 0);
+ }
+ }
+ }
+ t -= (p-buf);
+ memmove(buf, p, t);
+ }
+ }
+ if(m->fd){
+ close(m->fd);
+ m->fd = -1;
+ }
+ if(m->pid){
+ postnote(PNPROC, m->pid, "kill");
+ m->pid = 0;
+ }
+}
+
+int
+initmach(Machine *m, char *name)
+{
+ char *args[5], *q;
+ int p[2], kfd[3], pid;
+
+ m->name = name;
+ if(strcmp(name, mysysname) == 0)
+ name = nil;
+
+ if(pipe(p) < 0)
+ sysfatal("pipe: %r");
+
+ memset(args, 0, sizeof args);
+ args[0] = "sysstat";
+ if(name){
+ args[1] = name;
+ if((q = strchr(name, ':')) != nil){
+ *q++ = 0;
+ args[2] = q;
+ }
+ }
+ kfd[0] = open("/dev/null", OREAD);
+ kfd[1] = p[1];
+ kfd[2] = dup(2, -1);
+ if((pid = threadspawn(kfd, "sysstat", args)) < 0){
+ fprint(2, "spawn: %r\n");
+ close(kfd[0]);
+ close(p[0]);
+ close(p[1]);
+ return 0;
+ }
+ m->fd = p[0];
+ m->pid = pid;
+ if((q = strchr(m->name, '.')) != nil)
+ *q = 0;
+ return 1;
+}
+