gping.c 19 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  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. #include <ip.h>
  9. #include "icmp.h"
  10. #define MAXNUM 8 /* maximum number of numbers on data line */
  11. typedef struct Graph Graph;
  12. typedef struct Machine Machine;
  13. typedef struct Req Req;
  14. enum {
  15. Gmsglen = 16,
  16. };
  17. struct Graph
  18. {
  19. int colindex;
  20. Rectangle r;
  21. long *data;
  22. int ndata;
  23. char *label;
  24. void (*newvalue)(Machine*, long*, long*, long*);
  25. void (*update)(Graph*, long, long, long);
  26. Machine *mach;
  27. int overflow;
  28. Image *overtmp;
  29. int overtmplen;
  30. char msg[Gmsglen];
  31. int cursor;
  32. int vmax;
  33. };
  34. enum
  35. {
  36. MSGLEN = 64,
  37. Rttmax = 50,
  38. };
  39. struct Req
  40. {
  41. int seq; /* sequence number */
  42. vlong time; /* time sent */
  43. int rtt;
  44. Req *next;
  45. };
  46. struct Machine
  47. {
  48. Lock;
  49. char *name;
  50. int pingfd;
  51. int nproc;
  52. int rttmsgs;
  53. ulong rttsum;
  54. ulong lastrtt;
  55. int lostmsgs;
  56. int rcvdmsgs;
  57. ulong lostavg;
  58. int unreachable;
  59. ushort seq;
  60. Req *first;
  61. Req *last;
  62. Req *rcvd;
  63. char buf[1024];
  64. char *bufp;
  65. char *ebufp;
  66. };
  67. enum
  68. {
  69. Ncolor = 6,
  70. Ysqueeze = 2, /* vertical squeezing of label text */
  71. Labspace = 2, /* room around label */
  72. Dot = 2, /* height of dot */
  73. Opwid = 5, /* strlen("add ") or strlen("drop ") */
  74. NPROC = 128,
  75. NMACH = 32,
  76. };
  77. enum Menu2
  78. {
  79. Mrtt,
  80. Mlost,
  81. Nmenu2,
  82. };
  83. char *menu2str[Nmenu2+1] = {
  84. "add sec rtt",
  85. "add % lost ",
  86. nil,
  87. };
  88. void rttval(Machine*, long*, long*, long*);
  89. void lostval(Machine*, long*, long*, long*);
  90. Menu menu2 = {menu2str, nil};
  91. int present[Nmenu2];
  92. void (*newvaluefn[Nmenu2])(Machine*, long*, long*, long*) = {
  93. rttval,
  94. lostval,
  95. };
  96. Image *cols[Ncolor][3];
  97. Graph *graph;
  98. Machine mach[NMACH];
  99. Font *mediumfont;
  100. int pids[NPROC];
  101. int npid;
  102. int parity; /* toggled to avoid patterns in textured background */
  103. int nmach;
  104. int ngraph; /* totaly number is ngraph*nmach */
  105. long starttime;
  106. int pinginterval;
  107. void dropgraph(int);
  108. void addgraph(int);
  109. void startproc(void (*)(void*), void*);
  110. void resize(void);
  111. long rttscale(long);
  112. int which2index(int);
  113. int index2which(int);
  114. void
  115. killall(char *s)
  116. {
  117. int i, pid;
  118. pid = getpid();
  119. for(i=0; i<NPROC; i++)
  120. if(pids[i] && pids[i]!=pid)
  121. postnote(PNPROC, pids[i], "kill");
  122. exits(s);
  123. }
  124. void*
  125. emalloc(ulong sz)
  126. {
  127. void *v;
  128. v = malloc(sz);
  129. if(v == nil) {
  130. fprint(2, "%s: out of memory allocating %ld: %r\n", argv0, sz);
  131. killall("mem");
  132. }
  133. memset(v, 0, sz);
  134. return v;
  135. }
  136. void*
  137. erealloc(void *v, ulong sz)
  138. {
  139. v = realloc(v, sz);
  140. if(v == nil) {
  141. fprint(2, "%s: out of memory reallocating %ld: %r\n", argv0, sz);
  142. killall("mem");
  143. }
  144. return v;
  145. }
  146. char*
  147. estrdup(char *s)
  148. {
  149. char *t;
  150. if((t = strdup(s)) == nil) {
  151. fprint(2, "%s: out of memory in strdup(%.10s): %r\n", argv0, s);
  152. killall("mem");
  153. }
  154. return t;
  155. }
  156. void
  157. mkcol(int i, int c0, int c1, int c2)
  158. {
  159. cols[i][0] = allocimagemix(display, c0, DWhite);
  160. cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
  161. cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
  162. }
  163. void
  164. colinit(void)
  165. {
  166. mediumfont = openfont(display, "/lib/font/bit/pelm/latin1.8.font");
  167. if(mediumfont == nil)
  168. mediumfont = font;
  169. /* Peach */
  170. mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
  171. /* Aqua */
  172. mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
  173. /* Yellow */
  174. mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
  175. /* Green */
  176. mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
  177. /* Blue */
  178. mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
  179. /* Grey */
  180. cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
  181. cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
  182. cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
  183. }
  184. int
  185. loadbuf(Machine *m, int *fd)
  186. {
  187. int n;
  188. if(*fd < 0)
  189. return 0;
  190. seek(*fd, 0, 0);
  191. n = read(*fd, m->buf, sizeof m->buf);
  192. if(n <= 0){
  193. close(*fd);
  194. *fd = -1;
  195. return 0;
  196. }
  197. m->bufp = m->buf;
  198. m->ebufp = m->buf+n;
  199. return 1;
  200. }
  201. void
  202. label(Point p, int dy, char *text)
  203. {
  204. char *s;
  205. Rune r[2];
  206. int w, maxw, maxy;
  207. p.x += Labspace;
  208. maxy = p.y+dy;
  209. maxw = 0;
  210. r[1] = '\0';
  211. for(s=text; *s; ){
  212. if(p.y+mediumfont->height-Ysqueeze > maxy)
  213. break;
  214. w = chartorune(r, s);
  215. s += w;
  216. w = runestringwidth(mediumfont, r);
  217. if(w > maxw)
  218. maxw = w;
  219. runestring(screen, p, display->black, ZP, mediumfont, r);
  220. p.y += mediumfont->height-Ysqueeze;
  221. }
  222. }
  223. void
  224. hashmark(Point p, int dy, long v, long vmax, char *label)
  225. {
  226. int y;
  227. int x;
  228. x = p.x + Labspace;
  229. y = p.y + (dy*(vmax-v))/vmax;
  230. draw(screen, Rect(p.x, y-1, p.x+Labspace, y+1), display->black, nil, ZP);
  231. if(dy > 5*mediumfont->height)
  232. string(screen, Pt(x, y-mediumfont->height/2),
  233. display->black, ZP, mediumfont, label);
  234. }
  235. void
  236. hashmarks(Point p, int dy, int which)
  237. {
  238. switch(index2which(which)){
  239. case Mrtt:
  240. hashmark(p, dy, rttscale(1000000), Rttmax, "1.");
  241. hashmark(p, dy, rttscale(100000), Rttmax, "0.1");
  242. hashmark(p, dy, rttscale(10000), Rttmax, "0.01");
  243. hashmark(p, dy, rttscale(1000), Rttmax, "0.001");
  244. break;
  245. case Mlost:
  246. hashmark(p, dy, 75, 100, " 75%");
  247. hashmark(p, dy, 50, 100, " 50%");
  248. hashmark(p, dy, 25, 100, " 25%");
  249. break;
  250. }
  251. }
  252. Point
  253. paritypt(int x)
  254. {
  255. return Pt(x+parity, 0);
  256. }
  257. Point
  258. datapoint(Graph *g, int x, long v, long vmax)
  259. {
  260. Point p;
  261. p.x = x;
  262. p.y = g->r.max.y - Dy(g->r)*v/vmax - Dot;
  263. if(p.y < g->r.min.y)
  264. p.y = g->r.min.y;
  265. if(p.y > g->r.max.y-Dot)
  266. p.y = g->r.max.y-Dot;
  267. return p;
  268. }
  269. void
  270. drawdatum(Graph *g, int x, long prev, long v, long vmax)
  271. {
  272. int c;
  273. Point p, q;
  274. c = g->colindex;
  275. p = datapoint(g, x, v, vmax);
  276. q = datapoint(g, x, prev, vmax);
  277. if(p.y < q.y){
  278. draw(screen, Rect(p.x, g->r.min.y, p.x+1, p.y), cols[c][0], nil, paritypt(p.x));
  279. draw(screen, Rect(p.x, p.y, p.x+1, q.y+Dot), cols[c][2], nil, ZP);
  280. draw(screen, Rect(p.x, q.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
  281. }else{
  282. draw(screen, Rect(p.x, g->r.min.y, p.x+1, q.y), cols[c][0], nil, paritypt(p.x));
  283. draw(screen, Rect(p.x, q.y, p.x+1, p.y+Dot), cols[c][2], nil, ZP);
  284. draw(screen, Rect(p.x, p.y+Dot, p.x+1, g->r.max.y), cols[c][1], nil, ZP);
  285. }
  286. g->vmax = vmax;
  287. }
  288. void
  289. drawmark(Graph *g, int x)
  290. {
  291. int c;
  292. c = (g->colindex+1)&Ncolor;
  293. draw(screen, Rect(x, g->r.min.y, x+1, g->r.max.y), cols[c][2], nil, ZP);
  294. }
  295. void
  296. redraw(Graph *g, int vmax)
  297. {
  298. int i, c;
  299. c = g->colindex;
  300. draw(screen, g->r, cols[c][0], nil, paritypt(g->r.min.x));
  301. for(i=1; i<Dx(g->r); i++)
  302. drawdatum(g, g->r.max.x-i, g->data[i-1], g->data[i], vmax);
  303. drawdatum(g, g->r.min.x, g->data[i], g->data[i], vmax);
  304. }
  305. void
  306. clearmsg(Graph *g)
  307. {
  308. if(g->overtmp != nil)
  309. draw(screen, g->overtmp->r, g->overtmp, nil, g->overtmp->r.min);
  310. g->overflow = 0;
  311. }
  312. void
  313. drawmsg(Graph *g, char *msg)
  314. {
  315. if(g->overtmp == nil)
  316. return;
  317. /* save previous contents of screen */
  318. draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
  319. /* draw message */
  320. if(strlen(msg) > g->overtmplen)
  321. msg[g->overtmplen] = 0;
  322. string(screen, g->overtmp->r.min, display->black, ZP, mediumfont, msg);
  323. }
  324. void
  325. clearcursor(Graph *g)
  326. {
  327. int x;
  328. long prev;
  329. if(g->overtmp == nil)
  330. return;
  331. if(g->cursor > 0 && g->cursor < g->ndata){
  332. x = g->r.max.x - g->cursor;
  333. prev = 0;
  334. if(g->cursor > 0)
  335. prev = g->data[g->cursor-1];
  336. drawdatum(g, x, prev, g->data[g->cursor], g->vmax);
  337. g->cursor = -1;
  338. }
  339. }
  340. void
  341. drawcursor(Graph *g, int x)
  342. {
  343. if(g->overtmp == nil)
  344. return;
  345. draw(screen, Rect(x, g->r.min.y, x+1, g->r.max.y), cols[g->colindex][2], nil, ZP);
  346. }
  347. void
  348. update1(Graph *g, long v, long vmax, long mark)
  349. {
  350. char buf[Gmsglen];
  351. /* put back screen value sans message */
  352. if(g->overflow || *g->msg){
  353. clearmsg(g);
  354. g->overflow = 0;
  355. }
  356. draw(screen, g->r, screen, nil, Pt(g->r.min.x+1, g->r.min.y));
  357. drawdatum(g, g->r.max.x-1, g->data[0], v, vmax);
  358. if(mark)
  359. drawmark(g, g->r.max.x-1);
  360. memmove(g->data+1, g->data, (g->ndata-1)*sizeof(g->data[0]));
  361. g->data[0] = v;
  362. if(v>vmax){
  363. g->overflow = 1;
  364. sprint(buf, "%ld", v);
  365. drawmsg(g, buf);
  366. } else if(*g->msg)
  367. drawmsg(g, g->msg);
  368. if(g->cursor >= 0){
  369. g->cursor++;
  370. if(g->cursor >= g->ndata){
  371. g->cursor = -1;
  372. if(*g->msg){
  373. clearmsg(g);
  374. *g->msg = 0;
  375. }
  376. }
  377. }
  378. }
  379. void
  380. pinglost(Machine *m, Req*)
  381. {
  382. m->lostmsgs++;
  383. }
  384. void
  385. pingreply(Machine *m, Req *r)
  386. {
  387. ulong x;
  388. x = r->time/1000LL;
  389. m->rttsum += x;
  390. m->rcvdmsgs++;
  391. m->rttmsgs++;
  392. }
  393. void
  394. pingclean(Machine *m, ushort seq, vlong now, int)
  395. {
  396. Req **l, *r;
  397. vlong x, y;
  398. y = 10LL*1000000000LL;
  399. for(l = &m->first; *l; ){
  400. r = *l;
  401. x = now - r->time;
  402. if(x > y || r->seq == seq){
  403. *l = r->next;
  404. r->time = x;
  405. if(r->seq != seq)
  406. pinglost(m, r);
  407. else
  408. pingreply(m, r);
  409. free(r);
  410. } else
  411. l = &(r->next);
  412. }
  413. }
  414. void
  415. pingsend(Machine *m)
  416. {
  417. char buf[128];
  418. Icmp *ip;
  419. int i;
  420. Req *r;
  421. char err[ERRMAX];
  422. ip = (Icmp*)buf;
  423. r = malloc(sizeof *r);
  424. if(r == nil)
  425. return;
  426. for(i = 32; i < 64; i++)
  427. buf[i] = i;
  428. ip->type = EchoRequest;
  429. ip->code = 0;
  430. ip->seq[0] = m->seq;
  431. ip->seq[1] = m->seq>>8;
  432. r->seq = m->seq;
  433. r->next = nil;
  434. lock(m);
  435. pingclean(m, -1, nsec(), 0);
  436. if(m->first == nil)
  437. m->first = r;
  438. else
  439. m->last->next = r;
  440. m->last = r;
  441. r->time = nsec();
  442. unlock(m);
  443. if(write(m->pingfd, ip, MSGLEN) < MSGLEN){
  444. errstr(err, sizeof err);
  445. if(strstr(err, "unreach")||strstr(err, "exceed"))
  446. m->unreachable++;
  447. }
  448. m->seq++;
  449. }
  450. void
  451. pingrcv(void *arg)
  452. {
  453. uchar buf[512];
  454. Icmp *ip;
  455. ushort x;
  456. int i, n, fd;
  457. vlong now;
  458. Machine *m = arg;
  459. ip = (Icmp*)buf;
  460. fd = dup(m->pingfd, -1);
  461. for(;;){
  462. n = read(fd, buf, sizeof(buf));
  463. now = nsec();
  464. if(n <= 0)
  465. continue;
  466. if(n < MSGLEN){
  467. print("bad len %d/%d\n", n, MSGLEN);
  468. continue;
  469. }
  470. for(i = 32; i < MSGLEN; i++)
  471. if(buf[i] != (i&0xff))
  472. continue;
  473. x = (ip->seq[1]<<8)|ip->seq[0];
  474. if(ip->type != EchoReply || ip->code != 0)
  475. continue;
  476. lock(m);
  477. pingclean(m, x, now, ip->ttl);
  478. unlock(m);
  479. }
  480. }
  481. void
  482. initmach(Machine *m, char *name)
  483. {
  484. char *p;
  485. srand(time(0));
  486. p = strchr(name, '!');
  487. if(p){
  488. p++;
  489. m->name = estrdup(p+1);
  490. }else
  491. p = name;
  492. m->name = estrdup(p);
  493. m->nproc = 1;
  494. m->pingfd = dial(netmkaddr(m->name, "icmp", "1"), 0, 0, 0);
  495. if(m->pingfd < 0)
  496. sysfatal("dialing %s: %r", m->name);
  497. startproc(pingrcv, m);
  498. }
  499. long
  500. rttscale(long x)
  501. {
  502. if(x == 0)
  503. return 0;
  504. x = 10.0*log10(x) - 20.0;
  505. if(x < 0)
  506. x = 0;
  507. return x;
  508. }
  509. double
  510. rttunscale(long x)
  511. {
  512. double dx;
  513. x += 20;
  514. dx = x;
  515. return pow(10.0, dx/10.0);
  516. }
  517. void
  518. rttval(Machine *m, long *v, long *vmax, long *mark)
  519. {
  520. ulong x;
  521. if(m->rttmsgs == 0){
  522. x = m->lastrtt;
  523. } else {
  524. x = m->rttsum/m->rttmsgs;
  525. m->rttsum = m->rttmsgs = 0;
  526. m->lastrtt = x;
  527. }
  528. *v = rttscale(x);
  529. *vmax = Rttmax;
  530. *mark = 0;
  531. }
  532. void
  533. lostval(Machine *m, long *v, long *vmax, long *mark)
  534. {
  535. ulong x;
  536. if(m->rcvdmsgs+m->lostmsgs > 0)
  537. x = (m->lostavg>>1) + (((m->lostmsgs*100)/(m->lostmsgs + m->rcvdmsgs))>>1);
  538. else
  539. x = m->lostavg;
  540. m->lostavg = x;
  541. m->lostmsgs = m->rcvdmsgs = 0;
  542. if(m->unreachable){
  543. m->unreachable = 0;
  544. *mark = 100;
  545. } else
  546. *mark = 0;
  547. *v = x;
  548. *vmax = 100;
  549. }
  550. jmp_buf catchalarm;
  551. void
  552. alarmed(void *a, char *s)
  553. {
  554. if(strcmp(s, "alarm") == 0)
  555. notejmp(a, catchalarm, 1);
  556. noted(NDFLT);
  557. }
  558. void
  559. usage(void)
  560. {
  561. fprint(2, "usage: %s machine [machine...]\n", argv0);
  562. exits("usage");
  563. }
  564. void
  565. addgraph(int n)
  566. {
  567. Graph *g, *ograph;
  568. int i, j;
  569. static int nadd;
  570. if(n > nelem(menu2str))
  571. abort();
  572. /* avoid two adjacent graphs of same color */
  573. if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
  574. nadd++;
  575. ograph = graph;
  576. graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
  577. for(i=0; i<nmach; i++)
  578. for(j=0; j<ngraph; j++)
  579. graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
  580. free(ograph);
  581. ngraph++;
  582. for(i=0; i<nmach; i++){
  583. g = &graph[i*ngraph+(ngraph-1)];
  584. memset(g, 0, sizeof(Graph));
  585. g->label = menu2str[n]+Opwid;
  586. g->newvalue = newvaluefn[n];
  587. g->update = update1; /* no other update functions yet */
  588. g->mach = &mach[i];
  589. g->colindex = nadd%Ncolor;
  590. }
  591. present[n] = 1;
  592. nadd++;
  593. }
  594. int
  595. which2index(int which)
  596. {
  597. int i, n;
  598. n = -1;
  599. for(i=0; i<ngraph; i++){
  600. if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
  601. n = i;
  602. break;
  603. }
  604. }
  605. if(n < 0){
  606. fprint(2, "%s: internal error can't drop graph\n", argv0);
  607. killall("error");
  608. }
  609. return n;
  610. }
  611. int
  612. index2which(int index)
  613. {
  614. int i, n;
  615. n = -1;
  616. for(i=0; i<Nmenu2; i++){
  617. if(strcmp(menu2str[i]+Opwid, graph[index].label) == 0){
  618. n = i;
  619. break;
  620. }
  621. }
  622. if(n < 0){
  623. fprint(2, "%s: internal error can't identify graph\n", argv0);
  624. killall("error");
  625. }
  626. return n;
  627. }
  628. void
  629. dropgraph(int which)
  630. {
  631. Graph *ograph;
  632. int i, j, n;
  633. if(which > nelem(menu2str))
  634. abort();
  635. /* convert n to index in graph table */
  636. n = which2index(which);
  637. ograph = graph;
  638. graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
  639. for(i=0; i<nmach; i++){
  640. for(j=0; j<n; j++)
  641. graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
  642. free(ograph[i*ngraph+j].data);
  643. freeimage(ograph[i*ngraph+j].overtmp);
  644. for(j++; j<ngraph; j++)
  645. graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
  646. }
  647. free(ograph);
  648. ngraph--;
  649. present[which] = 0;
  650. }
  651. void
  652. addmachine(char *name)
  653. {
  654. if(ngraph > 0){
  655. fprint(2, "%s: internal error: ngraph>0 in addmachine()\n", argv0);
  656. usage();
  657. }
  658. if(nmach == NMACH)
  659. sysfatal("too many machines");
  660. initmach(&mach[nmach++], name);
  661. }
  662. void
  663. resize(void)
  664. {
  665. int i, j, n, startx, starty, x, y, dx, dy, hashdx, ondata;
  666. Graph *g;
  667. Rectangle machr, r;
  668. long v, vmax, mark;
  669. char buf[128];
  670. draw(screen, screen->r, display->white, nil, ZP);
  671. /* label left edge */
  672. x = screen->r.min.x;
  673. y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
  674. dy = (screen->r.max.y - y)/ngraph;
  675. dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
  676. startx = x+dx+1;
  677. starty = y;
  678. for(i=0; i<ngraph; i++,y+=dy){
  679. draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
  680. draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
  681. label(Pt(x, y), dy, graph[i].label);
  682. draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
  683. }
  684. /* label right edge */
  685. dx = Labspace+stringwidth(mediumfont, "0.001")+Labspace;
  686. hashdx = dx;
  687. x = screen->r.max.x - dx;
  688. y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
  689. for(i=0; i<ngraph; i++,y+=dy){
  690. draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
  691. draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
  692. hashmarks(Pt(x, y), dy, i);
  693. draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
  694. }
  695. /* label top edge */
  696. dx = (screen->r.max.x - dx - startx)/nmach;
  697. for(x=startx, i=0; i<nmach; i++,x+=dx){
  698. draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
  699. j = dx/stringwidth(mediumfont, "0");
  700. n = mach[i].nproc;
  701. if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */
  702. j -= 3+(n>10)+(n>100);
  703. if(j <= 0)
  704. j = 1;
  705. snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n);
  706. }else
  707. snprint(buf, sizeof buf, "%.*s", j, mach[i].name);
  708. string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP,
  709. mediumfont, buf);
  710. }
  711. /* draw last vertical line */
  712. draw(screen,
  713. Rect(screen->r.max.x-hashdx-1, starty-1, screen->r.max.x-hashdx, screen->r.max.y),
  714. display->black, nil, ZP);
  715. /* create graphs */
  716. for(i=0; i<nmach; i++){
  717. machr = Rect(startx+i*dx, starty, screen->r.max.x, screen->r.max.y);
  718. if(i < nmach-1)
  719. machr.max.x = startx+(i+1)*dx - 1;
  720. else
  721. machr.max.x = screen->r.max.x - hashdx - 1;
  722. y = starty;
  723. for(j=0; j<ngraph; j++, y+=dy){
  724. g = &graph[i*ngraph+j];
  725. /* allocate data */
  726. ondata = g->ndata;
  727. g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
  728. g->data = erealloc(g->data, g->ndata*sizeof(long));
  729. if(g->ndata > ondata)
  730. memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(long));
  731. /* set geometry */
  732. g->r = machr;
  733. g->r.min.y = y;
  734. g->r.max.y = y+dy - 1;
  735. if(j == ngraph-1)
  736. g->r.max.y = screen->r.max.y;
  737. draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
  738. g->overflow = 0;
  739. *g->msg = 0;
  740. freeimage(g->overtmp);
  741. g->overtmp = nil;
  742. g->overtmplen = 0;
  743. r = g->r;
  744. r.max.y = r.min.y+mediumfont->height;
  745. n = (g->r.max.x - r.min.x)/stringwidth(mediumfont, "9");
  746. if(n > 4){
  747. if(n > Gmsglen)
  748. n = Gmsglen;
  749. r.max.x = r.min.x+stringwidth(mediumfont, "9")*n;
  750. g->overtmplen = n;
  751. g->overtmp = allocimage(display, r, screen->chan, 0, -1);
  752. }
  753. g->newvalue(g->mach, &v, &vmax, &mark);
  754. redraw(g, vmax);
  755. }
  756. }
  757. flushimage(display, 1);
  758. }
  759. void
  760. eresized(int new)
  761. {
  762. lockdisplay(display);
  763. if(new && getwindow(display, Refnone) < 0) {
  764. fprint(2, "%s: can't reattach to window\n", argv0);
  765. killall("reattach");
  766. }
  767. resize();
  768. unlockdisplay(display);
  769. }
  770. void
  771. dobutton2(Mouse *m)
  772. {
  773. int i;
  774. for(i=0; i<Nmenu2; i++)
  775. if(present[i])
  776. memmove(menu2str[i], "drop ", Opwid);
  777. else
  778. memmove(menu2str[i], "add ", Opwid);
  779. i = emenuhit(3, m, &menu2);
  780. if(i >= 0){
  781. if(!present[i])
  782. addgraph(i);
  783. else if(ngraph > 1)
  784. dropgraph(i);
  785. resize();
  786. }
  787. }
  788. void
  789. dobutton1(Mouse *m)
  790. {
  791. int i, n, dx, dt;
  792. Graph *g;
  793. char *e;
  794. double f;
  795. for(i = 0; i < ngraph*nmach; i++){
  796. if(ptinrect(m->xy, graph[i].r))
  797. break;
  798. }
  799. if(i == ngraph*nmach)
  800. return;
  801. g = &graph[i];
  802. if(g->overtmp == nil)
  803. return;
  804. /* clear any previous message and cursor */
  805. if(g->overflow || *g->msg){
  806. clearmsg(g);
  807. *g->msg = 0;
  808. clearcursor(g);
  809. }
  810. dx = g->r.max.x - m->xy.x;
  811. g->cursor = dx;
  812. dt = dx*pinginterval;
  813. e = &g->msg[sizeof(g->msg)];
  814. seprint(g->msg, e, "%s", ctime(starttime-dt/1000)+11);
  815. g->msg[8] = 0;
  816. n = 8;
  817. switch(index2which(i)){
  818. case Mrtt:
  819. f = rttunscale(g->data[dx]);
  820. seprint(g->msg+n, e, " %3.3g", f/1000000);
  821. break;
  822. case Mlost:
  823. seprint(g->msg+n, e, " %ld%%", g->data[dx]);
  824. break;
  825. }
  826. drawmsg(g, g->msg);
  827. drawcursor(g, m->xy.x);
  828. }
  829. void
  830. mouseproc(void*)
  831. {
  832. Mouse mouse;
  833. for(;;){
  834. mouse = emouse();
  835. if(mouse.buttons == 4){
  836. lockdisplay(display);
  837. dobutton2(&mouse);
  838. unlockdisplay(display);
  839. } else if(mouse.buttons == 1){
  840. lockdisplay(display);
  841. dobutton1(&mouse);
  842. unlockdisplay(display);
  843. }
  844. }
  845. }
  846. void
  847. startproc(void (*f)(void*), void *arg)
  848. {
  849. int pid;
  850. switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
  851. case -1:
  852. fprint(2, "%s: fork failed: %r\n", argv0);
  853. killall("fork failed");
  854. case 0:
  855. f(arg);
  856. killall("process died");
  857. exits(nil);
  858. }
  859. pids[npid++] = pid;
  860. }
  861. void
  862. main(int argc, char *argv[])
  863. {
  864. int i, j;
  865. long v, vmax, mark;
  866. char flags[10], *f, *p;
  867. fmtinstall('V', eipfmt);
  868. f = flags;
  869. pinginterval = 5000; /* 5 seconds */
  870. ARGBEGIN{
  871. case 'i':
  872. p = ARGF();
  873. if(p == nil)
  874. usage();
  875. pinginterval = atoi(p);
  876. break;
  877. default:
  878. if(f - flags >= sizeof(flags)-1)
  879. usage();
  880. *f++ = ARGC();
  881. break;
  882. }ARGEND
  883. *f = 0;
  884. for(i=0; i<argc; i++)
  885. addmachine(argv[i]);
  886. for(f = flags; *f; f++)
  887. switch(*f){
  888. case 'l':
  889. addgraph(Mlost);
  890. break;
  891. case 'r':
  892. addgraph(Mrtt);
  893. break;
  894. }
  895. if(nmach == 0)
  896. usage();
  897. if(ngraph == 0)
  898. addgraph(Mrtt);
  899. for(i=0; i<nmach; i++)
  900. for(j=0; j<ngraph; j++)
  901. graph[i*ngraph+j].mach = &mach[i];
  902. if(initdraw(nil, nil, argv0) < 0){
  903. fprint(2, "%s: initdraw failed: %r\n", argv0);
  904. exits("initdraw");
  905. }
  906. colinit();
  907. einit(Emouse);
  908. notify(nil);
  909. startproc(mouseproc, 0);
  910. display->locking = 1; /* tell library we're using the display lock */
  911. resize();
  912. starttime = time(0);
  913. unlockdisplay(display); /* display is still locked from initdraw() */
  914. for(j = 0; ; j++){
  915. lockdisplay(display);
  916. if(j == nmach){
  917. parity = 1-parity;
  918. j = 0;
  919. for(i=0; i<nmach*ngraph; i++){
  920. graph[i].newvalue(graph[i].mach, &v, &vmax, &mark);
  921. graph[i].update(&graph[i], v, vmax, mark);
  922. }
  923. starttime = time(0);
  924. }
  925. flushimage(display, 1);
  926. unlockdisplay(display);
  927. pingsend(&mach[j%nmach]);
  928. sleep(pinginterval/nmach);
  929. }
  930. }