iostats.c 9.4 KB

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