| #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 |
| { |
| V80211, |
| Vbattery, |
| Vcontext, |
| Vcpu, |
| Vether, |
| Vethererr, |
| Vetherin, |
| Vetherout, |
| Vfault, |
| Vfork, |
| Vidle, |
| Vintr, |
| Vload, |
| Vmem, |
| Vswap, |
| Vsys, |
| Vsyscall, |
| Vuser, |
| Nvalue |
| }; |
| |
| char* |
| labels[Nvalue] = |
| { |
| "802.11", |
| "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*, long, 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]; |
| ulong load; |
| ulong nload; |
| }; |
| |
| char *menu2str[Nvalue+1]; |
| char xmenu2str[Nvalue+1][40]; |
| |
| Menu menu2 = {menu2str, 0}; |
| int present[Nvalue]; |
| Image *cols[Ncolor][3]; |
| Graph *graph; |
| Machine *mach; |
| Font *mediumfont; |
| char *fontname; |
| char *mysysname; |
| char argchars[] = "8bcCeEfiIlmnsw"; |
| 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"); |
| threadexitsall(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) |
| { |
| if(fontname) |
| mediumfont = openfont(display, fontname); |
| if(mediumfont == nil) |
| 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, long v, ulong vmax) |
| { |
| char buf[32]; |
| int overflow; |
| |
| if(v < 0) |
| v = 0; |
| 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 [-LY] [-F font] [-O] [-S scale] [-W winsize] [-%s] [machine...]\n", argchars); |
| threadexitsall("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->last[i] == 0) |
| m->last[i] = m->val[i][0]; |
| |
| if(i == Vload){ |
| /* |
| * Invert the ewma to obtain the 5s load statistics. |
| * Ewma is load' = (1884/2048)*load + (164/2048)*last5s, so we do |
| * last5s = (load' - (1884/2048)*load) / (164/2048). |
| */ |
| if(++m->nload%5 == 0){ |
| now = m->val[i][0]; |
| m->load = (now - (((vlong)m->last[i]*1884)/2048)) * 2048 / 164; |
| m->last[i] = now; |
| } |
| *v = m->load; |
| *vmax = m->val[i][1]; |
| }else 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 machproc(void*); |
| void updateproc(void*); |
| |
| void |
| threadmain(int argc, char *argv[]) |
| { |
| int i, j; |
| char *s; |
| ulong nargs; |
| char args[100]; |
| |
| nmach = 1; |
| mysysname = sysname(); |
| if(mysysname == nil){ |
| fprint(2, "stats: can't find sysname: %r\n"); |
| threadexitsall("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 'F': |
| fontname = EARGF(usage()); |
| break; |
| case 'Y': |
| ylabels++; |
| break; |
| case 'O': |
| oldsystem = 1; |
| break; |
| case 'W': |
| winsize = EARGF(usage()); |
| 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++) |
| proccreate(machproc, &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 '8': |
| addgraph(V80211); |
| break; |
| case 'b': |
| addgraph(Vbattery); |
| break; |
| case 'c': |
| addgraph(Vcontext); |
| break; |
| case 'C': |
| addgraph(Vcpu); |
| 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(0, nil, "stats") < 0) |
| sysfatal("initdraw: %r"); |
| colinit(); |
| if((mc = initmouse(nil, screen)) == 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); |
| proccreate(updateproc, nil, XSTACK); |
| resize(); |
| unlockdisplay(display); |
| } |
| |
| void |
| updateproc(void *z) |
| { |
| int i; |
| ulong v, vmax; |
| |
| USED(z); |
| 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); |
| sleep(sleeptime); |
| } |
| } |
| |
| void |
| machproc(void *v) |
| { |
| char buf[256], *f[4], *p; |
| int i, n, t; |
| Machine *m; |
| |
| m = v; |
| t = 0; |
| for(;;){ |
| n = read(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] = "auxstats"; |
| 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, "auxstats", 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; |
| } |
| |