stats.c 30 KB

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