stats.c 28 KB

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