xref: /plan9/sys/src/libc/port/profile.c (revision 7e2bd6dca14c6ae5d41e1a78002cb5e6c8cda9e0)
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 
8e288d156SDavid du Colombier ulong	khz;
9*7e2bd6dcSDavid du Colombier ulong	perr;
10e288d156SDavid du Colombier uvlong	cyclefreq;
11e288d156SDavid du Colombier int		havecycles;
123e12c5d1SDavid du Colombier 
133e12c5d1SDavid du Colombier typedef	struct	Plink	Plink;
143e12c5d1SDavid du Colombier struct	Plink
153e12c5d1SDavid du Colombier {
16e288d156SDavid du Colombier 	Plink	*old;
173e12c5d1SDavid du Colombier 	Plink	*down;
183e12c5d1SDavid du Colombier 	Plink	*link;
193e12c5d1SDavid du Colombier 	long	pc;
203e12c5d1SDavid du Colombier 	long	count;
21e288d156SDavid du Colombier 	vlong time;
223e12c5d1SDavid du Colombier };
233e12c5d1SDavid du Colombier 
24e288d156SDavid du Colombier #pragma profile off
253e12c5d1SDavid du Colombier 
263e12c5d1SDavid du Colombier ulong
273e12c5d1SDavid du Colombier _profin(void)
283e12c5d1SDavid du Colombier {
293e12c5d1SDavid du Colombier 	void *dummy;
303e12c5d1SDavid du Colombier 	long pc;
313e12c5d1SDavid du Colombier 	Plink *pp, *p;
323e12c5d1SDavid du Colombier 	ulong arg;
33e288d156SDavid du Colombier 	vlong t;
343e12c5d1SDavid du Colombier 
353e12c5d1SDavid du Colombier 	arg = _savearg();
363e12c5d1SDavid du Colombier 	pc = _callpc(&dummy);
37e288d156SDavid du Colombier 	pp = _tos->prof.pp;
38e288d156SDavid du Colombier 	if(pp == 0 || (_tos->prof.pid && _tos->pid != _tos->prof.pid))
393e12c5d1SDavid du Colombier 		return arg;
403e12c5d1SDavid du Colombier 
413e12c5d1SDavid du Colombier 	for(p=pp->down; p; p=p->link)
423e12c5d1SDavid du Colombier 		if(p->pc == pc)
433e12c5d1SDavid du Colombier 			goto out;
44e288d156SDavid du Colombier 	p = _tos->prof.next + 1;
45e288d156SDavid du Colombier 	if(p >= _tos->prof.last) {
46e288d156SDavid du Colombier 		_tos->prof.pp = 0;
47*7e2bd6dcSDavid du Colombier 		perr++;
483e12c5d1SDavid du Colombier 		return arg;
493e12c5d1SDavid du Colombier 	}
50e288d156SDavid du Colombier 	_tos->prof.next = p;
513e12c5d1SDavid du Colombier 	p->link = pp->down;
523e12c5d1SDavid du Colombier 	pp->down = p;
533e12c5d1SDavid du Colombier 	p->pc = pc;
543e12c5d1SDavid du Colombier 	p->old = pp;
553e12c5d1SDavid du Colombier 	p->down = 0;
563e12c5d1SDavid du Colombier 	p->count = 0;
57e288d156SDavid du Colombier 	p->time = 0LL;
583e12c5d1SDavid du Colombier 
593e12c5d1SDavid du Colombier out:
60e288d156SDavid du Colombier 	_tos->prof.pp = p;
613e12c5d1SDavid du Colombier 	p->count++;
62e288d156SDavid du Colombier 	switch(_tos->prof.what){
63e288d156SDavid du Colombier 	case Profkernel:
64e7d29567SDavid du Colombier 		p->time = p->time - _tos->pcycles;
65e288d156SDavid du Colombier 		goto proftime;
66e288d156SDavid du Colombier 	case Profuser:
67e288d156SDavid du Colombier 		/* Add kernel cycles on proc entry */
68e7d29567SDavid du Colombier 		p->time = p->time + _tos->kcycles;
69e288d156SDavid du Colombier 		/* fall through */
70e288d156SDavid du Colombier 	case Proftime:
71e288d156SDavid du Colombier 	proftime:		/* Subtract cycle counter on proc entry */
72e288d156SDavid du Colombier 		cycles((uvlong*)&t);
73e7d29567SDavid du Colombier 		p->time = p->time - t;
74e288d156SDavid du Colombier 		break;
75e288d156SDavid du Colombier 	case Profsample:
76e7d29567SDavid du Colombier 		p->time = p->time - _tos->clock;
77e288d156SDavid du Colombier 		break;
78e288d156SDavid du Colombier 	}
793e12c5d1SDavid du Colombier 	return arg;		/* disgusting linkage */
803e12c5d1SDavid du Colombier }
813e12c5d1SDavid du Colombier 
823e12c5d1SDavid du Colombier ulong
833e12c5d1SDavid du Colombier _profout(void)
843e12c5d1SDavid du Colombier {
853e12c5d1SDavid du Colombier 	Plink *p;
863e12c5d1SDavid du Colombier 	ulong arg;
87e288d156SDavid du Colombier 	vlong t;
883e12c5d1SDavid du Colombier 
893e12c5d1SDavid du Colombier 	arg = _savearg();
90e288d156SDavid du Colombier 	p = _tos->prof.pp;
91e288d156SDavid du Colombier 	if (p == nil || (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid))
92e288d156SDavid du Colombier 		return arg;	/* Not our process */
93e288d156SDavid du Colombier 	switch(_tos->prof.what){
94e288d156SDavid du Colombier 	case Profkernel:		/* Add proc cycles on proc entry */
95e7d29567SDavid du Colombier 		p->time = p->time + _tos->pcycles;
96e288d156SDavid du Colombier 		goto proftime;
97e288d156SDavid du Colombier 	case Profuser:			/* Subtract kernel cycles on proc entry */
98e7d29567SDavid du Colombier 		p->time = p->time - _tos->kcycles;
99e288d156SDavid du Colombier 		/* fall through */
100e288d156SDavid du Colombier 	case Proftime:
101e288d156SDavid du Colombier 	proftime:				/* Add cycle counter on proc entry */
102e288d156SDavid du Colombier 		cycles((uvlong*)&t);
103e7d29567SDavid du Colombier 		p->time = p->time + t;
104e288d156SDavid du Colombier 		break;
105e288d156SDavid du Colombier 	case Profsample:
106e7d29567SDavid du Colombier 		p->time = p->time + _tos->clock;
107e288d156SDavid du Colombier 		break;
1083e12c5d1SDavid du Colombier 	}
109e288d156SDavid du Colombier 	_tos->prof.pp = p->old;
1103e12c5d1SDavid du Colombier 	return arg;
1113e12c5d1SDavid du Colombier }
1123e12c5d1SDavid du Colombier 
1133e12c5d1SDavid du Colombier void
1143e12c5d1SDavid du Colombier _profdump(void)
1153e12c5d1SDavid du Colombier {
1163e12c5d1SDavid du Colombier 	int f;
1173e12c5d1SDavid du Colombier 	long n;
1183e12c5d1SDavid du Colombier 	Plink *p;
1193e12c5d1SDavid du Colombier 	char *vp;
120e288d156SDavid du Colombier 	char filename[64];
1213e12c5d1SDavid du Colombier 
122e288d156SDavid du Colombier 	if (_tos->prof.what == 0)
123e288d156SDavid du Colombier 		return;	/* No profiling */
124e288d156SDavid du Colombier 	if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)
125e288d156SDavid du Colombier 		return;	/* Not our process */
126*7e2bd6dcSDavid du Colombier 	if(perr)
127*7e2bd6dcSDavid du Colombier 		fprint(2, "%lud Prof errors\n", perr);
128e288d156SDavid du Colombier 	_tos->prof.pp = nil;
129e288d156SDavid du Colombier 	if (_tos->prof.pid)
130e288d156SDavid du Colombier 		snprint(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
131e288d156SDavid du Colombier 	else
132e288d156SDavid du Colombier 		snprint(filename, sizeof filename - 1, "prof.out");
133e288d156SDavid du Colombier 	f = create(filename, 1, 0666);
1343e12c5d1SDavid du Colombier 	if(f < 0) {
1353e12c5d1SDavid du Colombier 		perror("create prof.out");
1363e12c5d1SDavid du Colombier 		return;
1373e12c5d1SDavid du Colombier 	}
138e288d156SDavid du Colombier 	_tos->prof.pid = ~0;	/* make sure data gets dumped once */
139e288d156SDavid du Colombier 	switch(_tos->prof.what){
140e288d156SDavid du Colombier 	case Profkernel:
141e288d156SDavid du Colombier 		cycles((uvlong*)&_tos->prof.first->time);
142e7d29567SDavid du Colombier 		_tos->prof.first->time = _tos->prof.first->time + _tos->pcycles;
143e288d156SDavid du Colombier 		break;
144e288d156SDavid du Colombier 	case Profuser:
145e288d156SDavid du Colombier 		cycles((uvlong*)&_tos->prof.first->time);
146e7d29567SDavid du Colombier 		_tos->prof.first->time = _tos->prof.first->time + _tos->kcycles;
147e288d156SDavid du Colombier 		break;
148e288d156SDavid du Colombier 	case Proftime:
149e288d156SDavid du Colombier 		cycles((uvlong*)&_tos->prof.first->time);
150e288d156SDavid du Colombier 		break;
151e288d156SDavid du Colombier 	case Profsample:
152e288d156SDavid du Colombier 		_tos->prof.first->time = _tos->clock;
153e288d156SDavid du Colombier 		break;
154e288d156SDavid du Colombier 	}
155e288d156SDavid du Colombier 	vp = (char*)_tos->prof.first;
156e288d156SDavid du Colombier 
157e288d156SDavid du Colombier 	for(p = _tos->prof.first; p <= _tos->prof.next; p++) {
158e288d156SDavid du Colombier 
1593e12c5d1SDavid du Colombier 		/*
1603e12c5d1SDavid du Colombier 		 * short down
1613e12c5d1SDavid du Colombier 		 */
1623e12c5d1SDavid du Colombier 		n = 0xffff;
1633e12c5d1SDavid du Colombier 		if(p->down)
164e288d156SDavid du Colombier 			n = p->down - _tos->prof.first;
1653e12c5d1SDavid du Colombier 		vp[0] = n>>8;
1663e12c5d1SDavid du Colombier 		vp[1] = n;
1673e12c5d1SDavid du Colombier 
1683e12c5d1SDavid du Colombier 		/*
1693e12c5d1SDavid du Colombier 		 * short right
1703e12c5d1SDavid du Colombier 		 */
1713e12c5d1SDavid du Colombier 		n = 0xffff;
1723e12c5d1SDavid du Colombier 		if(p->link)
173e288d156SDavid du Colombier 			n = p->link - _tos->prof.first;
1743e12c5d1SDavid du Colombier 		vp[2] = n>>8;
1753e12c5d1SDavid du Colombier 		vp[3] = n;
1763e12c5d1SDavid du Colombier 		vp += 4;
1773e12c5d1SDavid du Colombier 
1783e12c5d1SDavid du Colombier 		/*
1793e12c5d1SDavid du Colombier 		 * long pc
1803e12c5d1SDavid du Colombier 		 */
1813e12c5d1SDavid du Colombier 		n = p->pc;
1823e12c5d1SDavid du Colombier 		vp[0] = n>>24;
1833e12c5d1SDavid du Colombier 		vp[1] = n>>16;
1843e12c5d1SDavid du Colombier 		vp[2] = n>>8;
1853e12c5d1SDavid du Colombier 		vp[3] = n;
1863e12c5d1SDavid du Colombier 		vp += 4;
1873e12c5d1SDavid du Colombier 
1883e12c5d1SDavid du Colombier 		/*
1893e12c5d1SDavid du Colombier 		 * long count
1903e12c5d1SDavid du Colombier 		 */
1913e12c5d1SDavid du Colombier 		n = p->count;
1923e12c5d1SDavid du Colombier 		vp[0] = n>>24;
1933e12c5d1SDavid du Colombier 		vp[1] = n>>16;
1943e12c5d1SDavid du Colombier 		vp[2] = n>>8;
1953e12c5d1SDavid du Colombier 		vp[3] = n;
1963e12c5d1SDavid du Colombier 		vp += 4;
1973e12c5d1SDavid du Colombier 
1983e12c5d1SDavid du Colombier 		/*
199e288d156SDavid du Colombier 		 * vlong time
2003e12c5d1SDavid du Colombier 		 */
201e288d156SDavid du Colombier 		if (havecycles){
202e288d156SDavid du Colombier 			n = (vlong)(p->time / (vlong)khz);
203e288d156SDavid du Colombier 		}else
204e288d156SDavid du Colombier 			n = p->time;
205e288d156SDavid du Colombier 
2063e12c5d1SDavid du Colombier 		vp[0] = n>>24;
2073e12c5d1SDavid du Colombier 		vp[1] = n>>16;
2083e12c5d1SDavid du Colombier 		vp[2] = n>>8;
2093e12c5d1SDavid du Colombier 		vp[3] = n;
2103e12c5d1SDavid du Colombier 		vp += 4;
2113e12c5d1SDavid du Colombier 	}
212e288d156SDavid du Colombier 	write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first);
2133e12c5d1SDavid du Colombier 	close(f);
2143e12c5d1SDavid du Colombier }
2153e12c5d1SDavid du Colombier 
2163e12c5d1SDavid du Colombier void
217e288d156SDavid du Colombier _profinit(int entries, int what)
218e288d156SDavid du Colombier {
219e288d156SDavid du Colombier 	if (_tos->prof.what == 0)
220e288d156SDavid du Colombier 		return;	/* Profiling not linked in */
221e288d156SDavid du Colombier 	_tos->prof.pp = nil;
222e288d156SDavid du Colombier 	_tos->prof.first = mallocz(entries*sizeof(Plink),1);
223e288d156SDavid du Colombier 	_tos->prof.last = _tos->prof.first + entries;
224e288d156SDavid du Colombier 	_tos->prof.next = _tos->prof.first;
225e288d156SDavid du Colombier 	_tos->prof.pid = _tos->pid;
226e288d156SDavid du Colombier 	_tos->prof.what = what;
227e288d156SDavid du Colombier 	_tos->clock = 1;
228e288d156SDavid du Colombier }
229e288d156SDavid du Colombier 
230e288d156SDavid du Colombier void
2313e12c5d1SDavid du Colombier _profmain(void)
2323e12c5d1SDavid du Colombier {
2333e12c5d1SDavid du Colombier 	char ename[50];
2343e12c5d1SDavid du Colombier 	int n, f;
2353e12c5d1SDavid du Colombier 
2363e12c5d1SDavid du Colombier 	n = 2000;
237e288d156SDavid du Colombier 	if (_tos->cyclefreq != 0LL){
238e288d156SDavid du Colombier 		khz = _tos->cyclefreq / 1000;	/* Report times in milliseconds */
239e288d156SDavid du Colombier 		havecycles = 1;
240e288d156SDavid du Colombier 	}
2413e12c5d1SDavid du Colombier 	f = open("/env/profsize", OREAD);
2423e12c5d1SDavid du Colombier 	if(f >= 0) {
2433e12c5d1SDavid du Colombier 		memset(ename, 0, sizeof(ename));
2443e12c5d1SDavid du Colombier 		read(f, ename, sizeof(ename)-1);
2453e12c5d1SDavid du Colombier 		close(f);
2463e12c5d1SDavid du Colombier 		n = atol(ename);
2473e12c5d1SDavid du Colombier 	}
248e288d156SDavid du Colombier 	_tos->prof.what = Profuser;
249e288d156SDavid du Colombier 	f = open("/env/proftype", OREAD);
250e288d156SDavid du Colombier 	if(f >= 0) {
251e288d156SDavid du Colombier 		memset(ename, 0, sizeof(ename));
252e288d156SDavid du Colombier 		read(f, ename, sizeof(ename)-1);
253e288d156SDavid du Colombier 		close(f);
254e288d156SDavid du Colombier 		if (strcmp(ename, "user") == 0)
255e288d156SDavid du Colombier 			_tos->prof.what = Profuser;
256e288d156SDavid du Colombier 		else if (strcmp(ename, "kernel") == 0)
257e288d156SDavid du Colombier 			_tos->prof.what = Profkernel;
258e288d156SDavid du Colombier 		else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0)
259e288d156SDavid du Colombier 			_tos->prof.what = Proftime;
260e288d156SDavid du Colombier 		else if (strcmp(ename, "sample") == 0)
261e288d156SDavid du Colombier 			_tos->prof.what = Profsample;
2623e12c5d1SDavid du Colombier 	}
263e288d156SDavid du Colombier 	_tos->prof.first = sbrk(n*sizeof(Plink));
264e288d156SDavid du Colombier 	_tos->prof.last = sbrk(0);
265e288d156SDavid du Colombier 	_tos->prof.next = _tos->prof.first;
266e288d156SDavid du Colombier 	_tos->prof.pp = nil;
267e288d156SDavid du Colombier 	_tos->prof.pid = _tos->pid;
268e288d156SDavid du Colombier 	atexit(_profdump);
269e288d156SDavid du Colombier 	_tos->clock = 1;
270e288d156SDavid du Colombier }
271e288d156SDavid du Colombier 
272e288d156SDavid du Colombier void 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