gping.c 20 KB

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