stats.c 26 KB

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