iostats.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  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. if(r->work.fid < 0)
  147. fatal("fid out of range");
  148. DEBUG(2, "%F\n", &r->work);
  149. type = r->work.type;
  150. rpc = &stats->rpc[type];
  151. rpc->count++;
  152. rpc->bin += n;
  153. (fcalls[type])(r);
  154. }
  155. /* Clear away the slave children */
  156. for(m = Proclist; m; m = m->next)
  157. postnote(PNPROC, m->pid, "kill");
  158. rpc = &stats->rpc[Tread];
  159. brpsec = (float)stats->totread / (((float)rpc->time/1e9)+.000001);
  160. rpc = &stats->rpc[Twrite];
  161. bwpsec = (float)stats->totwrite / (((float)rpc->time/1e9)+.000001);
  162. ttime = 0;
  163. for(n = 0; n < Maxrpc; n++) {
  164. rpc = &stats->rpc[n];
  165. if(rpc->count == 0)
  166. continue;
  167. ttime += rpc->time;
  168. }
  169. bppsec = (float)stats->nproto / ((ttime/1e9)+.000001);
  170. fprint(2, "\nread %lud bytes, %g Kb/sec\n", stats->totread, brpsec/1024.0);
  171. fprint(2, "write %lud bytes, %g Kb/sec\n", stats->totwrite, bwpsec/1024.0);
  172. fprint(2, "protocol %lud bytes, %g Kb/sec\n", stats->nproto, bppsec/1024.0);
  173. fprint(2, "rpc %lud count\n\n", stats->nrpc);
  174. fprint(2, "%-10s %5s %5s %5s %5s %5s T R\n",
  175. "Message", "Count", "Low", "High", "Time", "Averg");
  176. for(n = 0; n < Maxrpc; n++) {
  177. rpc = &stats->rpc[n];
  178. if(rpc->count == 0)
  179. continue;
  180. fprint(2, "%-10s %5lud %5llud %5llud %5llud %5llud ms %8lud %8lud bytes\n",
  181. rpc->name,
  182. rpc->count,
  183. rpc->lo/1000000,
  184. rpc->hi/1000000,
  185. rpc->time/1000000,
  186. rpc->time/1000000/rpc->count,
  187. rpc->bin,
  188. rpc->bout);
  189. }
  190. for(n = 0; n < FHASHSIZE; n++)
  191. for(fid = fhash[n]; fid; fid = fid->next)
  192. if(fid->nread || fid->nwrite)
  193. fidreport(fid);
  194. if(frhead == 0)
  195. exits(0);
  196. fprint(2, "\nOpens Reads (bytes) Writes (bytes) File\n");
  197. for(fr = frhead; fr; fr = fr->next) {
  198. s = fr->op;
  199. if(*s) {
  200. if(strcmp(s, "/fd/0") == 0)
  201. s = "(stdin)";
  202. else
  203. if(strcmp(s, "/fd/1") == 0)
  204. s = "(stdout)";
  205. else
  206. if(strcmp(s, "/fd/2") == 0)
  207. s = "(stderr)";
  208. }
  209. else
  210. s = "/.";
  211. fprint(2, "%5lud %8lud %8lud %8lud %8lud %s\n", fr->opens, fr->nread, fr->bread,
  212. fr->nwrite, fr->bwrite, s);
  213. }
  214. exits(0);
  215. }
  216. void
  217. reply(Fcall *r, Fcall *t, char *err)
  218. {
  219. uchar data[IOHDRSZ+Maxfdata];
  220. int n;
  221. t->tag = r->tag;
  222. t->fid = r->fid;
  223. if(err) {
  224. t->type = Rerror;
  225. t->ename = err;
  226. }
  227. else
  228. t->type = r->type + 1;
  229. DEBUG(2, "\t%F\n", t);
  230. n = convS2M(t, data, sizeof data);
  231. if(write(p[1], data, n)!=n)
  232. fatal("mount write");
  233. stats->nproto += n;
  234. stats->rpc[t->type-1].bout += n;
  235. }
  236. Fid *
  237. getfid(int nr)
  238. {
  239. Fid *f;
  240. for(f = fidhash(nr); f; f = f->next)
  241. if(f->nr == nr)
  242. return f;
  243. return 0;
  244. }
  245. int
  246. freefid(int nr)
  247. {
  248. Fid *f, **l;
  249. l = &fidhash(nr);
  250. for(f = *l; f; f = f->next) {
  251. if(f->nr == nr) {
  252. *l = f->next;
  253. f->next = fidfree;
  254. fidfree = f;
  255. return 1;
  256. }
  257. l = &f->next;
  258. }
  259. return 0;
  260. }
  261. Fid *
  262. newfid(int nr)
  263. {
  264. Fid *new, **l;
  265. int i;
  266. l = &fidhash(nr);
  267. for(new = *l; new; new = new->next)
  268. if(new->nr == nr)
  269. return 0;
  270. if(fidfree == 0) {
  271. fidfree = mallocz(sizeof(Fid) * Fidchunk, 1);
  272. if(fidfree == 0)
  273. fatal("out of memory");
  274. for(i = 0; i < Fidchunk-1; i++)
  275. fidfree[i].next = &fidfree[i+1];
  276. fidfree[Fidchunk-1].next = 0;
  277. }
  278. new = fidfree;
  279. fidfree = new->next;
  280. memset(new, 0, sizeof(Fid));
  281. new->next = *l;
  282. *l = new;
  283. new->nr = nr;
  284. new->fid = -1;
  285. new->nread = 0;
  286. new->nwrite = 0;
  287. new->bread = 0;
  288. new->bwrite = 0;
  289. return new;
  290. }
  291. Fsrpc *
  292. getsbuf(void)
  293. {
  294. static int ap;
  295. int look;
  296. Fsrpc *wb;
  297. for(look = 0; look < Nr_workbufs; look++) {
  298. if(++ap == Nr_workbufs)
  299. ap = 0;
  300. if(Workq[ap].busy == 0)
  301. break;
  302. }
  303. if(look == Nr_workbufs)
  304. fatal("No more work buffers");
  305. wb = &Workq[ap];
  306. wb->pid = 0;
  307. wb->canint = 0;
  308. wb->flushtag = NOTAG;
  309. wb->busy = 1;
  310. return wb;
  311. }
  312. char *
  313. strcatalloc(char *p, char *n)
  314. {
  315. char *v;
  316. v = realloc(p, strlen(p)+strlen(n)+1);
  317. if(v == 0)
  318. fatal("no memory");
  319. strcat(v, n);
  320. return v;
  321. }
  322. File *
  323. file(File *parent, char *name)
  324. {
  325. char buf[128];
  326. File *f, *new;
  327. Dir *dir;
  328. DEBUG(2, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
  329. for(f = parent->child; f; f = f->childlist)
  330. if(strcmp(name, f->name) == 0)
  331. break;
  332. if(f != nil && !f->inval)
  333. return f;
  334. makepath(buf, parent, name);
  335. dir = dirstat(buf);
  336. if(dir == nil)
  337. return 0;
  338. if(f != nil){
  339. free(dir);
  340. f->inval = 0;
  341. return f;
  342. }
  343. new = malloc(sizeof(File));
  344. if(new == 0)
  345. fatal("no memory");
  346. memset(new, 0, sizeof(File));
  347. new->name = strdup(name);
  348. if(new->name == nil)
  349. fatal("can't strdup");
  350. new->qid.type = dir->qid.type;
  351. new->qid.vers = dir->qid.vers;
  352. new->qid.path = ++qid;
  353. new->parent = parent;
  354. new->childlist = parent->child;
  355. parent->child = new;
  356. free(dir);
  357. return new;
  358. }
  359. void
  360. initroot(void)
  361. {
  362. Dir *dir;
  363. root = malloc(sizeof(File));
  364. if(root == 0)
  365. fatal("no memory");
  366. memset(root, 0, sizeof(File));
  367. root->name = strdup("/");
  368. if(root->name == nil)
  369. fatal("can't strdup");
  370. dir = dirstat(root->name);
  371. if(dir == nil)
  372. fatal("root stat");
  373. root->qid.type = dir->qid.type;
  374. root->qid.vers = dir->qid.vers;
  375. root->qid.path = ++qid;
  376. free(dir);
  377. }
  378. void
  379. makepath(char *as, File *p, char *name)
  380. {
  381. char *c, *seg[100];
  382. int i;
  383. char *s;
  384. seg[0] = name;
  385. for(i = 1; i < 100 && p; i++, p = p->parent){
  386. seg[i] = p->name;
  387. if(strcmp(p->name, "/") == 0)
  388. seg[i] = ""; /* will insert slash later */
  389. }
  390. s = as;
  391. while(i--) {
  392. for(c = seg[i]; *c; c++)
  393. *s++ = *c;
  394. *s++ = '/';
  395. }
  396. while(s[-1] == '/')
  397. s--;
  398. *s = '\0';
  399. if(as == s) /* empty string is root */
  400. strcpy(as, "/");
  401. }
  402. void
  403. fatal(char *s)
  404. {
  405. Proc *m;
  406. fprint(2, "iostats: %s: %r\n", s);
  407. /* Clear away the slave children */
  408. for(m = Proclist; m; m = m->next)
  409. postnote(PNPROC, m->pid, "exit");
  410. exits("fatal");
  411. }
  412. void
  413. runprog(char *argv[])
  414. {
  415. char arg0[128];
  416. exec(argv[0], argv);
  417. if (*argv[0] != '/' && strncmp(argv[0],"./",2) != 0) {
  418. sprint(arg0, "/bin/%s", argv[0]);
  419. exec(arg0, argv);
  420. }
  421. fatal("exec");
  422. }
  423. void
  424. catcher(void *a, char *msg)
  425. {
  426. USED(a);
  427. if(strcmp(msg, DONESTR) == 0) {
  428. done = 1;
  429. noted(NCONT);
  430. }
  431. if(strcmp(msg, "exit") == 0)
  432. exits("exit");
  433. noted(NDFLT);
  434. }
  435. void
  436. fidreport(Fid *f)
  437. {
  438. char *p, path[128];
  439. Frec *fr;
  440. p = path;
  441. makepath(p, f->f, "");
  442. for(fr = frhead; fr; fr = fr->next) {
  443. if(strcmp(fr->op, p) == 0) {
  444. fr->nread += f->nread;
  445. fr->nwrite += f->nwrite;
  446. fr->bread += f->bread;
  447. fr->bwrite += f->bwrite;
  448. fr->opens++;
  449. return;
  450. }
  451. }
  452. fr = malloc(sizeof(Frec));
  453. if(fr == 0 || (fr->op = strdup(p)) == 0)
  454. fatal("no memory");
  455. fr->nread = f->nread;
  456. fr->nwrite = f->nwrite;
  457. fr->bread = f->bread;
  458. fr->bwrite = f->bwrite;
  459. fr->opens = 1;
  460. if(frhead == 0) {
  461. frhead = fr;
  462. frtail = fr;
  463. }
  464. else {
  465. frtail->next = fr;
  466. frtail = fr;
  467. }
  468. fr->next = 0;
  469. }