URI:
       tstats.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tstats.c (18479B)
       ---
            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 <thread.h>
            8 #include <mouse.h>
            9 #include <keyboard.h>
           10 
           11 typedef struct Graph                Graph;
           12 typedef struct Machine        Machine;
           13 
           14 enum
           15 {
           16         Ncolor        = 6,
           17         Ysqueeze        = 2,        /* vertical squeezing of label text */
           18         Labspace        = 2,        /* room around label */
           19         Dot                = 2,        /* height of dot */
           20         Opwid        = 5,        /* strlen("add  ") or strlen("drop ") */
           21         Nlab                = 3,        /* max number of labels on y axis */
           22         Lablen        = 16,        /* max length of label */
           23         Lx                = 4,        /* label tick length */
           24 
           25         STACK        = 8192,
           26         XSTACK        = 32768
           27 };
           28 
           29 enum
           30 {
           31         V80211,
           32         Vbattery,
           33         Vcontext,
           34         Vcpu,
           35         Vether,
           36         Vethererr,
           37         Vetherin,
           38         Vetherout,
           39         Vfault,
           40         Vfork,
           41         Vidle,
           42         Vintr,
           43         Vload,
           44         Vmem,
           45         Vswap,
           46         Vsys,
           47         Vsyscall,
           48         Vuser,
           49         Nvalue
           50 };
           51 
           52 char*
           53 labels[Nvalue] =
           54 {
           55         "802.11",
           56         "battery",
           57         "context",
           58         "cpu",
           59         "ether",
           60         "ethererr",
           61         "etherin",
           62         "etherout",
           63         "fault",
           64         "fork",
           65         "idle",
           66         "intr",
           67         "load",
           68         "mem",
           69         "swap",
           70         "sys",
           71         "syscall",
           72         "user"
           73 };
           74 
           75 struct Graph
           76 {
           77         int                colindex;
           78         Rectangle        r;
           79         int                *data;
           80         int                ndata;
           81         char                *label;
           82         int                value;
           83         void                (*update)(Graph*, long, ulong);
           84         Machine        *mach;
           85         int                overflow;
           86         Image        *overtmp;
           87         ulong        vmax;
           88 };
           89 
           90 struct Machine
           91 {
           92         char                *name;
           93         int                fd;
           94         int                pid;
           95         int                dead;
           96         int                absolute[Nvalue];
           97         ulong        last[Nvalue];
           98         ulong        val[Nvalue][2];
           99         ulong        load;
          100         ulong        nload;
          101 };
          102 
          103 char        *menu2str[Nvalue+1];
          104 char xmenu2str[Nvalue+1][40];
          105 
          106 Menu        menu2 = {menu2str, 0};
          107 int                present[Nvalue];
          108 Image        *cols[Ncolor][3];
          109 Graph        *graph;
          110 Machine        *mach;
          111 Font                *mediumfont;
          112 char                *fontname;
          113 char                *mysysname;
          114 char                argchars[] = "8bcCeEfiIlmnsw";
          115 int                pids[1024];
          116 int                 parity;        /* toggled to avoid patterns in textured background */
          117 int                nmach;
          118 int                ngraph;        /* totaly number is ngraph*nmach */
          119 double        scale = 1.0;
          120 int                logscale = 0;
          121 int                ylabels = 0;
          122 int                oldsystem = 0;
          123 int                 sleeptime = 1000;
          124 int                changedvmax;
          125 
          126 Mousectl *mc;
          127 Keyboardctl *kc;
          128 
          129 void
          130 killall(char *s)
          131 {
          132         int i;
          133 
          134         for(i=0; i<nmach; i++)
          135                 if(mach[i].pid)
          136                         postnote(PNPROC, mach[i].pid, "kill");
          137         threadexitsall(s);
          138 }
          139 
          140 void*
          141 emalloc(ulong sz)
          142 {
          143         void *v;
          144         v = malloc(sz);
          145         if(v == nil) {
          146                 fprint(2, "stats: out of memory allocating %ld: %r\n", sz);
          147                 killall("mem");
          148         }
          149         memset(v, 0, sz);
          150         return v;
          151 }
          152 
          153 void*
          154 erealloc(void *v, ulong sz)
          155 {
          156         v = realloc(v, sz);
          157         if(v == nil) {
          158                 fprint(2, "stats: out of memory reallocating %ld: %r\n", sz);
          159                 killall("mem");
          160         }
          161         return v;
          162 }
          163 
          164 char*
          165 estrdup(char *s)
          166 {
          167         char *t;
          168         if((t = strdup(s)) == nil) {
          169                 fprint(2, "stats: out of memory in strdup(%.10s): %r\n", s);
          170                 killall("mem");
          171         }
          172         return t;
          173 }
          174 
          175 void
          176 mkcol(int i, int c0, int c1, int c2)
          177 {
          178         cols[i][0] = allocimagemix(display, c0, DWhite);
          179         cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
          180         cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
          181 }
          182 
          183 void
          184 colinit(void)
          185 {
          186         if(fontname)
          187                 mediumfont = openfont(display, fontname);
          188         if(mediumfont == nil)
          189                 mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
          190         if(mediumfont == nil)
          191                 mediumfont = font;
          192 
          193         /* Peach */
          194         mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
          195         /* Aqua */
          196         mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
          197         /* Yellow */
          198         mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
          199         /* Green */
          200         mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
          201         /* Blue */
          202         mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
          203         /* Grey */
          204         cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
          205         cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
          206         cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
          207 }
          208 
          209 void
          210 label(Point p, int dy, char *text)
          211 {
          212         char *s;
          213         Rune r[2];
          214         int w, maxw, maxy;
          215 
          216         p.x += Labspace;
          217         maxy = p.y+dy;
          218         maxw = 0;
          219         r[1] = '\0';
          220         for(s=text; *s; ){
          221                 if(p.y+mediumfont->height-Ysqueeze > maxy)
          222                         break;
          223                 w = chartorune(r, s);
          224                 s += w;
          225                 w = runestringwidth(mediumfont, r);
          226                 if(w > maxw)
          227                         maxw = w;
          228                 runestring(screen, p, display->black, ZP, mediumfont, r);
          229                 p.y += mediumfont->height-Ysqueeze;
          230         }
          231 }
          232 
          233 Point
          234 paritypt(int x)
          235 {
          236         return Pt(x+parity, 0);
          237 }
          238 
          239 Point
          240 datapoint(Graph *g, int x, ulong v, ulong vmax)
          241 {
          242         Point p;
          243         double y;
          244 
          245         p.x = x;
          246         y = ((double)v)/(vmax*scale);
          247         if(logscale){
          248                 /*
          249                  * Arrange scale to cover a factor of 1000.
          250                  * vmax corresponds to the 100 mark.
          251                  * 10*vmax is the top of the scale.
          252                  */
          253                 if(y <= 0.)
          254                         y = 0;
          255                 else{
          256                         y = log10(y);
          257                         /* 1 now corresponds to the top; -2 to the bottom; rescale */
          258                         y = (y+2.)/3.;
          259                 }
          260         }
          261         p.y = g->r.max.y - Dy(g->r)*y - Dot;
          262         if(p.y < g->r.min.y)
          263                 p.y = g->r.min.y;
          264         if(p.y > g->r.max.y-Dot)
          265                 p.y = g->r.max.y-Dot;
          266         return p;
          267 }
          268 
          269 void
          270 drawdatum(Graph *g, int x, ulong prev, ulong v, ulong vmax)
          271 {
          272         int c;
          273         Point p, q;
          274 
          275         c = g->colindex;
          276         p = datapoint(g, x, v, vmax);
          277         q = datapoint(g, x, prev, vmax);
          278         if(p.y < q.y){
          279                 draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
          280                 draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
          281                 draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
          282         }else{
          283                 draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
          284                 draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
          285                 draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
          286         }
          287 
          288 }
          289 
          290 void
          291 redraw(Graph *g, int vmax)
          292 {
          293         int i, c;
          294 
          295         if(vmax != g->vmax){
          296                 g->vmax = vmax;
          297                 changedvmax = 1;
          298         }
          299         c = g->colindex;
          300         draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
          301         for(i=1; i<Dx(g->r); i++)
          302                 drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
          303         drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
          304         g->overflow = 0;
          305 }
          306 
          307 void
          308 update1(Graph *g, long v, ulong vmax)
          309 {
          310         char buf[32];
          311         int overflow;
          312 
          313         if(v < 0)
          314                 v = 0;
          315         if(vmax != g->vmax){
          316                 g->vmax = vmax;
          317                 changedvmax = 1;
          318         }
          319         if(g->overflow && g->overtmp!=nil)
          320                 draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
          321         draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
          322         drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
          323         memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
          324         g->data[0] = v;
          325         g->overflow = 0;
          326         if(logscale)
          327                 overflow = (v>10*vmax*scale);
          328         else
          329                 overflow = (v>vmax*scale);
          330         if(overflow && g->overtmp!=nil){
          331                 g->overflow = 1;
          332                 draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
          333                 sprint(buf, "%ld", v);
          334                 string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, buf);
          335         }
          336 }
          337 
          338 void
          339 usage(void)
          340 {
          341         fprint(2, "usage: stats [-LY] [-F font] [-O] [-S scale] [-W winsize] [-%s] [machine...]\n", argchars);
          342         threadexitsall("usage");
          343 }
          344 
          345 void
          346 addgraph(int n)
          347 {
          348         Graph *g, *ograph;
          349         int i, j;
          350         static int nadd;
          351 
          352         if(n > Nvalue)
          353                 abort();
          354         /* avoid two adjacent graphs of same color */
          355         if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
          356                 nadd++;
          357         ograph = graph;
          358         graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
          359         for(i=0; i<nmach; i++)
          360                 for(j=0; j<ngraph; j++)
          361                         graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
          362         free(ograph);
          363         ngraph++;
          364         for(i=0; i<nmach; i++){
          365                 g = &graph[i*ngraph+(ngraph-1)];
          366                 memset(g, 0, sizeof(Graph));
          367                 g->value = n;
          368                 g->label = menu2str[n]+Opwid;
          369                 g->update = update1;        /* no other update functions yet */
          370                 g->mach = &mach[i];
          371                 g->colindex = nadd%Ncolor;
          372         }
          373         present[n] = 1;
          374         nadd++;
          375 }
          376 
          377 void
          378 dropgraph(int which)
          379 {
          380         Graph *ograph;
          381         int i, j, n;
          382 
          383         if(which > nelem(menu2str))
          384                 abort();
          385         /* convert n to index in graph table */
          386         n = -1;
          387         for(i=0; i<ngraph; i++)
          388                 if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
          389                         n = i;
          390                         break;
          391                 }
          392         if(n < 0){
          393                 fprint(2, "stats: internal error can't drop graph\n");
          394                 killall("error");
          395         }
          396         ograph = graph;
          397         graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
          398         for(i=0; i<nmach; i++){
          399                 for(j=0; j<n; j++)
          400                         graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
          401                 free(ograph[i*ngraph+j].data);
          402                 freeimage(ograph[i*ngraph+j].overtmp);
          403                 for(j++; j<ngraph; j++)
          404                         graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
          405         }
          406         free(ograph);
          407         ngraph--;
          408         present[which] = 0;
          409 }
          410 
          411 int initmach(Machine*, char*);
          412 
          413 int
          414 addmachine(char *name)
          415 {
          416         if(ngraph > 0){
          417                 fprint(2, "stats: internal error: ngraph>0 in addmachine()\n");
          418                 usage();
          419         }
          420         if(mach == nil)
          421                 nmach = 0;        /* a little dance to get us started with local machine by default */
          422         mach = erealloc(mach, (nmach+1)*sizeof(Machine));
          423         memset(mach+nmach, 0, sizeof(Machine));
          424         if (initmach(mach+nmach, name)){
          425                 nmach++;
          426                 return 1;
          427         } else
          428                 return 0;
          429 }
          430 
          431 void
          432 newvalue(Machine *m, int i, ulong *v, ulong *vmax)
          433 {
          434         ulong now;
          435 
          436         if(m->last[i] == 0)
          437                 m->last[i] = m->val[i][0];
          438 
          439         if(i == Vload){
          440                 /*
          441                  * Invert the ewma to obtain the 5s load statistics.
          442                  * Ewma is load' = (1884/2048)*load + (164/2048)*last5s, so we do
          443                  * last5s = (load' - (1884/2048)*load) / (164/2048).
          444                  */
          445                 if(++m->nload%5 == 0){
          446                         now = m->val[i][0];
          447                         m->load = (now - (((vlong)m->last[i]*1884)/2048)) * 2048 / 164;
          448                         m->last[i] = now;
          449                 }
          450                 *v = m->load;
          451                 *vmax = m->val[i][1];
          452         }else if(m->absolute[i]){
          453                 *v = m->val[i][0];
          454                 *vmax = m->val[i][1];
          455         }else{
          456                 now = m->val[i][0];
          457                 *v = (vlong)((now - m->last[i])*sleeptime)/1000;
          458                 m->last[i] = now;
          459                 *vmax = m->val[i][1];
          460         }
          461         if(*vmax == 0)
          462                 *vmax = 1;
          463 }
          464 
          465 void
          466 labelstrs(Graph *g, char strs[Nlab][Lablen], int *np)
          467 {
          468         int j;
          469         ulong vmax;
          470 
          471         vmax = g->vmax;
          472         if(logscale){
          473                 for(j=1; j<=2; j++)
          474                         sprint(strs[j-1], "%g", scale*pow(10., j)*(double)vmax/100.);
          475                 *np = 2;
          476         }else{
          477                 for(j=1; j<=3; j++)
          478                         sprint(strs[j-1], "%g", scale*(double)j*(double)vmax/4.0);
          479                 *np = 3;
          480         }
          481 }
          482 
          483 int
          484 labelwidth(void)
          485 {
          486         int i, j, n, w, maxw;
          487         char strs[Nlab][Lablen];
          488 
          489         maxw = 0;
          490         for(i=0; i<ngraph; i++){
          491                 /* choose value for rightmost graph */
          492                 labelstrs(&graph[ngraph*(nmach-1)+i], strs, &n);
          493                 for(j=0; j<n; j++){
          494                         w = stringwidth(mediumfont, strs[j]);
          495                         if(w > maxw)
          496                                 maxw = w;
          497                 }
          498         }
          499         return maxw;
          500 }
          501 
          502 void
          503 resize(void)
          504 {
          505         int i, j, k, n, startx, starty, x, y, dx, dy, ly, ondata, maxx, wid, nlab;
          506         Graph *g;
          507         Rectangle machr, r;
          508         ulong v, vmax;
          509         char buf[128], labs[Nlab][Lablen];
          510 
          511         draw(screen, screen->r, display->white, nil, ZP);
          512 
          513         /* label left edge */
          514         x = screen->r.min.x;
          515         y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
          516         dy = (screen->r.max.y - y)/ngraph;
          517         dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
          518         startx = x+dx+1;
          519         starty = y;
          520         for(i=0; i<ngraph; i++,y+=dy){
          521                 draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
          522                 draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
          523                 label(Pt(x, y), dy, graph[i].label);
          524                 draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
          525         }
          526 
          527         /* label top edge */
          528         dx = (screen->r.max.x - startx)/nmach;
          529         for(x=startx, i=0; i<nmach; i++,x+=dx){
          530                 draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
          531                 j = dx/stringwidth(mediumfont, "0");
          532         /*        n = mach[i].nproc; */
          533                 n = 1;
          534                 if(n>1 && j>=1+3+(n>10)+(n>100)){        /* first char of name + (n) */
          535                         j -= 3+(n>10)+(n>100);
          536                         if(j <= 0)
          537                                 j = 1;
          538                         snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n);
          539                 }else
          540                         snprint(buf, sizeof buf, "%.*s", j, mach[i].name);
          541                 string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, mediumfont, buf);
          542         }
          543 
          544         maxx = screen->r.max.x;
          545 
          546         /* label right, if requested */
          547         if(ylabels && dy>Nlab*(mediumfont->height+1)){
          548                 wid = labelwidth();
          549                 if(wid < (maxx-startx)-30){
          550                         /* else there's not enough room */
          551                         maxx -= 1+Lx+wid;
          552                         draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
          553                         y = starty;
          554                         for(j=0; j<ngraph; j++, y+=dy){
          555                                 /* choose value for rightmost graph */
          556                                 g = &graph[ngraph*(nmach-1)+j];
          557                                 labelstrs(g, labs, &nlab);
          558                                 r = Rect(maxx+1, y, screen->r.max.x, y+dy-1);
          559                                 if(j == ngraph-1)
          560                                         r.max.y = screen->r.max.y;
          561                                 draw(screen, r, cols[g->colindex][0], nil, paritypt(r.min.x));
          562                                 for(k=0; k<nlab; k++){
          563                                         ly = y + (dy*(nlab-k)/(nlab+1));
          564                                         draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
          565                                         ly -= mediumfont->height/2;
          566                                         string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, mediumfont, labs[k]);
          567                                 }
          568                         }
          569                 }
          570         }
          571 
          572         /* create graphs */
          573         for(i=0; i<nmach; i++){
          574                 machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
          575                 if(i < nmach-1)
          576                         machr.max.x = startx+(i+1)*dx - 1;
          577                 y = starty;
          578                 for(j=0; j<ngraph; j++, y+=dy){
          579                         g = &graph[i*ngraph+j];
          580                         /* allocate data */
          581                         ondata = g->ndata;
          582                         g->ndata = Dx(machr)+1;        /* may be too many if label will be drawn here; so what? */
          583                         g->data = erealloc(g->data, g->ndata*sizeof(ulong));
          584                         if(g->ndata > ondata)
          585                                 memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(ulong));
          586                         /* set geometry */
          587                         g->r = machr;
          588                         g->r.min.y = y;
          589                         g->r.max.y = y+dy - 1;
          590                         if(j == ngraph-1)
          591                                 g->r.max.y = screen->r.max.y;
          592                         draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
          593                         g->overflow = 0;
          594                         r = g->r;
          595                         r.max.y = r.min.y+mediumfont->height;
          596                         r.max.x = r.min.x+stringwidth(mediumfont, "9999999");
          597                         freeimage(g->overtmp);
          598                         g->overtmp = nil;
          599                         if(r.max.x <= g->r.max.x)
          600                                 g->overtmp = allocimage(display, r, screen->chan, 0, -1);
          601                         newvalue(g->mach, g->value, &v, &vmax);
          602                         redraw(g, vmax);
          603                 }
          604         }
          605 
          606         flushimage(display, 1);
          607 }
          608 
          609 void
          610 eresized(int new)
          611 {
          612         lockdisplay(display);
          613         if(new && getwindow(display, Refnone) < 0) {
          614                 fprint(2, "stats: can't reattach to window\n");
          615                 killall("reattach");
          616         }
          617         resize();
          618         unlockdisplay(display);
          619 }
          620 
          621 void
          622 mousethread(void *v)
          623 {
          624         Mouse m;
          625         int i;
          626 
          627         USED(v);
          628 
          629         while(readmouse(mc) == 0){
          630                 m = mc->m;
          631                 if(m.buttons == 4){
          632                         for(i=0; i<Nvalue; i++)
          633                                 if(present[i])
          634                                         memmove(menu2str[i], "drop ", Opwid);
          635                                 else
          636                                         memmove(menu2str[i], "add  ", Opwid);
          637                         lockdisplay(display);
          638                         i = menuhit(3, mc, &menu2, nil);
          639                         if(i >= 0){
          640                                 if(!present[i])
          641                                         addgraph(i);
          642                                 else if(ngraph > 1)
          643                                         dropgraph(i);
          644                                 resize();
          645                         }
          646                         unlockdisplay(display);
          647                 }
          648         }
          649 }
          650 
          651 void
          652 resizethread(void *v)
          653 {
          654         USED(v);
          655 
          656         while(recv(mc->resizec, 0) == 1){
          657                 lockdisplay(display);
          658                 if(getwindow(display, Refnone) < 0)
          659                         sysfatal("attach to window: %r");
          660                 resize();
          661                 unlockdisplay(display);
          662         }
          663 }
          664 
          665 void
          666 keyboardthread(void *v)
          667 {
          668         Rune r;
          669 
          670         while(recv(kc->c, &r) == 1)
          671                 if(r == 0x7F || r == 'q')
          672                         killall("quit");
          673 }
          674 
          675 void machproc(void*);
          676 void updateproc(void*);
          677 
          678 int
          679 threadmaybackground(void)
          680 {
          681         return 1;
          682 }
          683 
          684 void
          685 threadmain(int argc, char *argv[])
          686 {
          687         int i, j;
          688         char *s;
          689         ulong nargs;
          690         char args[100];
          691 
          692         nmach = 1;
          693         mysysname = sysname();
          694         if(mysysname == nil){
          695                 fprint(2, "stats: can't find sysname: %r\n");
          696                 threadexitsall("sysname");
          697         }
          698 
          699         nargs = 0;
          700         ARGBEGIN{
          701         case 'T':
          702                 s = ARGF();
          703                 if(s == nil)
          704                         usage();
          705                 i = atoi(s);
          706                 if(i > 0)
          707                         sleeptime = 1000*i;
          708                 break;
          709         case 'S':
          710                 s = ARGF();
          711                 if(s == nil)
          712                         usage();
          713                 scale = atof(s);
          714                 if(scale <= 0.)
          715                         usage();
          716                 break;
          717         case 'L':
          718                 logscale++;
          719                 break;
          720         case 'F':
          721                 fontname = EARGF(usage());
          722                 break;
          723         case 'Y':
          724                 ylabels++;
          725                 break;
          726         case 'O':
          727                 oldsystem = 1;
          728                 break;
          729         case 'W':
          730                 winsize = EARGF(usage());
          731                 break;
          732         default:
          733                 if(nargs>=sizeof args || strchr(argchars, ARGC())==nil)
          734                         usage();
          735                 args[nargs++] = ARGC();
          736         }ARGEND
          737 
          738         for(i=0; i<Nvalue; i++){
          739                 menu2str[i] = xmenu2str[i];
          740                 snprint(xmenu2str[i], sizeof xmenu2str[i], "add  %s", labels[i]);
          741         }
          742 
          743         if(argc == 0){
          744                 mach = emalloc(nmach*sizeof(Machine));
          745                 initmach(&mach[0], mysysname);
          746         }else{
          747                 for(i=j=0; i<argc; i++)
          748                         addmachine(argv[i]);
          749         }
          750 
          751         for(i=0; i<nmach; i++)
          752                 proccreate(machproc, &mach[i], STACK);
          753 
          754         for(i=0; i<nargs; i++)
          755         switch(args[i]){
          756         default:
          757                 fprint(2, "stats: internal error: unknown arg %c\n", args[i]);
          758                 usage();
          759         case '8':
          760                 addgraph(V80211);
          761                 break;
          762         case 'b':
          763                 addgraph(Vbattery);
          764                 break;
          765         case 'c':
          766                 addgraph(Vcontext);
          767                 break;
          768         case 'C':
          769                 addgraph(Vcpu);
          770                 break;
          771         case 'e':
          772                 addgraph(Vether);
          773                 break;
          774         case 'E':
          775                 addgraph(Vetherin);
          776                 addgraph(Vetherout);
          777                 break;
          778         case 'f':
          779                 addgraph(Vfault);
          780                 break;
          781         case 'i':
          782                 addgraph(Vintr);
          783                 break;
          784         case 'I':
          785                 addgraph(Vload);
          786                 addgraph(Vidle);
          787                 break;
          788         case 'l':
          789                 addgraph(Vload);
          790                 break;
          791         case 'm':
          792                 addgraph(Vmem);
          793                 break;
          794         case 'n':
          795                 addgraph(Vetherin);
          796                 addgraph(Vetherout);
          797                 addgraph(Vethererr);
          798                 break;
          799         case 's':
          800                 addgraph(Vsyscall);
          801                 break;
          802         case 'w':
          803                 addgraph(Vswap);
          804                 break;
          805         }
          806 
          807         if(ngraph == 0)
          808                 addgraph(Vload);
          809 
          810         for(i=0; i<nmach; i++)
          811                 for(j=0; j<ngraph; j++)
          812                         graph[i*ngraph+j].mach = &mach[i];
          813 
          814         if(initdraw(0, nil, "stats") < 0)
          815                 sysfatal("initdraw: %r");
          816         colinit();
          817         if((mc = initmouse(nil, screen)) == nil)
          818                 sysfatal("initmouse: %r");
          819         if((kc = initkeyboard(nil)) == nil)
          820                 sysfatal("initkeyboard: %r");
          821 
          822         display->locking = 1;
          823         threadcreate(keyboardthread, nil, XSTACK);
          824         threadcreate(mousethread, nil, XSTACK);
          825         threadcreate(resizethread, nil, XSTACK);
          826         proccreate(updateproc, nil, XSTACK);
          827         resize();
          828         unlockdisplay(display);
          829 }
          830 
          831 void
          832 updateproc(void *z)
          833 {
          834         int i;
          835         ulong v, vmax;
          836 
          837         USED(z);
          838         for(;;){
          839                 parity = 1-parity;
          840                 lockdisplay(display);
          841                 for(i=0; i<nmach*ngraph; i++){
          842                         newvalue(graph[i].mach, graph[i].value, &v, &vmax);
          843                         graph[i].update(&graph[i], v, vmax);
          844                 }
          845                 if(changedvmax){
          846                         changedvmax = 0;
          847                         resize();
          848                 }
          849                 flushimage(display, 1);
          850                 unlockdisplay(display);
          851                 sleep(sleeptime);
          852         }
          853 }
          854 
          855 void
          856 machproc(void *v)
          857 {
          858         char buf[256], *f[4], *p;
          859         int i, n, t;
          860         Machine *m;
          861 
          862         m = v;
          863         t = 0;
          864         for(;;){
          865                 n = read(m->fd, buf+t, sizeof buf-t);
          866                 m->dead = 0;
          867                 if(n <= 0)
          868                         break;
          869                 t += n;
          870                 while((p = memchr(buf, '\n', t)) != nil){
          871                         *p++ = 0;
          872                         n = tokenize(buf, f, nelem(f));
          873                         if(n >= 3){
          874                                 for(i=0; i<Nvalue; i++){
          875                                         if(strcmp(labels[i], f[0]) == 0){
          876                                                 if(*f[1] == '='){
          877                                                         m->absolute[i] = 1;
          878                                                         f[1]++;
          879                                                 }
          880                                                 m->val[i][0] = strtoul(f[1], 0, 0);
          881                                                 m->val[i][1] = strtoul(f[2], 0, 0);
          882                                         }
          883                                 }
          884                         }
          885                         t -= (p-buf);
          886                         memmove(buf, p, t);
          887                 }
          888         }
          889         if(m->fd){
          890                 close(m->fd);
          891                 m->fd = -1;
          892         }
          893         if(m->pid){
          894                 postnote(PNPROC, m->pid, "kill");
          895                 m->pid = 0;
          896         }
          897 }
          898 
          899 int
          900 initmach(Machine *m, char *name)
          901 {
          902         char *args[5], *q;
          903         int p[2], kfd[3], pid;
          904 
          905         m->name = name;
          906         if(strcmp(name, mysysname) == 0)
          907                 name = nil;
          908 
          909         if(pipe(p) < 0)
          910                 sysfatal("pipe: %r");
          911 
          912         memset(args, 0, sizeof args);
          913         args[0] = "auxstats";
          914         if(name){
          915                 args[1] = name;
          916                 if((q = strchr(name, ':')) != nil){
          917                         *q++ = 0;
          918                         args[2] = q;
          919                 }
          920         }
          921         kfd[0] = open("/dev/null", OREAD);
          922         kfd[1] = p[1];
          923         kfd[2] = dup(2, -1);
          924         if((pid = threadspawn(kfd, "auxstats", args)) < 0){
          925                 fprint(2, "spawn: %r\n");
          926                 close(kfd[0]);
          927                 close(p[0]);
          928                 close(p[1]);
          929                 return 0;
          930         }
          931         m->fd = p[0];
          932         m->pid = pid;
          933         if((q = strchr(m->name, '.')) != nil)
          934                 *q = 0;
          935         return 1;
          936 }