#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; voidkillall(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;} voidmkcol(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);} voidcolinit(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);} voidlabel(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; }} Pointparitypt(int x){ return Pt(x+parity, 0);} Pointdatapoint(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;} voiddrawdatum(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); } } voidredraw(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;} voidupdate1(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); }} voidusage(void){ fprint(2, "usage: stats [-LY] [-F font] [-O] [-S scale] [-W winsize] [-%s] [machine...]\n", argchars); threadexitsall("usage");} voidaddgraph(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++;} voiddropgraph(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*); intaddmachine(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;} voidnewvalue(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;} voidlabelstrs(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; }} intlabelwidth(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;}Lines 1–500 of 931. Load more
#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; }