gping.c 20 KB

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