xref: /inferno-os/os/port/devbench.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include <interp.h>
7 #include "io.h"
8 #include "../port/error.h"
9 #include <isa.h>
10 #include "kernel.h"
11 
12 /* Builtin module support */
13 #include "bench.h"
14 #include "benchmod.h"
15 
16 typedef enum { None, Calibrate, Base, Op, Intr, Dis, Gc, MS2T, xTest};
17 static struct {
18 	int		inuse;	/* reference count */
19 	int		test;
20 	void*	scratch;
21 	char*	buf;
22 	int		bufsz;
23 	char*	wpos;
24 	void		(*op)(void);
25 	vlong	tickstart;
26 } bench;
27 
28 static void
29 log(char *msg, ...)
30 {
31 	va_list ap;
32 
33 	va_start(ap, msg);
34 	bench.wpos = vseprint(bench.wpos, bench.buf+bench.bufsz, msg, ap);
35 	va_end(ap);
36 }
37 
38 void
39 elog(char *msg, ...)
40 {
41 	va_list ap;
42 
43 	if(bench.buf == 0)
44 		return;
45 	va_start(ap, msg);
46 	bench.wpos = vseprint(bench.wpos, bench.buf+bench.bufsz, msg, ap);
47 	va_end(ap);
48 }
49 
50 static void
51 clear(void)
52 {
53 	bench.wpos = bench.buf;
54 }
55 
56 static long
57 rep(void *to, long n, ulong offset)
58 {
59 	long left = bench.wpos - bench.buf - offset;
60 	if(left < 0)
61 		left = 0;
62 	if(n > left)
63 		n = left;
64 	memmove(to, bench.buf+offset, n);
65 	return n;
66 }
67 
68 static long
69 notest(int report, void *va, long n, ulong offset)
70 {
71 	USED(report, va, n, offset);
72 	if(report)
73 		return rep(va, n, offset);
74 	return 0;
75 }
76 
77 // Calibration
78 static long MS2TS = 0;	// time stamps per millisec
79 static long US2TS = 0;	// time stamps per microsecond
80 
81 static long
82 cal(int report, void *va, long n, ulong offset)
83 {
84 	int tot, i, lim, low, max, pl, mdelay;
85 	ulong t;
86 	if(report)
87 		return rep(va, n, offset);
88 	clear();
89 	setpri(PriRealtime);
90 	lim = 1000;
91 	low = 64000000;
92 	max = 0;
93 	tot = 0;
94 	mdelay = 1000;
95 	for(i=0; i<lim; i++){
96 		do{
97 			pl = splhi();
98 			t = archrdtsc32();
99 			microdelay(mdelay);
100 			t = archrdtsc32() - t;
101 			splx(pl);
102 		} while(t < 0);
103 		if(t < low)
104 			low = t;
105 		if(t > max)
106 			max = t;
107 		tot += t;
108 	}
109 	MS2TS = tot/lim;
110 	US2TS = MS2TS/1000;
111 	if(va)
112 		log("mdelay=%lud lim=%lud tot=%lud low=%lud max=%lud\n", mdelay, lim, tot, low, max);
113 	setpri(PriNormal);
114 	return n;
115 }
116 
117 /*
118  * ticks to format string
119  */
120 /*static*/ char *
121 ts2str(vlong ticks)
122 {
123 #define Nbuf 5
124 	static char b[Nbuf][40];
125 	static int n=Nbuf-1;
126 	char *fmt, *unit;
127 	double d;
128 
129 	if(0){
130 		print("ticks=%lld MS2TS=%ld\n", ticks, MS2TS);
131 		d = (double)ticks;
132 		print("1:%f\n", d);
133 		d = (double)ticks*1000;
134 		//print("2:%f\n", d);
135 		d = ((double)ticks)/MS2TS;
136 		//print("3:%f\n", d);
137 	}
138 	n = (n+1)%Nbuf;
139 	if(ticks > MS2TS*1000) {
140 		fmt = "%.2f %s";
141 		unit = "s";
142 		d = ((double)ticks/MS2TS) * 1000.0;
143 	} else if(ticks > MS2TS) {
144 		fmt = "%.2f %s";
145 		unit = "ms";
146 		d = (double)ticks/MS2TS;
147 	} else if(ticks > MS2TS/1000) {
148 		fmt = "%.2f %s";
149 		unit = "us";
150 		d = ((double)ticks*1000)/MS2TS;
151 	} else {
152 		fmt = "%.2f %s";
153 		unit = "ns";
154 		d = ((double)ticks*1000*1000)/MS2TS;
155 	}
156 	sprint(b[n], fmt, d, unit);
157 	return b[n];
158 }
159 
160 /*
161  * ticks to microseconds
162  */
163 static double
164 ts2us(vlong ticks)
165 {
166 	return ((double)ticks*1000)/MS2TS;
167 }
168 
169 /*
170  * microseconds timestamp
171  */
172 static vlong
173 bus(int reset)
174 {
175 	vlong now;
176 	if(US2TS == 0)
177 		return 0;
178 	if(reset) {
179 		bench.tickstart = archrdtsc();
180 		return 0;
181 	}
182 	now = archrdtsc();
183 	return ((now-bench.tickstart))/US2TS;
184 }
185 
186 // Base
187 static long
188 base(int report, void *va, long n, ulong offset)
189 {
190 	int tot, i, lim, low, max, pl;
191 	ulong t;
192 	char *bm;
193 
194 	if(report)
195 		return rep(va, n, offset);
196 	clear();
197 	setpri(PriRealtime);
198 	lim = 1000;
199 	low = 64000000;
200 	max = 0;
201 	tot = 0;
202 	for(i=0; i<lim; i++){
203 		do {
204 			pl = splhi();
205 			t = archrdtsc32();
206 			// do nothing
207 			t = archrdtsc32() - t;
208 			splx(pl);
209 		} while(t < 0);
210 		if(t < low)
211 			low = t;
212 		if(t > max)
213 			max = t;
214 		tot += t;
215 	}
216 	bm = ts2str(tot/lim);
217 	log("%d %lud %lud %lud %lud (%s)\n", up->pid, lim, tot, low, max, bm);
218 	setpri(PriNormal);
219 	return n;
220 }
221 
222 // Timeop
223 
224 typedef struct Psync Psync;
225 
226 enum {
227 	Maxprocs=3,
228 };
229 
230 struct Psync {
231 	Rendez	r;
232 	int	flag;
233 	int	id;
234 	int	awaken;
235 };
236 static Psync timesync[Maxprocs];
237 static Ref nactive;
238 static Ref nbusy;
239 static RWlock sync;
240 
241 static void
242 nilop(void)
243 {
244 }
245 
246 static int
247 timev(void *a)
248 {
249 	return *(int*)a;
250 }
251 
252 static void
253 timeop0(void *ap)
254 {
255 	int tot, i, lim, low, max;
256 	ulong t;
257 	Psync *ps;
258 	char *bm;
259 
260 	ps = ap;
261 	setpri(PriRealtime);
262 	incref(&nactive);
263 	sleep(&ps->r, timev, &ps->flag);
264 	rlock(&sync);
265 	lim = 1000;
266 	low = 64000000;
267 	max = 0;
268 	tot = 0;
269 	for(i=0; i<lim; i++){
270 		do{
271 			t = archrdtsc32();
272 			(*bench.op)();
273 			t = archrdtsc32() - t;
274 		}while(t < 0);
275 		if(t < low)
276 			low = t;
277 		if(t > max)
278 			max = t;
279 		tot += t;
280 	}
281 	bm = ts2str(tot/lim);
282 	log("%d %lud %lud %lud %lud (%s)\n", up->pid, lim, tot, low, max, bm);
283 	runlock(&sync);
284 	pexit("", 0);
285 }
286 
287 static long
288 timeop(int report, void *va, long n, ulong offset)
289 {
290 	int i, np, pl;
291 
292 	if(report)
293 		return rep(va, n, offset);
294 	clear();
295 	bench.op = 0;
296 	if(strncmp(va, "nil", 3) == 0)
297 		bench.op = nilop;
298 	else if(strncmp(va, "sched", 5) == 0)
299 		bench.op = sched;
300 	else
301 		return 0;
302 	for(np=1; np<=Maxprocs; np++) {
303 		nactive.ref = 0;
304 		wlock(&sync);
305 		log("%d procs\n", np);
306 		setpri(PriRealtime);
307 		for(i=0; i<np; i++) {
308 			timesync[i].id = i;
309 			kproc("timeop", timeop0, &timesync[i], 0);
310 		}
311 		while(nactive.ref < np)
312 			tsleep(&up->sleep, return0, 0, 20);
313 		for(i=0; i<np; i++){
314 			timesync[i].flag = 1;
315 			wakeup(&timesync[i].r);
316 		}
317 		sched();
318 		pl = splhi();
319 		setpri(PriNormal);
320 		wunlock(&sync);
321 		// now they run
322 		wlock(&sync);		// wait for last reader
323 		wunlock(&sync);
324 		splx(pl);
325 	}
326 	return n;
327 }
328 
329 typedef struct Ictr Ictr;
330 struct Ictr {
331 	ulong	base;
332 	ulong	sleep;
333 	ulong	spllo;
334 	ulong	intr;
335 	ulong	isave;
336 	ulong	arrive;
337 	ulong	wakeup;
338 	ulong	awake;
339 };
340 static Ictr counters[5/*100*/], *curct;
341 static int intrwant;
342 static Rendez vous;
343 int	spltbl;	/* set by spllo */
344 int	intrtbl;	/* set by intrvec() */
345 int	isavetbl;	/* set by intrvec() */
346 
347 static int ienable;
348 
349 
350 static void
351 intrwake(void)
352 {
353 	if(ienable == 0)
354 		return;
355 	ienable = 0;
356 	if(spltbl == 0)		// not used here
357 		curct->spllo = curct->intr = curct->isave = archrdtsc32();
358 	else {
359 		curct->spllo = spltbl;
360 		curct->intr = intrtbl;
361 		curct->isave = isavetbl;
362 	}
363 	curct->arrive = archrdtsc32();
364 	intrwant = 0;
365 	wakeup(&vous);
366 	curct->wakeup = archrdtsc32();
367 }
368 
369 /*
370  * sleep calls intrtest with splhi (under lock):
371  * provoke the interrupt now, so that it is guaranteed
372  * not to happen until sleep has queued the process,
373  * forcing wakeup to do something.
374  */
375 static int
376 intrtest(void*)
377 {
378 	ienable = 1;		/* enable recording on interrupt */
379 	curct->sleep = archrdtsc32();
380 	return intrwant==0;
381 }
382 
383 static long
384 intrtime(int report, void *va, long n, ulong offset)
385 {
386 	Ictr *ic;
387 	long t;
388 	int i;
389 	char *bm;
390 	if(report)
391 		return rep(va, n, offset);
392 	clear();
393 
394 	setpri(PriRealtime);
395 	sched();
396 	curct = counters;
397 	ienable = 0;
398 	addclock0link(intrwake, MS2HZ);
399 	for(i=0; i<nelem(counters); i++){
400 		curct = &counters[i];
401 		intrwant = 1;
402 		curct->base = archrdtsc32();
403 		sleep(&vous, intrtest, nil);
404 		curct->awake = archrdtsc32();
405 		sched();	/* just to slow it down between trials */
406 	}
407 	log("interrupt\n");
408 	for(i=0; i<nelem(counters); i++){
409 		ic = &counters[i];
410 		t = ic->awake - ic->base;
411 		bm = ts2str(ic->awake - ic->arrive);
412 		ic->awake -= ic->wakeup;
413 		ic->wakeup -= ic->arrive;
414 		ic->arrive -= ic->isave;
415 		ic->isave -= ic->intr;
416 		ic->intr -= ic->spllo;
417 		ic->spllo -= ic->sleep;
418 		ic->sleep -= ic->base;
419 		log("%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld\t%ld (%s)\n", ic->sleep, ic->spllo, ic->intr, ic->isave, ic->arrive, ic->wakeup, ic->awake, t, bm);
420 	}
421 	setpri(PriNormal);
422 	return n;
423 }
424 
425 
426 /* DIS operation timing */
427 
428 typedef struct  {
429 	vlong	n;		/* count */
430 	vlong	min;
431 	vlong 	max;
432 	vlong	sum;
433 	vlong	sumsq;	/* sum of squares */
434 } Stat;
435 
436 static void
437 stat(enum { Reset, Inc } op, Stat *c, vlong val)
438 {
439 	switch(op) {
440 	case	Reset:
441 		c->n = 0;
442 		c->sum = 0;
443 		c->sumsq = 0;
444 		c->min = 0;
445 		c->max = 0;
446 		break;
447 	case Inc:
448 		c->n++;
449 		c->sum += val;
450 		c->sumsq += val*val;
451 		break;
452 	}
453 	if(val < c->min || c->n == 1)
454 		c->min = val;
455 	if(val > c->max || c->n == 1)
456 		c->max = val;
457 }
458 
459 static void
460 statinc(Stat *d, Stat *s)
461 {
462 	d->n += s->n;
463 	d->sum += s->sum;
464 	d->sumsq += s->sumsq;
465 	if(s->min < d->min || d->n == s->n)
466 		d->min = s->min;
467 	if(s->max > d->max || d->n == s->n)
468 		d->max = s->max;
469 }
470 
471 enum
472 {
473 	HSIZE	= 31,
474 	MAXCOUNT	= 100000000L,
475 };
476 
477 typedef struct {
478 	int		op;
479 	int		pc;
480 	long		count;
481 	Stat	t;		/* combined dec and execution time */
482 } Istat;
483 
484 typedef struct Mstat Mstat;
485 struct Mstat {
486 	char*	name;
487 	char*	path;
488 	int		ninst;
489 	Istat*	inst;
490 	Inst*		base;
491 	Mstat*	hash;
492 	Mstat*	link;
493 };
494 
495 struct
496 {
497 	Mstat*	hash[HSIZE];
498 	Mstat*	list;
499 } vmstat;
500 
501 extern struct			/* see ../../interp/tab.h:/keywds/ */
502 {
503 	char*	name;
504 	int	op;
505 	int	terminal;
506 }keywds[];
507 
508 static char *
509 opname(int op)
510 {
511 	char *name;
512 
513 	if(op < 0 || op >= MAXDIS)
514 		return "Unknown";
515 	return keywds[op].name;
516 	if(name == 0)
517 		name = "<Noname>";
518 	return name;
519 }
520 
521 static void
522 mreset(void)
523 {
524 	Mstat *mp, *link;
525 
526 	for(mp=vmstat.list; mp; mp=link) {
527 		link = mp->link;
528 		free(mp->inst);
529 		free(mp);
530 	}
531 	vmstat.list = 0;
532 	memset(vmstat.hash, 0, HSIZE*sizeof(Mstat*));
533 }
534 
535 static ulong
536 hash(void *s)
537 {
538 	ulong sum = 0;
539 	uchar *a = s;
540 
541 	while(*a)
542 		sum = (sum << 1) + *a++;
543 	return sum%HSIZE;
544 }
545 
546 static Mstat *
547 mlookup(Module *mod)
548 {
549 	Mstat *m;
550 	ulong h;
551 
552 	for(m=vmstat.hash[hash(mod->name)]; m; m=m->hash)
553 		if(strcmp(m->name, mod->name) == 0
554 		&& strcmp(m->path, mod->path) == 0) {
555 			return m;
556 		}
557 
558 
559 	m = malloc(sizeof(Mstat));
560 	if(m == 0)
561 		return 0;
562 	kstrdup(&m->name, mod->name);
563 	kstrdup(&m->path, mod->path);
564 	m->ninst = mod->nprog;
565 	m->inst = malloc(m->ninst*sizeof(Istat));
566 	if(m->path == 0 || m->inst == 0)
567 		return 0;
568 	m->base = mod->prog;
569 	m->link = vmstat.list;
570 	vmstat.list = m;
571 	h = hash(m->name);
572 	m->hash = vmstat.hash[h];
573 	vmstat.hash[h] = m;
574 	return m;
575 }
576 
577 /* interpreted code Dis timing */
578 void
579 bxec(Prog *p)
580 {
581 	int op, pc;
582 	vlong t0, t;
583 	Mstat*	ms;
584 	Istat*	is;
585 	Module *om;
586 
587 	R = p->R;
588 	R.MP = R.M->MP;
589 	R.IC = p->quanta;
590 
591 	if(p->kill != nil) {
592 		char *m;
593 		m = p->kill;
594 		p->kill = nil;
595 		error(m);
596 	}
597 
598 	if(R.M->compiled)
599 		comvec();
600 	else {
601 		om = 0;
602 		ms = mlookup(R.M->m);
603 		do {
604 			op = R.PC->op;
605 			pc = R.PC-R.M->prog;
606 			if(om != R.M->m) {
607 				om = R.M->m;
608 				ms = mlookup(R.M->m);
609 			}
610 
611 			t0 = archrdtsc();
612 			dec[R.PC->add]();
613 			R.PC++;
614 			optab[op]();
615 			t = archrdtsc();
616 			if(ms) {
617 				is = &ms->inst[pc];
618 				if(is->count < MAXCOUNT) {
619 					if(is->count++ == 0) {
620 						is->op = op;
621 						is->pc = pc;
622 					}
623 					stat(Inc, &is->t, t-t0);
624 				}
625 			}
626 			if(op==ISPAWN || op==IMSPAWN) {
627 				Prog *new = delruntail(Pdebug);
628 				new->xec = bxec;
629 				addrun(new);
630 			}
631 		} while(--R.IC != 0);
632 	}
633 
634 	p->R = R;
635 }
636 
637 /* compiled code Dis timing */
638 
639 static struct {			/* compiled code timing */
640 	int		set;
641 	int 		op, pc;		/* Dis opcode and program counter */
642 	vlong	t0, t;			/* time-in and time-out */
643 	vlong	base;		/* cost of doing the timing */
644 	Mstat	*ms;
645 	Module	*om;
646 	int		timing;		/* between "dis timer start" and stop */
647 } C;
648 
649 enum { Nop = 0 };	/* opcode value for Dis NOP instruction */
650 void
651 dopostcomp(vlong t)
652 {
653 	Istat*	is;
654 
655 	C.t = t;
656 	C.set = 0;
657 	if(C.ms != 0) {
658 		is = &C.ms->inst[C.pc];
659 		if(C.op == Nop) {			/* NOP  calibration */
660 			vlong newbase = C.t - C.t0;
661 			if(C.base == 0 || newbase < C.base)
662 				C.base = newbase;
663 		}
664 		if(is->count < MAXCOUNT) {
665 			if(is->count++ == 0) {
666 				is->op = C.op;
667 				is->pc = C.pc;
668 			}
669 			stat(Inc, &is->t, C.t-C.t0/*-C.base*/);
670 		}
671 	}
672 }
673 
674 void
675 postcomp(void)
676 {
677 	vlong t;
678 
679 	t = archrdtsc();
680 	if(C.timing == 0 || C.set == 0)
681 		return;
682 	dopostcomp(t);
683 }
684 
685 void
686 precomp(void)
687 {
688 	vlong t;
689 
690 	t = archrdtsc();
691 	if(C.timing == 0)
692 		return;
693 	if(C.set)
694 		dopostcomp(t);
695 	C.pc = *(ulong *)R.m;
696 	C.op = *(ulong *)R.s;
697 	if(C.om != R.M->m) {
698 		C.om = R.M->m;
699 		C.ms = mlookup(R.M->m);
700 	}
701 	C.set = 1;
702 	C.t0 = archrdtsc();
703 }
704 
705 /* standard deviation */
706 static vlong
707 sdev(Stat *s)
708 {
709 	extern double sqrt(double);
710 	vlong var;
711 	var = s->sum;
712 	var *= var/s->n;
713 	var = (s->sumsq - var)/s->n;
714 	return (vlong)sqrt(var);
715 }
716 
717 /*
718  * Use the sequence:
719  * 1. "timer startclr" or "timer start", then,
720  * 2. Any DIS operations, and,
721  * 3. "timer stop", to stop timing.
722  * 4. Read the results from the data file after:
723  *	a) "timer report" to get module/pc level results, or
724  *	b) "timer summary" to get opcode level results
725  */
726 static long
727 distime(int report, void *va, long n, ulong offset)
728 {
729 	Prog *p;
730 	Mstat *mp;
731 	Istat *ip, *ep;
732 
733 	if(report)
734 		return rep(va, n, offset);
735 	clear();
736 	acquire();
737 	p = currun();
738 	if(strncmp(va, "timer startclr", 14) == 0) {
739 		mreset();
740 		memset(&C, 0, sizeof(C));
741 		C.timing = 1;
742 		p->xec = bxec;
743 	} else if(strncmp(va, "timer start", 11) == 0) {
744 		p->xec = bxec;
745 		C.timing = 1;
746 	} else if(strncmp(va, "timer stop", 10) == 0) {
747 		p->xec = xec;				/* bug: stop all xec threads */
748 		C.timing = 0;
749 	} else if(strncmp(va, "timer nilop", 11) == 0) {
750 	} else if(strncmp(va, "timer report", 12) == 0)	/* by address */
751 		for(mp=vmstat.list; mp; mp=mp->link) {
752 			ep = mp->inst + mp->ninst;
753 			for(ip=mp->inst; ip<ep; ip++)
754 				if(ip->count > 0) {
755 					char *mean = ts2str(ip->t.sum/ip->count);
756 					char *min = ts2str(ip->t.min);
757 					char *max = ts2str(ip->t.max);
758 					char *std = ts2str(sdev(&ip->t));
759 					log("%s %d %s %ld %s %s %s %s\n", mp->path, ip->pc, opname(ip->op), ip->count, mean, min, max, std);
760 				}
761 		}
762 	else if(strncmp(va, "timer summary", 13) == 0) {	/* by opcode */
763 		static Stat T[MAXDIS];
764 		int i;
765 
766 		for(i=0; i<MAXDIS; i++)
767 			stat(Reset, &T[i], 0);
768 		for(mp=vmstat.list; mp; mp=mp->link) {
769 			ep = mp->inst + mp->ninst;
770 			for(ip=mp->inst; ip<ep; ip++)
771 				if(ip->count > 0)
772 					statinc(&T[ip->op], &ip->t);
773 		}
774 		for(i=0; i<MAXDIS; i++) {
775 			Stat *t = &T[i];
776 			char *mean = "0.00 ms";
777 			char *min = "0.00 ms";
778 			char *max = "0.00 ms";
779 			char *std = "0.00 ms";
780 			if(t->n > 0) {
781 				mean = ts2str(t->sum/t->n);
782 				min = ts2str(t->min);
783 				max = ts2str(t->max);
784 				std = ts2str(sdev(t));
785 			}
786 			log("%d %s %lld %s %s %s %s\n", i, opname(i), t->n, mean, min, max, std);
787 		}
788 	} else
789 		n = 0;
790 	R.IC = 1;
791 	release();
792 
793 	return n;
794 }
795 
796 /*
797  * Garbage collection
798  */
799 static int nidle;
800 
801 int
802 idlegc(void *p)
803 {
804 	int done;
805 	Prog *head;
806 	vlong t0, t1, tot;
807 	USED(p);
808 
809 	head = progn(0);	/* isched.head */
810 	done = gccolor + 3;
811 	tot = 0;
812 	while(gccolor < done && gcruns()) {
813 		if(tready(nil))
814 			break;
815 		t0 = archrdtsc();
816 		rungc(head);
817 		t1 = archrdtsc();
818 		t1 -= t0;
819 		tot += t1;
820 //		log(" %.2f",  ts2us(t1));
821 	}
822 	log(" %.2f",  ts2us(tot));
823 	nidle--;
824 	if(nidle == 0) {
825 		log("\n");
826 		return 1;
827 	}
828 	return 0;
829 }
830 
831 static long
832 gctime(int report, void *va, long n, ulong offset)
833 {
834 	int i;
835 	vlong t0, t1;
836 	Prog *head;
837 
838 	if(report)
839 		return rep(va, n, offset);
840 	clear();
841 	acquire();
842 	head = progn(0);	/* isched.head */
843 /*
844 	if(strncmp(va, "idle", 4) == 0) {
845 		nidle = 100;
846 		log("GCIDLE:1l:Observation:n:Time:us");
847 		atidle(idlegc, 0);
848 	} else if(strncmp(va, "stop", 4) == 0) {
849 		atidledont(idlegc, 0);
850 	} else
851 */
852 	if(strncmp(va, "sched", 5) == 0) {
853 		log("GCSCHED:1l:Observation:n:Time:us");
854 		for(i=0; i<1000; i++) {
855 			t0 = archrdtsc();
856 			rungc(head);
857 			t1 = archrdtsc();
858 			log(" %.2f",  ts2us(t1-t0));
859 			release();
860 			acquire();
861 		}
862 		log("\n");
863 	} else if(strncmp(va, "acquire", 7) == 0) {
864 		log("GCACQUIRE:1l:Observation:n:Time:us");
865 		for(i=0; i<1000; i++) {
866 			t0 = archrdtsc();
867 			release();
868 			acquire();
869 			head = progn(0);	/* isched.head */
870 			rungc(head);
871 			release();
872 			acquire();
873 			t1 = archrdtsc();
874 			log(" %.2f",  ts2us(t1-t0));
875 		}
876 		log("\n");
877 	}
878 
879 	release();
880 
881 	return n;
882 }
883 
884 
885 /*
886  * Request the number of time stamp ticks per millisecond
887  */
888 static long
889 ms2ts(int report, void *va, long n, ulong offset)
890 {
891 	if(report)
892 		return rep(va, n, offset);
893 	log("%.ld\n", MS2TS);
894 	return n;
895 }
896 
897 /*
898  * test
899  */
900 
901 static long
902 test(int report, void *va, long n, ulong offset)
903 {
904 //	vlong v;
905 	double d;
906 	if(report)
907 		return rep(va, n, offset);
908 //	v = 5;
909 //	print("vlong %lld\n", v);
910 //	print("before cast\n");
911 //	d = (double)v;
912 //	print("after cast\n");
913 //	print("before assign\n");
914 	d=100.0;
915 	print("after assign\n");
916 	print("double %f\n", d);
917 //	log("%lld %f\n", v, d);
918 	return n;
919 }
920 
921 /*
922  * $Bench builtin support
923  */
924 void
925 Bench_reset(void *)
926 {
927 	bus(1);
928 }
929 
930 void
931 Bench_microsec(void *fp)
932 {
933 	F_Bench_microsec *f;
934 
935 	f = fp;
936 	*f->ret = bus(0);
937 }
938 
939 void
940 Bench_disablegc(void *)
941 {
942 	gclock();
943 }
944 
945 void
946 Bench_enablegc(void *)
947 {
948 	gcunlock();
949 }
950 
951 
952 #define fdchk(x)	((x) == (Bench_FD*)H ? -1 : (x)->fd)
953 void
954 Bench_read(void *fp)
955 {
956 	int n;
957 	F_Bench_read *f;
958 	vlong usrelease, uskread, usacquire, ussched;
959 
960 	f = fp;
961 	n = f->n;
962 	if(f->buf == (Array*)H) {
963 		*f->ret = 0;
964 		return;
965 	}
966 	if(n > f->buf->len)
967 		n = f->buf->len;
968 
969 	bus(1);
970 	release();
971 	usrelease = bus(0);
972 	*f->ret = kread(fdchk(f->fd), f->buf->data, n);
973 	uskread = bus(0);
974 	acquire();
975 	usacquire = bus(0);
976 	sched();
977 	ussched = bus(0);
978 	log("%lld %lld %lld %lud %lld\n", usrelease, uskread, usacquire, m->ticks, ussched);
979 }
980 
981 
982 /*
983  * driver support
984  */
985 long (*Test[])(int report, void *va, long n, ulong offset) = {
986 	[None]		notest,
987 	[Calibrate]	cal,
988 	[Base]		base,
989 	[Op]			timeop,
990 	[Intr]			intrtime,
991 	[Dis]			distime,
992 	[Gc]			gctime,
993 	[MS2T]		ms2ts,
994 	[xTest]		test,
995 };
996 
997 enum {
998 	Benchdirqid,
999 	Benchdataqid,
1000 	Benchctlqid,
1001 	Benchusqid,
1002 };
1003 #define Data 0
1004 static Dirtab benchtab[]={
1005 	".",		{Benchdirqid,0,QTDIR},	0,	0555,
1006 	"bdata",	{Benchdataqid},		0,	0444,
1007 	"bctl",	{Benchctlqid},		0,	0660,
1008 	"busec",	{Benchusqid},		0,	0660,
1009 };
1010 
1011 static void
1012 benchreset(void)
1013 {
1014 	builtinmod("$Bench", Benchmodtab);
1015 }
1016 
1017 static Chan*
1018 benchattach(char *spec)
1019 {
1020 	bench.inuse++;
1021 	if(bench.inuse == 1) {
1022 		bench.bufsz = 100*READSTR;
1023 		bench.buf = xalloc(bench.bufsz);
1024 		bench.wpos = bench.buf;
1025 		if(bench.buf == 0)
1026 			error(Enomem);
1027 		bench.test = None;
1028 		cal(0, 0, 0, 0);
1029 	}
1030 	return devattach('x', spec);
1031 }
1032 
1033 void
1034 benchshutdown(void)
1035 {
1036 	bench.inuse--;
1037 	if(bench.inuse == 0)
1038 		xfree(bench.buf);
1039 }
1040 
1041 static Walkqid*
1042 benchwalk(Chan *c, Chan *nc, char **name, int nname)
1043 {
1044 	return devwalk(c, nc, name, nname, benchtab, nelem(benchtab), devgen);
1045 }
1046 
1047 static Chan*
1048 benchopen(Chan *c, int omode)
1049 {
1050 	if(c->qid.path == Benchdirqid){
1051 		if(omode != OREAD)
1052 			error(Eperm);
1053 	}
1054 	c->mode = openmode(omode);
1055 	c->flag |= COPEN;
1056 	c->offset = 0;
1057 	return c;
1058 }
1059 
1060 static int
1061 benchstat(Chan *c, uchar *dp, int n)
1062 {
1063 	switch((ulong)c->qid.path){
1064 	case Benchdataqid:
1065 		benchtab[Data].length = bench.wpos - bench.buf;
1066 	}
1067 	return devstat(c, dp, n, benchtab, nelem(benchtab), devgen);
1068 }
1069 
1070 static void
1071 benchclose(Chan*)
1072 {
1073 }
1074 
1075 static long
1076 benchread(Chan *c, void *buf, long n, vlong offset)
1077 {
1078 	vlong us;
1079 	char tmp[64];
1080 
1081 	switch((ulong)c->qid.path){
1082 	case Benchdirqid:
1083 		return devdirread(c, buf, n, benchtab, nelem(benchtab), devgen);
1084 
1085 	case Benchdataqid:
1086 		return Test[bench.test](1, buf, n, offset);
1087 
1088 	case Benchusqid:
1089 		us = archrdtsc();
1090 		us /= US2TS;
1091 		snprint(tmp, sizeof(tmp), "%.lld", us);
1092 		return readstr(0, buf, n, tmp);
1093 	default:
1094 		n = 0;
1095 		break;
1096 	}
1097 	return n;
1098 }
1099 
1100 static long
1101 benchwrite(Chan *c, void *buf, long n, vlong offset)
1102 {
1103 	int argn = n;
1104 
1105 	switch((ulong)c->qid.path){
1106 	case Benchctlqid:
1107 		bench.test = None;
1108 		memset((char *)bench.buf, 0, bench.bufsz);
1109 		bench.wpos = bench.buf;
1110 		if(strncmp(buf, "test", 4) == 0)
1111 			bench.test = xTest;
1112 		else if(strncmp(buf, "calibrate", 9) == 0)
1113 			bench.test = Calibrate;
1114 		else if(strncmp(buf, "base", 4) == 0)
1115 			bench.test = Base;
1116 		else if(strncmp(buf, "intr", 4) == 0)
1117 			bench.test = Intr;
1118 		else if(strncmp(buf, "op ", 3) == 0) {
1119 			bench.test = Op;
1120 			buf = (char *)buf + 3;
1121 			argn -= 3;
1122 		} else if(strncmp(buf, "dis ", 4) == 0) {
1123 			bench.test = Dis;
1124 			buf = (char *)buf + 4;
1125 			argn -= 4;
1126 		} else if(strncmp(buf, "gc ", 3) == 0) {
1127 			bench.test = Gc;
1128 			buf = (char *)buf + 3;
1129 			argn -= 3;
1130 		} else if(strncmp(buf, "ms2ts", 5) == 0)
1131 			bench.test = MS2T;
1132 		else
1133 			error(Ebadctl);
1134 		Test[bench.test](0, buf, argn, offset);
1135 		break;
1136 	case Benchusqid:
1137 		bench.tickstart = archrdtsc();
1138 		break;
1139 	default:
1140 		error(Ebadusefd);
1141 	}
1142 	return n;
1143 }
1144 
1145 Dev	benchdevtab = {
1146 	'x',
1147 	"bench",
1148 
1149 	benchreset,
1150 	devinit,
1151 	benchshutdown,
1152 	benchattach,
1153 	benchwalk,
1154 	benchstat,
1155 	benchopen,
1156 	devcreate,
1157 	benchclose,
1158 	benchread,
1159 	devbread,
1160 	benchwrite,
1161 	devbwrite,
1162 	devremove,
1163 	devwstat,
1164 
1165 };
1166