iostats.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  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. /*
  10. * iostats - Gather file system information
  11. */
  12. #include <u.h>
  13. #include <libc.h>
  14. #include <auth.h>
  15. #include <fcall.h>
  16. #define Extern
  17. #include "statfs.h"
  18. void runprog(char**);
  19. void (*fcalls[])(Fsrpc*) =
  20. {
  21. [Tversion] Xversion,
  22. [Tauth] Xauth,
  23. [Tflush] Xflush,
  24. [Tattach] Xattach,
  25. [Twalk] Xwalk,
  26. [Topen] slave,
  27. [Tcreate] Xcreate,
  28. [Tclunk] Xclunk,
  29. [Tread] slave,
  30. [Twrite] slave,
  31. [Tremove] Xremove,
  32. [Tstat] Xstat,
  33. [Twstat] Xwstat,
  34. };
  35. int p[2];
  36. void
  37. usage(void)
  38. {
  39. fprint(2, "usage: iostats [-d] [-f debugfile] cmds [args ...]\n");
  40. exits("usage");
  41. }
  42. void
  43. main(int argc, char **argv)
  44. {
  45. Fsrpc *r;
  46. Rpc *rpc;
  47. Proc *m;
  48. Frec *fr;
  49. Fid *fid;
  50. unsigned long ttime;
  51. char *dbfile, *s;
  52. char buf[128];
  53. float brpsec, bwpsec, bppsec;
  54. int type, cpid, fspid, n;
  55. dbfile = DEBUGFILE;
  56. ARGBEGIN{
  57. case 'd':
  58. dbg++;
  59. break;
  60. case 'f':
  61. dbfile = ARGF();
  62. break;
  63. default:
  64. usage();
  65. }ARGEND
  66. if(argc == 0)
  67. usage();
  68. if(dbg) {
  69. close(2);
  70. create(dbfile, OWRITE, 0666);
  71. }
  72. if(pipe(p) < 0)
  73. fatal("pipe");
  74. switch(cpid = fork()) {
  75. case -1:
  76. fatal("fork");
  77. case 0:
  78. close(p[1]);
  79. if(getwd(buf, sizeof(buf)) == 0)
  80. fatal("no working directory");
  81. rfork(RFENVG|RFNAMEG|RFNOTEG);
  82. if(mount(p[0], -1, "/", MREPL, "", 'M') < 0)
  83. fatal("mount /");
  84. bind("#c/pid", "/dev/pid", MREPL);
  85. bind("#e", "/env", MREPL|MCREATE);
  86. close(0);
  87. close(1);
  88. close(2);
  89. open("/fd/0", OREAD);
  90. open("/fd/1", OWRITE);
  91. open("/fd/2", OWRITE);
  92. if(chdir(buf) < 0)
  93. fatal("chdir");
  94. runprog(argv);
  95. default:
  96. close(p[0]);
  97. }
  98. switch(fspid = fork()) {
  99. default:
  100. while(cpid != waitpid())
  101. ;
  102. postnote(PNPROC, fspid, DONESTR);
  103. while(fspid != waitpid())
  104. ;
  105. exits(0);
  106. case -1:
  107. fatal("fork");
  108. case 0:
  109. break;
  110. }
  111. /* Allocate work queues in shared memory */
  112. malloc(Dsegpad);
  113. Workq = malloc(sizeof(Fsrpc)*Nr_workbufs);
  114. stats = malloc(sizeof(Stats));
  115. fhash = mallocz(sizeof(Fid*)*FHASHSIZE, 1);
  116. if(Workq == 0 || fhash == 0 || stats == 0)
  117. fatal("no initial memory");
  118. memset(Workq, 0, sizeof(Fsrpc)*Nr_workbufs);
  119. memset(stats, 0, sizeof(Stats));
  120. stats->rpc[Tversion].name = "version";
  121. stats->rpc[Tauth].name = "auth";
  122. stats->rpc[Tflush].name = "flush";
  123. stats->rpc[Tattach].name = "attach";
  124. stats->rpc[Twalk].name = "walk";
  125. stats->rpc[Topen].name = "open";
  126. stats->rpc[Tcreate].name = "create";
  127. stats->rpc[Tclunk].name = "clunk";
  128. stats->rpc[Tread].name = "read";
  129. stats->rpc[Twrite].name = "write";
  130. stats->rpc[Tremove].name = "remove";
  131. stats->rpc[Tstat].name = "stat";
  132. stats->rpc[Twstat].name = "wstat";
  133. for(n = 0; n < Maxrpc; n++)
  134. stats->rpc[n].lo = 10000000000LL;
  135. fmtinstall('M', dirmodefmt);
  136. fmtinstall('D', dirfmt);
  137. fmtinstall('F', fcallfmt);
  138. if(chdir("/") < 0)
  139. fatal("chdir");
  140. initroot();
  141. DEBUG(2, "statfs: %s\n", buf);
  142. notify(catcher);
  143. for(;;) {
  144. r = getsbuf();
  145. if(r == 0)
  146. fatal("Out of service buffers");
  147. n = read9pmsg(p[1], r->buf, sizeof(r->buf));
  148. if(done)
  149. break;
  150. if(n < 0)
  151. fatal("read server");
  152. if(convM2S(r->buf, n, &r->work) == 0)
  153. fatal("format error");
  154. stats->nrpc++;
  155. stats->nproto += n;
  156. DEBUG(2, "%F\n", &r->work);
  157. type = r->work.type;
  158. rpc = &stats->rpc[type];
  159. rpc->count++;
  160. rpc->bin += n;
  161. (fcalls[type])(r);
  162. }
  163. /* Clear away the slave children */
  164. for(m = Proclist; m; m = m->next)
  165. postnote(PNPROC, m->pid, "kill");
  166. rpc = &stats->rpc[Tread];
  167. brpsec = (float)stats->totread / (((float)rpc->time/1e9)+.000001);
  168. rpc = &stats->rpc[Twrite];
  169. bwpsec = (float)stats->totwrite / (((float)rpc->time/1e9)+.000001);
  170. ttime = 0;
  171. for(n = 0; n < Maxrpc; n++) {
  172. rpc = &stats->rpc[n];
  173. if(rpc->count == 0)
  174. continue;
  175. ttime += rpc->time;
  176. }
  177. bppsec = (float)stats->nproto / ((ttime/1e9)+.000001);
  178. fprint(2, "\nread %lud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
  179. fprint(2, "write %lud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
  180. fprint(2, "protocol %lud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0);
  181. fprint(2, "rpc %lud count\n\n", stats->nrpc);
  182. fprint(2, "%-10s %5s %5s %5s %5s %5s T R\n",
  183. "Message", "Count", "Low", "High", "Time", "Averg");
  184. for(n = 0; n < Maxrpc; n++) {
  185. rpc = &stats->rpc[n];
  186. if(rpc->count == 0)
  187. continue;
  188. fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8lud %8lud bytes\n",
  189. rpc->name,
  190. rpc->count,
  191. rpc->lo/1000000,
  192. rpc->hi/1000000,
  193. rpc->time/1000000,
  194. rpc->time/1000000/rpc->count,
  195. rpc->bin,
  196. rpc->bout);
  197. }
  198. for(n = 0; n < FHASHSIZE; n++)
  199. for(fid = fhash[n]; fid; fid = fid->next)
  200. if(fid->nread || fid->nwrite)
  201. fidreport(fid);
  202. if(frhead == 0)
  203. exits(0);
  204. fprint(2, "\nOpens Reads (bytes) Writes (bytes) File\n");
  205. for(fr = frhead; fr; fr = fr->next) {
  206. s = fr->op;
  207. if(*s) {
  208. if(strcmp(s, "/fd/0") == 0)
  209. s = "(stdin)";
  210. else
  211. if(strcmp(s, "/fd/1") == 0)
  212. s = "(stdout)";
  213. else
  214. if(strcmp(s, "/fd/2") == 0)
  215. s = "(stderr)";
  216. }
  217. else
  218. s = "/.";
  219. fprint(2, "%5lud %8lud %8lud %8lud %8lud %s\n", fr->opens, fr->nread, fr->bread,
  220. fr->nwrite, fr->bwrite, s);
  221. }
  222. exits(0);
  223. }
  224. void
  225. reply(Fcall *r, Fcall *t, char *err)
  226. {
  227. uint8_t data[IOHDRSZ+Maxfdata];
  228. int n;
  229. t->tag = r->tag;
  230. t->fid = r->fid;
  231. if(err) {
  232. t->type = Rerror;
  233. t->ename = err;
  234. }
  235. else
  236. t->type = r->type + 1;
  237. DEBUG(2, "\t%F\n", t);
  238. n = convS2M(t, data, sizeof data);
  239. if(write(p[1], data, n)!=n)
  240. fatal("mount write");
  241. stats->nproto += n;
  242. stats->rpc[t->type-1].bout += n;
  243. }
  244. Fid *
  245. getfid(int nr)
  246. {
  247. Fid *f;
  248. for(f = fidhash(nr); f; f = f->next)
  249. if(f->nr == nr)
  250. return f;
  251. return 0;
  252. }
  253. int
  254. freefid(int nr)
  255. {
  256. Fid *f, **l;
  257. l = &fidhash(nr);
  258. for(f = *l; f; f = f->next) {
  259. if(f->nr == nr) {
  260. *l = f->next;
  261. f->next = fidfree;
  262. fidfree = f;
  263. return 1;
  264. }
  265. l = &f->next;
  266. }
  267. return 0;
  268. }
  269. Fid *
  270. newfid(int nr)
  271. {
  272. Fid *new, **l;
  273. int i;
  274. l = &fidhash(nr);
  275. for(new = *l; new; new = new->next)
  276. if(new->nr == nr)
  277. return 0;
  278. if(fidfree == 0) {
  279. fidfree = mallocz(sizeof(Fid) * Fidchunk, 1);
  280. if(fidfree == 0)
  281. fatal("out of memory");
  282. for(i = 0; i < Fidchunk-1; i++)
  283. fidfree[i].next = &fidfree[i+1];
  284. fidfree[Fidchunk-1].next = 0;
  285. }
  286. new = fidfree;
  287. fidfree = new->next;
  288. memset(new, 0, sizeof(Fid));
  289. new->next = *l;
  290. *l = new;
  291. new->nr = nr;
  292. new->fid = -1;
  293. new->nread = 0;
  294. new->nwrite = 0;
  295. new->bread = 0;
  296. new->bwrite = 0;
  297. return new;
  298. }
  299. Fsrpc *
  300. getsbuf(void)
  301. {
  302. static int ap;
  303. int look;
  304. Fsrpc *wb;
  305. for(look = 0; look < Nr_workbufs; look++) {
  306. if(++ap == Nr_workbufs)
  307. ap = 0;
  308. if(Workq[ap].busy == 0)
  309. break;
  310. }
  311. if(look == Nr_workbufs)
  312. fatal("No more work buffers");
  313. wb = &Workq[ap];
  314. wb->pid = 0;
  315. wb->canint = 0;
  316. wb->flushtag = NOTAG;
  317. wb->busy = 1;
  318. return wb;
  319. }
  320. char *
  321. strcatalloc(char *p, char *n)
  322. {
  323. char *v;
  324. v = realloc(p, strlen(p)+strlen(n)+1);
  325. if(v == 0)
  326. fatal("no memory");
  327. strcat(v, n);
  328. return v;
  329. }
  330. File *
  331. file(File *parent, char *name)
  332. {
  333. char buf[128];
  334. File *f, *new;
  335. Dir *dir;
  336. DEBUG(2, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
  337. for(f = parent->child; f; f = f->childlist)
  338. if(strcmp(name, f->name) == 0)
  339. break;
  340. if(f != nil && !f->inval)
  341. return f;
  342. makepath(buf, parent, name);
  343. dir = dirstat(buf);
  344. if(dir == nil)
  345. return 0;
  346. if(f != nil){
  347. free(dir);
  348. f->inval = 0;
  349. return f;
  350. }
  351. new = malloc(sizeof(File));
  352. if(new == 0)
  353. fatal("no memory");
  354. memset(new, 0, sizeof(File));
  355. new->name = strdup(name);
  356. if(new->name == nil)
  357. fatal("can't strdup");
  358. new->qid.type = dir->qid.type;
  359. new->qid.vers = dir->qid.vers;
  360. new->qid.path = ++qid;
  361. new->parent = parent;
  362. new->childlist = parent->child;
  363. parent->child = new;
  364. free(dir);
  365. return new;
  366. }
  367. void
  368. initroot(void)
  369. {
  370. Dir *dir;
  371. root = malloc(sizeof(File));
  372. if(root == 0)
  373. fatal("no memory");
  374. memset(root, 0, sizeof(File));
  375. root->name = strdup("/");
  376. if(root->name == nil)
  377. fatal("can't strdup");
  378. dir = dirstat(root->name);
  379. if(dir == nil)
  380. fatal("root stat");
  381. root->qid.type = dir->qid.type;
  382. root->qid.vers = dir->qid.vers;
  383. root->qid.path = ++qid;
  384. free(dir);
  385. }
  386. void
  387. makepath(char *as, File *p, char *name)
  388. {
  389. char *c, *seg[100];
  390. int i;
  391. char *s;
  392. seg[0] = name;
  393. for(i = 1; i < 100 && p; i++, p = p->parent){
  394. seg[i] = p->name;
  395. if(strcmp(p->name, "/") == 0)
  396. seg[i] = ""; /* will insert slash later */
  397. }
  398. s = as;
  399. while(i--) {
  400. for(c = seg[i]; *c; c++)
  401. *s++ = *c;
  402. *s++ = '/';
  403. }
  404. while(s[-1] == '/')
  405. s--;
  406. *s = '\0';
  407. if(as == s) /* empty string is root */
  408. strcpy(as, "/");
  409. }
  410. void
  411. fatal(char *s)
  412. {
  413. Proc *m;
  414. fprint(2, "iostats: %s: %r\n", s);
  415. /* Clear away the slave children */
  416. for(m = Proclist; m; m = m->next)
  417. postnote(PNPROC, m->pid, "exit");
  418. exits("fatal");
  419. }
  420. char*
  421. rdenv(char *v, char **end)
  422. {
  423. int fd, n;
  424. char *buf;
  425. Dir *d;
  426. if((fd = open(v, OREAD)) == -1)
  427. return nil;
  428. d = dirfstat(fd);
  429. if(d == nil || (buf = malloc(d->length + 1)) == nil)
  430. return nil;
  431. n = (int)d->length;
  432. n = read(fd, buf, n);
  433. close(fd);
  434. if(n <= 0){
  435. free(buf);
  436. buf = nil;
  437. }else{
  438. if(buf[n-1] != '\0')
  439. buf[n++] = '\0';
  440. *end = &buf[n];
  441. }
  442. free(d);
  443. return buf;
  444. }
  445. char Defaultpath[] = ".\0/bin";
  446. void
  447. runprog(char *argv[])
  448. {
  449. char *path, *ep, *p;
  450. char arg0[256];
  451. path = rdenv("/env/path", &ep);
  452. if(path == nil){
  453. path = Defaultpath;
  454. ep = path+sizeof(Defaultpath);
  455. }
  456. for(p = path; p < ep; p += strlen(p)+1){
  457. snprint(arg0, sizeof arg0, "%s/%s", p, argv[0]);
  458. exec(arg0, argv);
  459. }
  460. fatal("exec");
  461. }
  462. void
  463. catcher(void *a, char *msg)
  464. {
  465. USED(a);
  466. if(strcmp(msg, DONESTR) == 0) {
  467. done = 1;
  468. noted(NCONT);
  469. }
  470. if(strcmp(msg, "exit") == 0)
  471. exits("exit");
  472. noted(NDFLT);
  473. }
  474. void
  475. fidreport(Fid *f)
  476. {
  477. char *p, path[128];
  478. Frec *fr;
  479. p = path;
  480. makepath(p, f->f, "");
  481. for(fr = frhead; fr; fr = fr->next) {
  482. if(strcmp(fr->op, p) == 0) {
  483. fr->nread += f->nread;
  484. fr->nwrite += f->nwrite;
  485. fr->bread += f->bread;
  486. fr->bwrite += f->bwrite;
  487. fr->opens++;
  488. return;
  489. }
  490. }
  491. fr = malloc(sizeof(Frec));
  492. if(fr == 0 || (fr->op = strdup(p)) == 0)
  493. fatal("no memory");
  494. fr->nread = f->nread;
  495. fr->nwrite = f->nwrite;
  496. fr->bread = f->bread;
  497. fr->bwrite = f->bwrite;
  498. fr->opens = 1;
  499. if(frhead == 0) {
  500. frhead = fr;
  501. frtail = fr;
  502. }
  503. else {
  504. frtail->next = fr;
  505. frtail = fr;
  506. }
  507. fr->next = 0;
  508. }