gping.c 19 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  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. /* IPv4 only */
  415. void
  416. pingsend(Machine *m)
  417. {
  418. int i;
  419. char buf[128], err[ERRMAX];
  420. Icmphdr *ip;
  421. Req *r;
  422. ip = (Icmphdr *)(buf + IPV4HDR_LEN);
  423. memset(buf, 0, sizeof buf);
  424. r = malloc(sizeof *r);
  425. if(r == nil)
  426. return;
  427. for(i = 32; i < MSGLEN; i++)
  428. buf[i] = i;
  429. ip->type = EchoRequest;
  430. ip->code = 0;
  431. ip->seq[0] = m->seq;
  432. ip->seq[1] = m->seq>>8;
  433. r->seq = m->seq;
  434. r->next = nil;
  435. lock(m);
  436. pingclean(m, -1, nsec(), 0);
  437. if(m->first == nil)
  438. m->first = r;
  439. else
  440. m->last->next = r;
  441. m->last = r;
  442. r->time = nsec();
  443. unlock(m);
  444. if(write(m->pingfd, buf, MSGLEN) < MSGLEN){
  445. errstr(err, sizeof err);
  446. if(strstr(err, "unreach")||strstr(err, "exceed"))
  447. m->unreachable++;
  448. }
  449. m->seq++;
  450. }
  451. /* IPv4 only */
  452. void
  453. pingrcv(void *arg)
  454. {
  455. int i, n, fd;
  456. uchar buf[512];
  457. ushort x;
  458. vlong now;
  459. Icmphdr *ip;
  460. Ip4hdr *ip4;
  461. Machine *m = arg;
  462. ip4 = (Ip4hdr *)buf;
  463. ip = (Icmphdr *)(buf + IPV4HDR_LEN);
  464. fd = dup(m->pingfd, -1);
  465. for(;;){
  466. n = read(fd, buf, sizeof(buf));
  467. now = nsec();
  468. if(n <= 0)
  469. continue;
  470. if(n < MSGLEN){
  471. print("bad len %d/%d\n", n, MSGLEN);
  472. continue;
  473. }
  474. for(i = 32; i < MSGLEN; i++)
  475. if(buf[i] != (i&0xff))
  476. continue;
  477. x = (ip->seq[1]<<8) | ip->seq[0];
  478. if(ip->type != EchoReply || ip->code != 0)
  479. continue;
  480. lock(m);
  481. pingclean(m, x, now, ip4->ttl);
  482. unlock(m);
  483. }
  484. }
  485. void
  486. initmach(Machine *m, char *name)
  487. {
  488. char *p;
  489. srand(time(0));
  490. p = strchr(name, '!');
  491. if(p){
  492. p++;
  493. m->name = estrdup(p+1);
  494. }else
  495. p = name;
  496. m->name = estrdup(p);
  497. m->nproc = 1;
  498. m->pingfd = dial(netmkaddr(m->name, "icmp", "1"), 0, 0, 0);
  499. if(m->pingfd < 0)
  500. sysfatal("dialing %s: %r", m->name);
  501. startproc(pingrcv, m);
  502. }
  503. long
  504. rttscale(long x)
  505. {
  506. if(x == 0)
  507. return 0;
  508. x = 10.0*log10(x) - 20.0;
  509. if(x < 0)
  510. x = 0;
  511. return x;
  512. }
  513. double
  514. rttunscale(long x)
  515. {
  516. double dx;
  517. x += 20;
  518. dx = x;
  519. return pow(10.0, dx/10.0);
  520. }
  521. void
  522. rttval(Machine *m, long *v, long *vmax, long *mark)
  523. {
  524. ulong x;
  525. if(m->rttmsgs == 0){
  526. x = m->lastrtt;
  527. } else {
  528. x = m->rttsum/m->rttmsgs;
  529. m->rttsum = m->rttmsgs = 0;
  530. m->lastrtt = x;
  531. }
  532. *v = rttscale(x);
  533. *vmax = Rttmax;
  534. *mark = 0;
  535. }
  536. void
  537. lostval(Machine *m, long *v, long *vmax, long *mark)
  538. {
  539. ulong x;
  540. if(m->rcvdmsgs+m->lostmsgs > 0)
  541. x = (m->lostavg>>1) + (((m->lostmsgs*100)/(m->lostmsgs + m->rcvdmsgs))>>1);
  542. else
  543. x = m->lostavg;
  544. m->lostavg = x;
  545. m->lostmsgs = m->rcvdmsgs = 0;
  546. if(m->unreachable){
  547. m->unreachable = 0;
  548. *mark = 100;
  549. } else
  550. *mark = 0;
  551. *v = x;
  552. *vmax = 100;
  553. }
  554. jmp_buf catchalarm;
  555. void
  556. alarmed(void *a, char *s)
  557. {
  558. if(strcmp(s, "alarm") == 0)
  559. notejmp(a, catchalarm, 1);
  560. noted(NDFLT);
  561. }
  562. void
  563. usage(void)
  564. {
  565. fprint(2, "usage: %s machine [machine...]\n", argv0);
  566. exits("usage");
  567. }
  568. void
  569. addgraph(int n)
  570. {
  571. Graph *g, *ograph;
  572. int i, j;
  573. static int nadd;
  574. if(n > nelem(menu2str))
  575. abort();
  576. /* avoid two adjacent graphs of same color */
  577. if(ngraph>0 && graph[ngraph-1].colindex==nadd%Ncolor)
  578. nadd++;
  579. ograph = graph;
  580. graph = emalloc(nmach*(ngraph+1)*sizeof(Graph));
  581. for(i=0; i<nmach; i++)
  582. for(j=0; j<ngraph; j++)
  583. graph[i*(ngraph+1)+j] = ograph[i*ngraph+j];
  584. free(ograph);
  585. ngraph++;
  586. for(i=0; i<nmach; i++){
  587. g = &graph[i*ngraph+(ngraph-1)];
  588. memset(g, 0, sizeof(Graph));
  589. g->label = menu2str[n]+Opwid;
  590. g->newvalue = newvaluefn[n];
  591. g->update = update1; /* no other update functions yet */
  592. g->mach = &mach[i];
  593. g->colindex = nadd%Ncolor;
  594. }
  595. present[n] = 1;
  596. nadd++;
  597. }
  598. int
  599. which2index(int which)
  600. {
  601. int i, n;
  602. n = -1;
  603. for(i=0; i<ngraph; i++){
  604. if(strcmp(menu2str[which]+Opwid, graph[i].label) == 0){
  605. n = i;
  606. break;
  607. }
  608. }
  609. if(n < 0){
  610. fprint(2, "%s: internal error can't drop graph\n", argv0);
  611. killall("error");
  612. }
  613. return n;
  614. }
  615. int
  616. index2which(int index)
  617. {
  618. int i, n;
  619. n = -1;
  620. for(i=0; i<Nmenu2; i++){
  621. if(strcmp(menu2str[i]+Opwid, graph[index].label) == 0){
  622. n = i;
  623. break;
  624. }
  625. }
  626. if(n < 0){
  627. fprint(2, "%s: internal error can't identify graph\n", argv0);
  628. killall("error");
  629. }
  630. return n;
  631. }
  632. void
  633. dropgraph(int which)
  634. {
  635. Graph *ograph;
  636. int i, j, n;
  637. if(which > nelem(menu2str))
  638. abort();
  639. /* convert n to index in graph table */
  640. n = which2index(which);
  641. ograph = graph;
  642. graph = emalloc(nmach*(ngraph-1)*sizeof(Graph));
  643. for(i=0; i<nmach; i++){
  644. for(j=0; j<n; j++)
  645. graph[i*(ngraph-1)+j] = ograph[i*ngraph+j];
  646. free(ograph[i*ngraph+j].data);
  647. freeimage(ograph[i*ngraph+j].overtmp);
  648. for(j++; j<ngraph; j++)
  649. graph[i*(ngraph-1)+j-1] = ograph[i*ngraph+j];
  650. }
  651. free(ograph);
  652. ngraph--;
  653. present[which] = 0;
  654. }
  655. void
  656. addmachine(char *name)
  657. {
  658. if(ngraph > 0){
  659. fprint(2, "%s: internal error: ngraph>0 in addmachine()\n", argv0);
  660. usage();
  661. }
  662. if(nmach == NMACH)
  663. sysfatal("too many machines");
  664. initmach(&mach[nmach++], name);
  665. }
  666. void
  667. resize(void)
  668. {
  669. int i, j, n, startx, starty, x, y, dx, dy, hashdx, ondata;
  670. Graph *g;
  671. Rectangle machr, r;
  672. long v, vmax, mark;
  673. char buf[128];
  674. draw(screen, screen->r, display->white, nil, ZP);
  675. /* label left edge */
  676. x = screen->r.min.x;
  677. y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
  678. dy = (screen->r.max.y - y)/ngraph;
  679. dx = Labspace+stringwidth(mediumfont, "0")+Labspace;
  680. startx = x+dx+1;
  681. starty = y;
  682. for(i=0; i<ngraph; i++,y+=dy){
  683. draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
  684. draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
  685. label(Pt(x, y), dy, graph[i].label);
  686. draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
  687. }
  688. /* label right edge */
  689. dx = Labspace+stringwidth(mediumfont, "0.001")+Labspace;
  690. hashdx = dx;
  691. x = screen->r.max.x - dx;
  692. y = screen->r.min.y + Labspace+mediumfont->height+Labspace;
  693. for(i=0; i<ngraph; i++,y+=dy){
  694. draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
  695. draw(screen, Rect(x, y, x+dx, screen->r.max.y), cols[graph[i].colindex][0], nil, paritypt(x));
  696. hashmarks(Pt(x, y), dy, i);
  697. draw(screen, Rect(x+dx, y, x+dx+1, screen->r.max.y), cols[graph[i].colindex][2], nil, ZP);
  698. }
  699. /* label top edge */
  700. dx = (screen->r.max.x - dx - startx)/nmach;
  701. for(x=startx, i=0; i<nmach; i++,x+=dx){
  702. draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
  703. j = dx/stringwidth(mediumfont, "0");
  704. n = mach[i].nproc;
  705. if(n>1 && j>=1+3+(n>10)+(n>100)){ /* first char of name + (n) */
  706. j -= 3+(n>10)+(n>100);
  707. if(j <= 0)
  708. j = 1;
  709. snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].name, n);
  710. }else
  711. snprint(buf, sizeof buf, "%.*s", j, mach[i].name);
  712. string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP,
  713. mediumfont, buf);
  714. }
  715. /* draw last vertical line */
  716. draw(screen,
  717. Rect(screen->r.max.x-hashdx-1, starty-1, screen->r.max.x-hashdx, screen->r.max.y),
  718. display->black, nil, ZP);
  719. /* create graphs */
  720. for(i=0; i<nmach; i++){
  721. machr = Rect(startx+i*dx, starty, screen->r.max.x, screen->r.max.y);
  722. if(i < nmach-1)
  723. machr.max.x = startx+(i+1)*dx - 1;
  724. else
  725. machr.max.x = screen->r.max.x - hashdx - 1;
  726. y = starty;
  727. for(j=0; j<ngraph; j++, y+=dy){
  728. g = &graph[i*ngraph+j];
  729. /* allocate data */
  730. ondata = g->ndata;
  731. g->ndata = Dx(machr)+1; /* may be too many if label will be drawn here; so what? */
  732. g->data = erealloc(g->data, g->ndata*sizeof(long));
  733. if(g->ndata > ondata)
  734. memset(g->data+ondata, 0, (g->ndata-ondata)*sizeof(long));
  735. /* set geometry */
  736. g->r = machr;
  737. g->r.min.y = y;
  738. g->r.max.y = y+dy - 1;
  739. if(j == ngraph-1)
  740. g->r.max.y = screen->r.max.y;
  741. draw(screen, g->r, cols[g->colindex][0], nil, paritypt(g->r.min.x));
  742. g->overflow = 0;
  743. *g->msg = 0;
  744. freeimage(g->overtmp);
  745. g->overtmp = nil;
  746. g->overtmplen = 0;
  747. r = g->r;
  748. r.max.y = r.min.y+mediumfont->height;
  749. n = (g->r.max.x - r.min.x)/stringwidth(mediumfont, "9");
  750. if(n > 4){
  751. if(n > Gmsglen)
  752. n = Gmsglen;
  753. r.max.x = r.min.x+stringwidth(mediumfont, "9")*n;
  754. g->overtmplen = n;
  755. g->overtmp = allocimage(display, r, screen->chan, 0, -1);
  756. }
  757. g->newvalue(g->mach, &v, &vmax, &mark);
  758. redraw(g, vmax);
  759. }
  760. }
  761. flushimage(display, 1);
  762. }
  763. void
  764. eresized(int new)
  765. {
  766. lockdisplay(display);
  767. if(new && getwindow(display, Refnone) < 0) {
  768. fprint(2, "%s: can't reattach to window\n", argv0);
  769. killall("reattach");
  770. }
  771. resize();
  772. unlockdisplay(display);
  773. }
  774. void
  775. dobutton2(Mouse *m)
  776. {
  777. int i;
  778. for(i=0; i<Nmenu2; i++)
  779. if(present[i])
  780. memmove(menu2str[i], "drop ", Opwid);
  781. else
  782. memmove(menu2str[i], "add ", Opwid);
  783. i = emenuhit(3, m, &menu2);
  784. if(i >= 0){
  785. if(!present[i])
  786. addgraph(i);
  787. else if(ngraph > 1)
  788. dropgraph(i);
  789. resize();
  790. }
  791. }
  792. void
  793. dobutton1(Mouse *m)
  794. {
  795. int i, n, dx, dt;
  796. Graph *g;
  797. char *e;
  798. double f;
  799. for(i = 0; i < ngraph*nmach; i++){
  800. if(ptinrect(m->xy, graph[i].r))
  801. break;
  802. }
  803. if(i == ngraph*nmach)
  804. return;
  805. g = &graph[i];
  806. if(g->overtmp == nil)
  807. return;
  808. /* clear any previous message and cursor */
  809. if(g->overflow || *g->msg){
  810. clearmsg(g);
  811. *g->msg = 0;
  812. clearcursor(g);
  813. }
  814. dx = g->r.max.x - m->xy.x;
  815. g->cursor = dx;
  816. dt = dx*pinginterval;
  817. e = &g->msg[sizeof(g->msg)];
  818. seprint(g->msg, e, "%s", ctime(starttime-dt/1000)+11);
  819. g->msg[8] = 0;
  820. n = 8;
  821. switch(index2which(i)){
  822. case Mrtt:
  823. f = rttunscale(g->data[dx]);
  824. seprint(g->msg+n, e, " %3.3g", f/1000000);
  825. break;
  826. case Mlost:
  827. seprint(g->msg+n, e, " %ld%%", g->data[dx]);
  828. break;
  829. }
  830. drawmsg(g, g->msg);
  831. drawcursor(g, m->xy.x);
  832. }
  833. void
  834. mouseproc(void*)
  835. {
  836. Mouse mouse;
  837. for(;;){
  838. mouse = emouse();
  839. if(mouse.buttons == 4){
  840. lockdisplay(display);
  841. dobutton2(&mouse);
  842. unlockdisplay(display);
  843. } else if(mouse.buttons == 1){
  844. lockdisplay(display);
  845. dobutton1(&mouse);
  846. unlockdisplay(display);
  847. }
  848. }
  849. }
  850. void
  851. startproc(void (*f)(void*), void *arg)
  852. {
  853. int pid;
  854. switch(pid = rfork(RFPROC|RFMEM|RFNOWAIT)){
  855. case -1:
  856. fprint(2, "%s: fork failed: %r\n", argv0);
  857. killall("fork failed");
  858. case 0:
  859. f(arg);
  860. killall("process died");
  861. exits(nil);
  862. }
  863. pids[npid++] = pid;
  864. }
  865. void
  866. main(int argc, char *argv[])
  867. {
  868. int i, j;
  869. long v, vmax, mark;
  870. char flags[10], *f, *p;
  871. fmtinstall('V', eipfmt);
  872. f = flags;
  873. pinginterval = 5000; /* 5 seconds */
  874. ARGBEGIN{
  875. case 'i':
  876. p = ARGF();
  877. if(p == nil)
  878. usage();
  879. pinginterval = atoi(p);
  880. break;
  881. default:
  882. if(f - flags >= sizeof(flags)-1)
  883. usage();
  884. *f++ = ARGC();
  885. break;
  886. }ARGEND
  887. *f = 0;
  888. for(i=0; i<argc; i++)
  889. addmachine(argv[i]);
  890. for(f = flags; *f; f++)
  891. switch(*f){
  892. case 'l':
  893. addgraph(Mlost);
  894. break;
  895. case 'r':
  896. addgraph(Mrtt);
  897. break;
  898. }
  899. if(nmach == 0)
  900. usage();
  901. if(ngraph == 0)
  902. addgraph(Mrtt);
  903. for(i=0; i<nmach; i++)
  904. for(j=0; j<ngraph; j++)
  905. graph[i*ngraph+j].mach = &mach[i];
  906. if(initdraw(nil, nil, argv0) < 0){
  907. fprint(2, "%s: initdraw failed: %r\n", argv0);
  908. exits("initdraw");
  909. }
  910. colinit();
  911. einit(Emouse);
  912. notify(nil);
  913. startproc(mouseproc, 0);
  914. display->locking = 1; /* tell library we're using the display lock */
  915. resize();
  916. starttime = time(0);
  917. unlockdisplay(display); /* display is still locked from initdraw() */
  918. for(j = 0; ; j++){
  919. lockdisplay(display);
  920. if(j == nmach){
  921. parity = 1-parity;
  922. j = 0;
  923. for(i=0; i<nmach*ngraph; i++){
  924. graph[i].newvalue(graph[i].mach, &v, &vmax, &mark);
  925. graph[i].update(&graph[i], v, vmax, mark);
  926. }
  927. starttime = time(0);
  928. }
  929. flushimage(display, 1);
  930. unlockdisplay(display);
  931. pingsend(&mach[j%nmach]);
  932. sleep(pinginterval/nmach);
  933. }
  934. }