xref: /plan9/sys/src/libc/port/profile.c (revision 8c04328423b2322742bda0a2bec3fb1145ef1037)
13e12c5d1SDavid du Colombier #include	<u.h>
23e12c5d1SDavid du Colombier #include	<libc.h>
3e288d156SDavid du Colombier #include	<tos.h>
43e12c5d1SDavid du Colombier 
53e12c5d1SDavid du Colombier extern	long	_callpc(void**);
63e12c5d1SDavid du Colombier extern	long	_savearg(void);
7e288d156SDavid du Colombier 
8208510e1SDavid du Colombier static	ulong	khz;
9208510e1SDavid du Colombier static	ulong	perr;
10208510e1SDavid du Colombier static	int	havecycles;
113e12c5d1SDavid du Colombier 
123e12c5d1SDavid du Colombier typedef	struct	Plink	Plink;
133e12c5d1SDavid du Colombier struct	Plink
143e12c5d1SDavid du Colombier {
15e288d156SDavid du Colombier 	Plink	*old;
163e12c5d1SDavid du Colombier 	Plink	*down;
173e12c5d1SDavid du Colombier 	Plink	*link;
183e12c5d1SDavid du Colombier 	long	pc;
193e12c5d1SDavid du Colombier 	long	count;
20e288d156SDavid du Colombier 	vlong time;
213e12c5d1SDavid du Colombier };
223e12c5d1SDavid du Colombier 
23e288d156SDavid du Colombier #pragma profile off
243e12c5d1SDavid du Colombier 
253e12c5d1SDavid du Colombier ulong
_profin(void)263e12c5d1SDavid du Colombier _profin(void)
273e12c5d1SDavid du Colombier {
283e12c5d1SDavid du Colombier 	void *dummy;
293e12c5d1SDavid du Colombier 	long pc;
303e12c5d1SDavid du Colombier 	Plink *pp, *p;
313e12c5d1SDavid du Colombier 	ulong arg;
32e288d156SDavid du Colombier 	vlong t;
333e12c5d1SDavid du Colombier 
343e12c5d1SDavid du Colombier 	arg = _savearg();
353e12c5d1SDavid du Colombier 	pc = _callpc(&dummy);
36e288d156SDavid du Colombier 	pp = _tos->prof.pp;
37e288d156SDavid du Colombier 	if(pp == 0 || (_tos->prof.pid && _tos->pid != _tos->prof.pid))
383e12c5d1SDavid du Colombier 		return arg;
393e12c5d1SDavid du Colombier 
403e12c5d1SDavid du Colombier 	for(p=pp->down; p; p=p->link)
413e12c5d1SDavid du Colombier 		if(p->pc == pc)
423e12c5d1SDavid du Colombier 			goto out;
43e288d156SDavid du Colombier 	p = _tos->prof.next + 1;
44e288d156SDavid du Colombier 	if(p >= _tos->prof.last) {
45e288d156SDavid du Colombier 		_tos->prof.pp = 0;
467e2bd6dcSDavid du Colombier 		perr++;
473e12c5d1SDavid du Colombier 		return arg;
483e12c5d1SDavid du Colombier 	}
49e288d156SDavid du Colombier 	_tos->prof.next = p;
503e12c5d1SDavid du Colombier 	p->link = pp->down;
513e12c5d1SDavid du Colombier 	pp->down = p;
523e12c5d1SDavid du Colombier 	p->pc = pc;
533e12c5d1SDavid du Colombier 	p->old = pp;
543e12c5d1SDavid du Colombier 	p->down = 0;
553e12c5d1SDavid du Colombier 	p->count = 0;
56e288d156SDavid du Colombier 	p->time = 0LL;
573e12c5d1SDavid du Colombier 
583e12c5d1SDavid du Colombier out:
59e288d156SDavid du Colombier 	_tos->prof.pp = p;
603e12c5d1SDavid du Colombier 	p->count++;
61e288d156SDavid du Colombier 	switch(_tos->prof.what){
62e288d156SDavid du Colombier 	case Profkernel:
63e7d29567SDavid du Colombier 		p->time = p->time - _tos->pcycles;
64e288d156SDavid du Colombier 		goto proftime;
65e288d156SDavid du Colombier 	case Profuser:
66e288d156SDavid du Colombier 		/* Add kernel cycles on proc entry */
67e7d29567SDavid du Colombier 		p->time = p->time + _tos->kcycles;
68e288d156SDavid du Colombier 		/* fall through */
69e288d156SDavid du Colombier 	case Proftime:
70e288d156SDavid du Colombier 	proftime:		/* Subtract cycle counter on proc entry */
71e288d156SDavid du Colombier 		cycles((uvlong*)&t);
72e7d29567SDavid du Colombier 		p->time = p->time - t;
73e288d156SDavid du Colombier 		break;
74e288d156SDavid du Colombier 	case Profsample:
75e7d29567SDavid du Colombier 		p->time = p->time - _tos->clock;
76e288d156SDavid du Colombier 		break;
77e288d156SDavid du Colombier 	}
783e12c5d1SDavid du Colombier 	return arg;		/* disgusting linkage */
793e12c5d1SDavid du Colombier }
803e12c5d1SDavid du Colombier 
813e12c5d1SDavid du Colombier ulong
_profout(void)823e12c5d1SDavid du Colombier _profout(void)
833e12c5d1SDavid du Colombier {
843e12c5d1SDavid du Colombier 	Plink *p;
853e12c5d1SDavid du Colombier 	ulong arg;
86e288d156SDavid du Colombier 	vlong t;
873e12c5d1SDavid du Colombier 
883e12c5d1SDavid du Colombier 	arg = _savearg();
89e288d156SDavid du Colombier 	p = _tos->prof.pp;
90e288d156SDavid du Colombier 	if (p == nil || (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid))
91e288d156SDavid du Colombier 		return arg;	/* Not our process */
92e288d156SDavid du Colombier 	switch(_tos->prof.what){
93e288d156SDavid du Colombier 	case Profkernel:		/* Add proc cycles on proc entry */
94e7d29567SDavid du Colombier 		p->time = p->time + _tos->pcycles;
95e288d156SDavid du Colombier 		goto proftime;
96e288d156SDavid du Colombier 	case Profuser:			/* Subtract kernel cycles on proc entry */
97e7d29567SDavid du Colombier 		p->time = p->time - _tos->kcycles;
98e288d156SDavid du Colombier 		/* fall through */
99e288d156SDavid du Colombier 	case Proftime:
100e288d156SDavid du Colombier 	proftime:				/* Add cycle counter on proc entry */
101e288d156SDavid du Colombier 		cycles((uvlong*)&t);
102e7d29567SDavid du Colombier 		p->time = p->time + t;
103e288d156SDavid du Colombier 		break;
104e288d156SDavid du Colombier 	case Profsample:
105e7d29567SDavid du Colombier 		p->time = p->time + _tos->clock;
106e288d156SDavid du Colombier 		break;
1073e12c5d1SDavid du Colombier 	}
108e288d156SDavid du Colombier 	_tos->prof.pp = p->old;
1093e12c5d1SDavid du Colombier 	return arg;
1103e12c5d1SDavid du Colombier }
1113e12c5d1SDavid du Colombier 
1123e12c5d1SDavid du Colombier void
_profdump(void)1133e12c5d1SDavid du Colombier _profdump(void)
1143e12c5d1SDavid du Colombier {
1153e12c5d1SDavid du Colombier 	int f;
1163e12c5d1SDavid du Colombier 	long n;
1173e12c5d1SDavid du Colombier 	Plink *p;
1183e12c5d1SDavid du Colombier 	char *vp;
119e288d156SDavid du Colombier 	char filename[64];
1203e12c5d1SDavid du Colombier 
121e288d156SDavid du Colombier 	if (_tos->prof.what == 0)
122e288d156SDavid du Colombier 		return;	/* No profiling */
123e288d156SDavid du Colombier 	if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)
124e288d156SDavid du Colombier 		return;	/* Not our process */
1257e2bd6dcSDavid du Colombier 	if(perr)
1267e2bd6dcSDavid du Colombier 		fprint(2, "%lud Prof errors\n", perr);
127e288d156SDavid du Colombier 	_tos->prof.pp = nil;
128e288d156SDavid du Colombier 	if (_tos->prof.pid)
129e288d156SDavid du Colombier 		snprint(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
130e288d156SDavid du Colombier 	else
131e288d156SDavid du Colombier 		snprint(filename, sizeof filename - 1, "prof.out");
132e288d156SDavid du Colombier 	f = create(filename, 1, 0666);
1333e12c5d1SDavid du Colombier 	if(f < 0) {
1343e12c5d1SDavid du Colombier 		perror("create prof.out");
1353e12c5d1SDavid du Colombier 		return;
1363e12c5d1SDavid du Colombier 	}
137e288d156SDavid du Colombier 	_tos->prof.pid = ~0;	/* make sure data gets dumped once */
138e288d156SDavid du Colombier 	switch(_tos->prof.what){
139e288d156SDavid du Colombier 	case Profkernel:
140e288d156SDavid du Colombier 		cycles((uvlong*)&_tos->prof.first->time);
141e7d29567SDavid du Colombier 		_tos->prof.first->time = _tos->prof.first->time + _tos->pcycles;
142e288d156SDavid du Colombier 		break;
143e288d156SDavid du Colombier 	case Profuser:
144e288d156SDavid du Colombier 		cycles((uvlong*)&_tos->prof.first->time);
1453801c5d3SDavid du Colombier 		_tos->prof.first->time = _tos->prof.first->time - _tos->kcycles;
146e288d156SDavid du Colombier 		break;
147e288d156SDavid du Colombier 	case Proftime:
148e288d156SDavid du Colombier 		cycles((uvlong*)&_tos->prof.first->time);
149e288d156SDavid du Colombier 		break;
150e288d156SDavid du Colombier 	case Profsample:
151e288d156SDavid du Colombier 		_tos->prof.first->time = _tos->clock;
152e288d156SDavid du Colombier 		break;
153e288d156SDavid du Colombier 	}
154e288d156SDavid du Colombier 	vp = (char*)_tos->prof.first;
155e288d156SDavid du Colombier 
156e288d156SDavid du Colombier 	for(p = _tos->prof.first; p <= _tos->prof.next; p++) {
157e288d156SDavid du Colombier 
1583e12c5d1SDavid du Colombier 		/*
1593e12c5d1SDavid du Colombier 		 * short down
1603e12c5d1SDavid du Colombier 		 */
1613e12c5d1SDavid du Colombier 		n = 0xffff;
1623e12c5d1SDavid du Colombier 		if(p->down)
163e288d156SDavid du Colombier 			n = p->down - _tos->prof.first;
1643e12c5d1SDavid du Colombier 		vp[0] = n>>8;
1653e12c5d1SDavid du Colombier 		vp[1] = n;
1663e12c5d1SDavid du Colombier 
1673e12c5d1SDavid du Colombier 		/*
1683e12c5d1SDavid du Colombier 		 * short right
1693e12c5d1SDavid du Colombier 		 */
1703e12c5d1SDavid du Colombier 		n = 0xffff;
1713e12c5d1SDavid du Colombier 		if(p->link)
172e288d156SDavid du Colombier 			n = p->link - _tos->prof.first;
1733e12c5d1SDavid du Colombier 		vp[2] = n>>8;
1743e12c5d1SDavid du Colombier 		vp[3] = n;
1753e12c5d1SDavid du Colombier 		vp += 4;
1763e12c5d1SDavid du Colombier 
1773e12c5d1SDavid du Colombier 		/*
1783e12c5d1SDavid du Colombier 		 * long pc
1793e12c5d1SDavid du Colombier 		 */
1803e12c5d1SDavid du Colombier 		n = p->pc;
1813e12c5d1SDavid du Colombier 		vp[0] = n>>24;
1823e12c5d1SDavid du Colombier 		vp[1] = n>>16;
1833e12c5d1SDavid du Colombier 		vp[2] = n>>8;
1843e12c5d1SDavid du Colombier 		vp[3] = n;
1853e12c5d1SDavid du Colombier 		vp += 4;
1863e12c5d1SDavid du Colombier 
1873e12c5d1SDavid du Colombier 		/*
1883e12c5d1SDavid du Colombier 		 * long count
1893e12c5d1SDavid du Colombier 		 */
1903e12c5d1SDavid du Colombier 		n = p->count;
1913e12c5d1SDavid du Colombier 		vp[0] = n>>24;
1923e12c5d1SDavid du Colombier 		vp[1] = n>>16;
1933e12c5d1SDavid du Colombier 		vp[2] = n>>8;
1943e12c5d1SDavid du Colombier 		vp[3] = n;
1953e12c5d1SDavid du Colombier 		vp += 4;
1963e12c5d1SDavid du Colombier 
1973e12c5d1SDavid du Colombier 		/*
198e288d156SDavid du Colombier 		 * vlong time
1993e12c5d1SDavid du Colombier 		 */
200e288d156SDavid du Colombier 		if (havecycles){
201e288d156SDavid du Colombier 			n = (vlong)(p->time / (vlong)khz);
202e288d156SDavid du Colombier 		}else
203e288d156SDavid du Colombier 			n = p->time;
204e288d156SDavid du Colombier 
2053e12c5d1SDavid du Colombier 		vp[0] = n>>24;
2063e12c5d1SDavid du Colombier 		vp[1] = n>>16;
2073e12c5d1SDavid du Colombier 		vp[2] = n>>8;
2083e12c5d1SDavid du Colombier 		vp[3] = n;
2093e12c5d1SDavid du Colombier 		vp += 4;
2103e12c5d1SDavid du Colombier 	}
211e288d156SDavid du Colombier 	write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first);
2123e12c5d1SDavid du Colombier 	close(f);
2133e12c5d1SDavid du Colombier }
2143e12c5d1SDavid du Colombier 
2153e12c5d1SDavid du Colombier void
_profinit(int entries,int what)216e288d156SDavid du Colombier _profinit(int entries, int what)
217e288d156SDavid du Colombier {
218e288d156SDavid du Colombier 	if (_tos->prof.what == 0)
219e288d156SDavid du Colombier 		return;	/* Profiling not linked in */
220e288d156SDavid du Colombier 	_tos->prof.pp = nil;
221e288d156SDavid du Colombier 	_tos->prof.first = mallocz(entries*sizeof(Plink),1);
222e288d156SDavid du Colombier 	_tos->prof.last = _tos->prof.first + entries;
223e288d156SDavid du Colombier 	_tos->prof.next = _tos->prof.first;
224e288d156SDavid du Colombier 	_tos->prof.pid = _tos->pid;
225e288d156SDavid du Colombier 	_tos->prof.what = what;
226e288d156SDavid du Colombier 	_tos->clock = 1;
227e288d156SDavid du Colombier }
228e288d156SDavid du Colombier 
229e288d156SDavid du Colombier void
_profmain(void)2303e12c5d1SDavid du Colombier _profmain(void)
2313e12c5d1SDavid du Colombier {
2323e12c5d1SDavid du Colombier 	char ename[50];
2333e12c5d1SDavid du Colombier 	int n, f;
2343e12c5d1SDavid du Colombier 
2353e12c5d1SDavid du Colombier 	n = 2000;
236e288d156SDavid du Colombier 	if (_tos->cyclefreq != 0LL){
237e288d156SDavid du Colombier 		khz = _tos->cyclefreq / 1000;	/* Report times in milliseconds */
238e288d156SDavid du Colombier 		havecycles = 1;
239e288d156SDavid du Colombier 	}
2403e12c5d1SDavid du Colombier 	f = open("/env/profsize", OREAD);
2413e12c5d1SDavid du Colombier 	if(f >= 0) {
2423e12c5d1SDavid du Colombier 		memset(ename, 0, sizeof(ename));
2433e12c5d1SDavid du Colombier 		read(f, ename, sizeof(ename)-1);
2443e12c5d1SDavid du Colombier 		close(f);
2453e12c5d1SDavid du Colombier 		n = atol(ename);
2463e12c5d1SDavid du Colombier 	}
247e288d156SDavid du Colombier 	_tos->prof.what = Profuser;
248e288d156SDavid du Colombier 	f = open("/env/proftype", OREAD);
249e288d156SDavid du Colombier 	if(f >= 0) {
250e288d156SDavid du Colombier 		memset(ename, 0, sizeof(ename));
251e288d156SDavid du Colombier 		read(f, ename, sizeof(ename)-1);
252e288d156SDavid du Colombier 		close(f);
253e288d156SDavid du Colombier 		if (strcmp(ename, "user") == 0)
254e288d156SDavid du Colombier 			_tos->prof.what = Profuser;
255e288d156SDavid du Colombier 		else if (strcmp(ename, "kernel") == 0)
256e288d156SDavid du Colombier 			_tos->prof.what = Profkernel;
257e288d156SDavid du Colombier 		else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0)
258e288d156SDavid du Colombier 			_tos->prof.what = Proftime;
259e288d156SDavid du Colombier 		else if (strcmp(ename, "sample") == 0)
260e288d156SDavid du Colombier 			_tos->prof.what = Profsample;
2613e12c5d1SDavid du Colombier 	}
262e288d156SDavid du Colombier 	_tos->prof.first = sbrk(n*sizeof(Plink));
263e288d156SDavid du Colombier 	_tos->prof.last = sbrk(0);
264e288d156SDavid du Colombier 	_tos->prof.next = _tos->prof.first;
265e288d156SDavid du Colombier 	_tos->prof.pp = nil;
266e288d156SDavid du Colombier 	_tos->prof.pid = _tos->pid;
267e288d156SDavid du Colombier 	atexit(_profdump);
268e288d156SDavid du Colombier 	_tos->clock = 1;
269e288d156SDavid du Colombier }
270e288d156SDavid du Colombier 
271*8c043284SDavid du Colombier void
prof(void (* fn)(void *),void * arg,int entries,int what)272*8c043284SDavid du Colombier prof(void (*fn)(void*), void *arg, int entries, int what)
273e288d156SDavid du Colombier {
274e288d156SDavid du Colombier 	_profinit(entries, what);
275e288d156SDavid du Colombier 	_tos->prof.pp = _tos->prof.next;
276e288d156SDavid du Colombier 	fn(arg);
277e288d156SDavid du Colombier 	_profdump();
278e288d156SDavid du Colombier }
279e288d156SDavid du Colombier 
280e288d156SDavid du Colombier #pragma profile on
281e288d156SDavid du Colombier 
282