xref: /plan9-contrib/sys/src/ape/lib/ap/plan9/profile.c (revision 0ceffd3c74a1de03d13dc3aa8fa06e39d209dbd4)
13e12c5d1SDavid du Colombier #include	<stdio.h>
23e12c5d1SDavid du Colombier #include	<stdlib.h>
33e12c5d1SDavid du Colombier #include	<string.h>
43e12c5d1SDavid du Colombier #include	<unistd.h>
53801c5d3SDavid du Colombier #include	<errno.h>
63e12c5d1SDavid du Colombier #include	<sys/types.h>
73e12c5d1SDavid du Colombier #include	<fcntl.h>
83e12c5d1SDavid du Colombier #include	"sys9.h"
93e12c5d1SDavid du Colombier 
103801c5d3SDavid du Colombier enum {
113801c5d3SDavid du Colombier 	Profoff,			/* No profiling */
123801c5d3SDavid du Colombier 	Profuser,			/* Measure user time only (default) */
133801c5d3SDavid du Colombier 	Profkernel,			/* Measure user + kernel time */
143801c5d3SDavid du Colombier 	Proftime,			/* Measure total time */
153801c5d3SDavid du Colombier 	Profsample,			/* Use clock interrupt to sample (default when there is no cycle counter) */
163801c5d3SDavid du Colombier }; /* what */
173801c5d3SDavid du Colombier 
183801c5d3SDavid du Colombier typedef long long vlong;
193801c5d3SDavid du Colombier typedef unsigned long ulong;
203801c5d3SDavid du Colombier typedef unsigned long long uvlong;
213801c5d3SDavid du Colombier 
223801c5d3SDavid du Colombier #include	"/sys/include/tos.h"
233801c5d3SDavid du Colombier 
243801c5d3SDavid du Colombier extern	void*	sbrk(ulong);
253e12c5d1SDavid du Colombier extern	long	_callpc(void**);
263e12c5d1SDavid du Colombier extern	long	_savearg(void);
273801c5d3SDavid du Colombier extern	void	_cycles(uvlong*);	/* 64-bit value of the cycle counter if there is one, 0 if there isn't */
283e12c5d1SDavid du Colombier 
293801c5d3SDavid du Colombier static ulong	khz;
303801c5d3SDavid du Colombier static ulong	perr;
313801c5d3SDavid du Colombier static int	havecycles;
323e12c5d1SDavid du Colombier 
333e12c5d1SDavid du Colombier typedef	struct	Plink	Plink;
343e12c5d1SDavid du Colombier struct	Plink
353e12c5d1SDavid du Colombier {
363801c5d3SDavid du Colombier 	Plink	*old;
373e12c5d1SDavid du Colombier 	Plink	*down;
383e12c5d1SDavid du Colombier 	Plink	*link;
393e12c5d1SDavid du Colombier 	long	pc;
403e12c5d1SDavid du Colombier 	long	count;
413801c5d3SDavid du Colombier 	vlong	time;
423e12c5d1SDavid du Colombier };
433e12c5d1SDavid du Colombier 
443801c5d3SDavid du Colombier #pragma profile off
453e12c5d1SDavid du Colombier 
463e12c5d1SDavid du Colombier ulong
_profin(void)473e12c5d1SDavid du Colombier _profin(void)
483e12c5d1SDavid du Colombier {
493e12c5d1SDavid du Colombier 	void *dummy;
503e12c5d1SDavid du Colombier 	long pc;
513e12c5d1SDavid du Colombier 	Plink *pp, *p;
523e12c5d1SDavid du Colombier 	ulong arg;
533801c5d3SDavid du Colombier 	vlong t;
543e12c5d1SDavid du Colombier 
553e12c5d1SDavid du Colombier 	arg = _savearg();
563e12c5d1SDavid du Colombier 	pc = _callpc(&dummy);
573801c5d3SDavid du Colombier 	pp = _tos->prof.pp;
583801c5d3SDavid du Colombier 	if(pp == 0 || (_tos->prof.pid && _tos->pid != _tos->prof.pid))
593e12c5d1SDavid du Colombier 		return arg;
603e12c5d1SDavid du Colombier 
613e12c5d1SDavid du Colombier 	for(p=pp->down; p; p=p->link)
623e12c5d1SDavid du Colombier 		if(p->pc == pc)
633e12c5d1SDavid du Colombier 			goto out;
643801c5d3SDavid du Colombier 	p = _tos->prof.next + 1;
653801c5d3SDavid du Colombier 	if(p >= _tos->prof.last){
663801c5d3SDavid du Colombier 		_tos->prof.pp = 0;
673801c5d3SDavid du Colombier 		perr++;
683e12c5d1SDavid du Colombier 		return arg;
693e12c5d1SDavid du Colombier 	}
703801c5d3SDavid du Colombier 	_tos->prof.next = p;
713e12c5d1SDavid du Colombier 	p->link = pp->down;
723e12c5d1SDavid du Colombier 	pp->down = p;
733e12c5d1SDavid du Colombier 	p->pc = pc;
743e12c5d1SDavid du Colombier 	p->old = pp;
753e12c5d1SDavid du Colombier 	p->down = 0;
763e12c5d1SDavid du Colombier 	p->count = 0;
773801c5d3SDavid du Colombier 	p->time = 0LL;
783e12c5d1SDavid du Colombier 
793e12c5d1SDavid du Colombier out:
803801c5d3SDavid du Colombier 	_tos->prof.pp = p;
813e12c5d1SDavid du Colombier 	p->count++;
823801c5d3SDavid du Colombier 	switch(_tos->prof.what){
833801c5d3SDavid du Colombier 	case Profkernel:
843801c5d3SDavid du Colombier 		p->time = p->time - _tos->pcycles;
853801c5d3SDavid du Colombier 		goto proftime;
863801c5d3SDavid du Colombier 	case Profuser:
873801c5d3SDavid du Colombier 		/* Add kernel cycles on proc entry */
883801c5d3SDavid du Colombier 		p->time = p->time + _tos->kcycles;
893801c5d3SDavid du Colombier 		/* fall through */
903801c5d3SDavid du Colombier 	case Proftime:
913801c5d3SDavid du Colombier 	proftime:		/* Subtract cycle counter on proc entry */
923801c5d3SDavid du Colombier 		_cycles((uvlong*)&t);
933801c5d3SDavid du Colombier 		p->time = p->time - t;
943801c5d3SDavid du Colombier 		break;
953801c5d3SDavid du Colombier 	case Profsample:
963801c5d3SDavid du Colombier 		p->time = p->time - _tos->clock;
973801c5d3SDavid du Colombier 		break;
983801c5d3SDavid du Colombier 	}
993e12c5d1SDavid du Colombier 	return arg;		/* disgusting linkage */
1003e12c5d1SDavid du Colombier }
1013e12c5d1SDavid du Colombier 
1023e12c5d1SDavid du Colombier ulong
_profout(void)1033e12c5d1SDavid du Colombier _profout(void)
1043e12c5d1SDavid du Colombier {
1053e12c5d1SDavid du Colombier 	Plink *p;
1063e12c5d1SDavid du Colombier 	ulong arg;
1073801c5d3SDavid du Colombier 	vlong t;
1083e12c5d1SDavid du Colombier 
1093e12c5d1SDavid du Colombier 	arg = _savearg();
1103801c5d3SDavid du Colombier 	p = _tos->prof.pp;
1113801c5d3SDavid du Colombier 	if (p == NULL || (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid))
1123801c5d3SDavid du Colombier 		return arg;	/* Not our process */
1133801c5d3SDavid du Colombier 	switch(_tos->prof.what){
1143801c5d3SDavid du Colombier 	case Profkernel:		/* Add proc cycles on proc entry */
1153801c5d3SDavid du Colombier 		p->time = p->time + _tos->pcycles;
1163801c5d3SDavid du Colombier 		goto proftime;
1173801c5d3SDavid du Colombier 	case Profuser:			/* Subtract kernel cycles on proc entry */
1183801c5d3SDavid du Colombier 		p->time = p->time - _tos->kcycles;
1193801c5d3SDavid du Colombier 		/* fall through */
1203801c5d3SDavid du Colombier 	case Proftime:
1213801c5d3SDavid du Colombier 	proftime:				/* Add cycle counter on proc entry */
1223801c5d3SDavid du Colombier 		_cycles((uvlong*)&t);
1233801c5d3SDavid du Colombier 		p->time = p->time + t;
1243801c5d3SDavid du Colombier 		break;
1253801c5d3SDavid du Colombier 	case Profsample:
1263801c5d3SDavid du Colombier 		p->time = p->time + _tos->clock;
1273801c5d3SDavid du Colombier 		break;
1283e12c5d1SDavid du Colombier 	}
1293801c5d3SDavid du Colombier 	_tos->prof.pp = p->old;
1303e12c5d1SDavid du Colombier 	return arg;
1313e12c5d1SDavid du Colombier }
1323e12c5d1SDavid du Colombier 
1333801c5d3SDavid du Colombier /* stdio may not be ready for us yet */
1343801c5d3SDavid du Colombier static void
err(char * fmt,...)1353801c5d3SDavid du Colombier err(char *fmt, ...)
1363801c5d3SDavid du Colombier {
13725fc6993SDavid du Colombier 	int fd;
1383801c5d3SDavid du Colombier 	va_list arg;
1393801c5d3SDavid du Colombier 	char buf[128];
1403801c5d3SDavid du Colombier 
1413801c5d3SDavid du Colombier 	if((fd = open("/dev/cons", OWRITE)) == -1)
1423801c5d3SDavid du Colombier 		return;
1433801c5d3SDavid du Colombier 	va_start(arg, fmt);
14425fc6993SDavid du Colombier 	/*
14525fc6993SDavid du Colombier 	 * C99 now requires *snprintf to return the number of characters
146*0ceffd3cSDavid du Colombier 	 * that *would* have been emitted, had there been room for them,
147*0ceffd3cSDavid du Colombier 	 * or a negative value on an `encoding error'.  Arrgh!
14825fc6993SDavid du Colombier 	 */
14925fc6993SDavid du Colombier 	vsnprintf(buf, sizeof buf, fmt, arg);
1503801c5d3SDavid du Colombier 	va_end(arg);
15125fc6993SDavid du Colombier 	write(fd, buf, strlen(buf));
1523801c5d3SDavid du Colombier 	close(fd);
1533801c5d3SDavid du Colombier }
1543801c5d3SDavid du Colombier 
1553e12c5d1SDavid du Colombier void
_profdump(void)1563e12c5d1SDavid du Colombier _profdump(void)
1573e12c5d1SDavid du Colombier {
1583e12c5d1SDavid du Colombier 	int f;
1593e12c5d1SDavid du Colombier 	long n;
1603e12c5d1SDavid du Colombier 	Plink *p;
1613e12c5d1SDavid du Colombier 	char *vp;
1623801c5d3SDavid du Colombier 	char filename[64];
1633e12c5d1SDavid du Colombier 
1643801c5d3SDavid du Colombier 	if (_tos->prof.what == 0)
1653801c5d3SDavid du Colombier 		return;	/* No profiling */
1663801c5d3SDavid du Colombier 	if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)
1673801c5d3SDavid du Colombier 		return;	/* Not our process */
1683801c5d3SDavid du Colombier 	if(perr)
1693801c5d3SDavid du Colombier 		err("%lud Prof errors\n", perr);
1703801c5d3SDavid du Colombier 	_tos->prof.pp = NULL;
1713801c5d3SDavid du Colombier 	if (_tos->prof.pid)
1723801c5d3SDavid du Colombier 		snprintf(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
1733801c5d3SDavid du Colombier 	else
1743801c5d3SDavid du Colombier 		snprintf(filename, sizeof filename - 1, "prof.out");
1753801c5d3SDavid du Colombier 	f = creat(filename, 0666);
1763e12c5d1SDavid du Colombier 	if(f < 0) {
1773801c5d3SDavid du Colombier 		err("%s: cannot create - %s\n", filename, strerror(errno));
1783e12c5d1SDavid du Colombier 		return;
1793e12c5d1SDavid du Colombier 	}
1803801c5d3SDavid du Colombier 	_tos->prof.pid = ~0;	/* make sure data gets dumped once */
1813801c5d3SDavid du Colombier 	switch(_tos->prof.what){
1823801c5d3SDavid du Colombier 	case Profkernel:
1833801c5d3SDavid du Colombier 		_cycles((uvlong*)&_tos->prof.first->time);
1843801c5d3SDavid du Colombier 		_tos->prof.first->time = _tos->prof.first->time + _tos->pcycles;
1853801c5d3SDavid du Colombier 		break;
1863801c5d3SDavid du Colombier 	case Profuser:
1873801c5d3SDavid du Colombier 		_cycles((uvlong*)&_tos->prof.first->time);
1883801c5d3SDavid du Colombier 		_tos->prof.first->time = _tos->prof.first->time - _tos->kcycles;
1893801c5d3SDavid du Colombier 		break;
1903801c5d3SDavid du Colombier 	case Proftime:
1913801c5d3SDavid du Colombier 		_cycles((uvlong*)&_tos->prof.first->time);
1923801c5d3SDavid du Colombier 		break;
1933801c5d3SDavid du Colombier 	case Profsample:
1943801c5d3SDavid du Colombier 		_tos->prof.first->time = _tos->clock;
1953801c5d3SDavid du Colombier 		break;
1963801c5d3SDavid du Colombier 	}
1973801c5d3SDavid du Colombier 	vp = (char*)_tos->prof.first;
1983801c5d3SDavid du Colombier 
1993801c5d3SDavid du Colombier 	for(p = _tos->prof.first; p <= _tos->prof.next; p++) {
2003801c5d3SDavid du Colombier 
2013e12c5d1SDavid du Colombier 		/*
2023e12c5d1SDavid du Colombier 		 * short down
2033e12c5d1SDavid du Colombier 		 */
2043e12c5d1SDavid du Colombier 		n = 0xffff;
2053e12c5d1SDavid du Colombier 		if(p->down)
2063801c5d3SDavid du Colombier 			n = p->down - _tos->prof.first;
2073e12c5d1SDavid du Colombier 		vp[0] = n>>8;
2083e12c5d1SDavid du Colombier 		vp[1] = n;
2093e12c5d1SDavid du Colombier 
2103e12c5d1SDavid du Colombier 		/*
2113e12c5d1SDavid du Colombier 		 * short right
2123e12c5d1SDavid du Colombier 		 */
2133e12c5d1SDavid du Colombier 		n = 0xffff;
2143e12c5d1SDavid du Colombier 		if(p->link)
2153801c5d3SDavid du Colombier 			n = p->link - _tos->prof.first;
2163e12c5d1SDavid du Colombier 		vp[2] = n>>8;
2173e12c5d1SDavid du Colombier 		vp[3] = n;
2183e12c5d1SDavid du Colombier 		vp += 4;
2193e12c5d1SDavid du Colombier 
2203e12c5d1SDavid du Colombier 		/*
2213e12c5d1SDavid du Colombier 		 * long pc
2223e12c5d1SDavid du Colombier 		 */
2233e12c5d1SDavid du Colombier 		n = p->pc;
2243e12c5d1SDavid du Colombier 		vp[0] = n>>24;
2253e12c5d1SDavid du Colombier 		vp[1] = n>>16;
2263e12c5d1SDavid du Colombier 		vp[2] = n>>8;
2273e12c5d1SDavid du Colombier 		vp[3] = n;
2283e12c5d1SDavid du Colombier 		vp += 4;
2293e12c5d1SDavid du Colombier 
2303e12c5d1SDavid du Colombier 		/*
2313e12c5d1SDavid du Colombier 		 * long count
2323e12c5d1SDavid du Colombier 		 */
2333e12c5d1SDavid du Colombier 		n = p->count;
2343e12c5d1SDavid du Colombier 		vp[0] = n>>24;
2353e12c5d1SDavid du Colombier 		vp[1] = n>>16;
2363e12c5d1SDavid du Colombier 		vp[2] = n>>8;
2373e12c5d1SDavid du Colombier 		vp[3] = n;
2383e12c5d1SDavid du Colombier 		vp += 4;
2393e12c5d1SDavid du Colombier 
2403e12c5d1SDavid du Colombier 		/*
2413801c5d3SDavid du Colombier 		 * vlong time
2423e12c5d1SDavid du Colombier 		 */
2433801c5d3SDavid du Colombier 		if (havecycles){
2443801c5d3SDavid du Colombier 			n = (vlong)(p->time / (vlong)khz);
2453801c5d3SDavid du Colombier 		}else
2463801c5d3SDavid du Colombier 			n = p->time;
2473801c5d3SDavid du Colombier 
2483e12c5d1SDavid du Colombier 		vp[0] = n>>24;
2493e12c5d1SDavid du Colombier 		vp[1] = n>>16;
2503e12c5d1SDavid du Colombier 		vp[2] = n>>8;
2513e12c5d1SDavid du Colombier 		vp[3] = n;
2523e12c5d1SDavid du Colombier 		vp += 4;
2533e12c5d1SDavid du Colombier 	}
2543801c5d3SDavid du Colombier 	write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first);
2553e12c5d1SDavid du Colombier 	close(f);
2563801c5d3SDavid du Colombier 
2573801c5d3SDavid du Colombier }
2583801c5d3SDavid du Colombier 
2593801c5d3SDavid du Colombier void
_profinit(int entries,int what)2603801c5d3SDavid du Colombier _profinit(int entries, int what)
2613801c5d3SDavid du Colombier {
2623801c5d3SDavid du Colombier 	if (_tos->prof.what == 0)
2633801c5d3SDavid du Colombier 		return;	/* Profiling not linked in */
2643801c5d3SDavid du Colombier 	_tos->prof.pp = NULL;
2653801c5d3SDavid du Colombier 	_tos->prof.first = calloc(entries*sizeof(Plink),1);
2663801c5d3SDavid du Colombier 	_tos->prof.last = _tos->prof.first + entries;
2673801c5d3SDavid du Colombier 	_tos->prof.next = _tos->prof.first;
2683801c5d3SDavid du Colombier 	_tos->prof.pid = _tos->pid;
2693801c5d3SDavid du Colombier 	_tos->prof.what = what;
2703801c5d3SDavid du Colombier 	_tos->clock = 1;
2713e12c5d1SDavid du Colombier }
2723e12c5d1SDavid du Colombier 
2733e12c5d1SDavid du Colombier void
_profmain(void)2743e12c5d1SDavid du Colombier _profmain(void)
2753e12c5d1SDavid du Colombier {
2763e12c5d1SDavid du Colombier 	char ename[50];
2773e12c5d1SDavid du Colombier 	int n, f;
2783e12c5d1SDavid du Colombier 
2793801c5d3SDavid du Colombier 	n = 2000;
2803801c5d3SDavid du Colombier 	if (_tos->cyclefreq != 0LL){
2813801c5d3SDavid du Colombier 		khz = _tos->cyclefreq / 1000;	/* Report times in milliseconds */
2823801c5d3SDavid du Colombier 		havecycles = 1;
2833801c5d3SDavid du Colombier 	}
2843801c5d3SDavid du Colombier 	f = open("/env/profsize", OREAD);
2853e12c5d1SDavid du Colombier 	if(f >= 0) {
2863e12c5d1SDavid du Colombier 		memset(ename, 0, sizeof(ename));
2873801c5d3SDavid du Colombier 		read(f, ename, sizeof(ename)-1);
2883801c5d3SDavid du Colombier 		close(f);
2893e12c5d1SDavid du Colombier 		n = atol(ename);
2903e12c5d1SDavid du Colombier 	}
2913801c5d3SDavid du Colombier 	_tos->prof.what = Profuser;
2923801c5d3SDavid du Colombier 	f = open("/env/proftype", OREAD);
2933801c5d3SDavid du Colombier 	if(f >= 0) {
2943801c5d3SDavid du Colombier 		memset(ename, 0, sizeof(ename));
2953801c5d3SDavid du Colombier 		read(f, ename, sizeof(ename)-1);
2963801c5d3SDavid du Colombier 		close(f);
2973801c5d3SDavid du Colombier 		if (strcmp(ename, "user") == 0)
2983801c5d3SDavid du Colombier 			_tos->prof.what = Profuser;
2993801c5d3SDavid du Colombier 		else if (strcmp(ename, "kernel") == 0)
3003801c5d3SDavid du Colombier 			_tos->prof.what = Profkernel;
3013801c5d3SDavid du Colombier 		else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0)
3023801c5d3SDavid du Colombier 			_tos->prof.what = Proftime;
3033801c5d3SDavid du Colombier 		else if (strcmp(ename, "sample") == 0)
3043801c5d3SDavid du Colombier 			_tos->prof.what = Profsample;
3053e12c5d1SDavid du Colombier 	}
3063801c5d3SDavid du Colombier 	_tos->prof.first = sbrk(n*sizeof(Plink));
3073801c5d3SDavid du Colombier 	_tos->prof.last = sbrk(0);
3083801c5d3SDavid du Colombier 	_tos->prof.next = _tos->prof.first;
3093801c5d3SDavid du Colombier 	_tos->prof.pp = NULL;
3103801c5d3SDavid du Colombier 	_tos->prof.pid = _tos->pid;
3113801c5d3SDavid du Colombier 	atexit(_profdump);
3123801c5d3SDavid du Colombier 	_tos->clock = 1;
3133801c5d3SDavid du Colombier }
3143801c5d3SDavid du Colombier 
prof(void (* fn)(void *),void * arg,int entries,int what)3153801c5d3SDavid du Colombier void prof(void (*fn)(void*), void *arg, int entries, int what)
3163801c5d3SDavid du Colombier {
3173801c5d3SDavid du Colombier 	_profinit(entries, what);
3183801c5d3SDavid du Colombier 	_tos->prof.pp = _tos->prof.next;
3193801c5d3SDavid du Colombier 	fn(arg);
3203801c5d3SDavid du Colombier 	_profdump();
3213801c5d3SDavid du Colombier }
3223801c5d3SDavid du Colombier 
3233801c5d3SDavid du Colombier #pragma profile on
3243801c5d3SDavid du Colombier 
325