Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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