stats.c 28 KB

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