xref: /plan9-contrib/sys/src/ape/lib/ap/plan9/profile.c (revision 60014d6756a98ad10929607ca84a1b7488a16cfc)
1 #include	<stdio.h>
2 #include	<stdlib.h>
3 #include	<string.h>
4 #include	<unistd.h>
5 #include	<errno.h>
6 #include	<sys/types.h>
7 #include	<fcntl.h>
8 #include	"sys9.h"
9 
10 enum {
11 	Profoff,			/* No profiling */
12 	Profuser,			/* Measure user time only (default) */
13 	Profkernel,			/* Measure user + kernel time */
14 	Proftime,			/* Measure total time */
15 	Profsample,			/* Use clock interrupt to sample (default when there is no cycle counter) */
16 }; /* what */
17 
18 typedef long long vlong;
19 typedef unsigned long ulong;
20 typedef unsigned long long uvlong;
21 
22 #include	"/sys/include/tos.h"
23 
24 extern	void*	sbrk(ulong);
25 extern	long	_callpc(void**);
26 extern	long	_savearg(void);
27 extern	void	_cycles(uvlong*);	/* 64-bit value of the cycle counter if there is one, 0 if there isn't */
28 
29 static ulong	khz;
30 static ulong	perr;
31 static int	havecycles;
32 
33 typedef	struct	Plink	Plink;
34 struct	Plink
35 {
36 	Plink	*old;
37 	Plink	*down;
38 	Plink	*link;
39 	long	pc;
40 	long	count;
41 	vlong	time;
42 };
43 
44 #pragma profile off
45 
46 ulong
47 _profin(void)
48 {
49 	void *dummy;
50 	long pc;
51 	Plink *pp, *p;
52 	ulong arg;
53 	vlong t;
54 
55 	arg = _savearg();
56 	pc = _callpc(&dummy);
57 	pp = _tos->prof.pp;
58 	if(pp == 0 || (_tos->prof.pid && _tos->pid != _tos->prof.pid))
59 		return arg;
60 
61 	for(p=pp->down; p; p=p->link)
62 		if(p->pc == pc)
63 			goto out;
64 	p = _tos->prof.next + 1;
65 	if(p >= _tos->prof.last){
66 		_tos->prof.pp = 0;
67 		perr++;
68 		return arg;
69 	}
70 	_tos->prof.next = p;
71 	p->link = pp->down;
72 	pp->down = p;
73 	p->pc = pc;
74 	p->old = pp;
75 	p->down = 0;
76 	p->count = 0;
77 	p->time = 0LL;
78 
79 out:
80 	_tos->prof.pp = p;
81 	p->count++;
82 	switch(_tos->prof.what){
83 	case Profkernel:
84 		p->time = p->time - _tos->pcycles;
85 		goto proftime;
86 	case Profuser:
87 		/* Add kernel cycles on proc entry */
88 		p->time = p->time + _tos->kcycles;
89 		/* fall through */
90 	case Proftime:
91 	proftime:		/* Subtract cycle counter on proc entry */
92 		_cycles((uvlong*)&t);
93 		p->time = p->time - t;
94 		break;
95 	case Profsample:
96 		p->time = p->time - _tos->clock;
97 		break;
98 	}
99 	return arg;		/* disgusting linkage */
100 }
101 
102 ulong
103 _profout(void)
104 {
105 	Plink *p;
106 	ulong arg;
107 	vlong t;
108 
109 	arg = _savearg();
110 	p = _tos->prof.pp;
111 	if (p == NULL || (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid))
112 		return arg;	/* Not our process */
113 	switch(_tos->prof.what){
114 	case Profkernel:		/* Add proc cycles on proc entry */
115 		p->time = p->time + _tos->pcycles;
116 		goto proftime;
117 	case Profuser:			/* Subtract kernel cycles on proc entry */
118 		p->time = p->time - _tos->kcycles;
119 		/* fall through */
120 	case Proftime:
121 	proftime:				/* Add cycle counter on proc entry */
122 		_cycles((uvlong*)&t);
123 		p->time = p->time + t;
124 		break;
125 	case Profsample:
126 		p->time = p->time + _tos->clock;
127 		break;
128 	}
129 	_tos->prof.pp = p->old;
130 	return arg;
131 }
132 
133 /* stdio may not be ready for us yet */
134 static void
135 err(char *fmt, ...)
136 {
137 	int n, fd;
138 	va_list arg;
139 	char buf[128];
140 
141 	if((fd = open("/dev/cons", OWRITE)) == -1)
142 		return;
143 	va_start(arg, fmt);
144 	n = vsnprintf(buf, sizeof(buf), fmt, arg);
145 	va_end(arg);
146 	write(fd, buf, n);
147 	close(fd);
148 }
149 
150 void
151 _profdump(void)
152 {
153 	int f;
154 	long n;
155 	Plink *p;
156 	char *vp;
157 	char filename[64];
158 
159 	if (_tos->prof.what == 0)
160 		return;	/* No profiling */
161 	if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)
162 		return;	/* Not our process */
163 	if(perr)
164 		err("%lud Prof errors\n", perr);
165 	_tos->prof.pp = NULL;
166 	if (_tos->prof.pid)
167 		snprintf(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
168 	else
169 		snprintf(filename, sizeof filename - 1, "prof.out");
170 	f = creat(filename, 0666);
171 	if(f < 0) {
172 		err("%s: cannot create - %s\n", filename, strerror(errno));
173 		return;
174 	}
175 	_tos->prof.pid = ~0;	/* make sure data gets dumped once */
176 	switch(_tos->prof.what){
177 	case Profkernel:
178 		_cycles((uvlong*)&_tos->prof.first->time);
179 		_tos->prof.first->time = _tos->prof.first->time + _tos->pcycles;
180 		break;
181 	case Profuser:
182 		_cycles((uvlong*)&_tos->prof.first->time);
183 		_tos->prof.first->time = _tos->prof.first->time - _tos->kcycles;
184 		break;
185 	case Proftime:
186 		_cycles((uvlong*)&_tos->prof.first->time);
187 		break;
188 	case Profsample:
189 		_tos->prof.first->time = _tos->clock;
190 		break;
191 	}
192 	vp = (char*)_tos->prof.first;
193 
194 	for(p = _tos->prof.first; p <= _tos->prof.next; p++) {
195 
196 		/*
197 		 * short down
198 		 */
199 		n = 0xffff;
200 		if(p->down)
201 			n = p->down - _tos->prof.first;
202 		vp[0] = n>>8;
203 		vp[1] = n;
204 
205 		/*
206 		 * short right
207 		 */
208 		n = 0xffff;
209 		if(p->link)
210 			n = p->link - _tos->prof.first;
211 		vp[2] = n>>8;
212 		vp[3] = n;
213 		vp += 4;
214 
215 		/*
216 		 * long pc
217 		 */
218 		n = p->pc;
219 		vp[0] = n>>24;
220 		vp[1] = n>>16;
221 		vp[2] = n>>8;
222 		vp[3] = n;
223 		vp += 4;
224 
225 		/*
226 		 * long count
227 		 */
228 		n = p->count;
229 		vp[0] = n>>24;
230 		vp[1] = n>>16;
231 		vp[2] = n>>8;
232 		vp[3] = n;
233 		vp += 4;
234 
235 		/*
236 		 * vlong time
237 		 */
238 		if (havecycles){
239 			n = (vlong)(p->time / (vlong)khz);
240 		}else
241 			n = p->time;
242 
243 		vp[0] = n>>24;
244 		vp[1] = n>>16;
245 		vp[2] = n>>8;
246 		vp[3] = n;
247 		vp += 4;
248 	}
249 	write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first);
250 	close(f);
251 
252 }
253 
254 void
255 _profinit(int entries, int what)
256 {
257 	if (_tos->prof.what == 0)
258 		return;	/* Profiling not linked in */
259 	_tos->prof.pp = NULL;
260 	_tos->prof.first = calloc(entries*sizeof(Plink),1);
261 	_tos->prof.last = _tos->prof.first + entries;
262 	_tos->prof.next = _tos->prof.first;
263 	_tos->prof.pid = _tos->pid;
264 	_tos->prof.what = what;
265 	_tos->clock = 1;
266 }
267 
268 void
269 _profmain(void)
270 {
271 	char ename[50];
272 	int n, f;
273 
274 	n = 2000;
275 	if (_tos->cyclefreq != 0LL){
276 		khz = _tos->cyclefreq / 1000;	/* Report times in milliseconds */
277 		havecycles = 1;
278 	}
279 	f = open("/env/profsize", OREAD);
280 	if(f >= 0) {
281 		memset(ename, 0, sizeof(ename));
282 		read(f, ename, sizeof(ename)-1);
283 		close(f);
284 		n = atol(ename);
285 	}
286 	_tos->prof.what = Profuser;
287 	f = open("/env/proftype", OREAD);
288 	if(f >= 0) {
289 		memset(ename, 0, sizeof(ename));
290 		read(f, ename, sizeof(ename)-1);
291 		close(f);
292 		if (strcmp(ename, "user") == 0)
293 			_tos->prof.what = Profuser;
294 		else if (strcmp(ename, "kernel") == 0)
295 			_tos->prof.what = Profkernel;
296 		else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0)
297 			_tos->prof.what = Proftime;
298 		else if (strcmp(ename, "sample") == 0)
299 			_tos->prof.what = Profsample;
300 	}
301 	_tos->prof.first = sbrk(n*sizeof(Plink));
302 	_tos->prof.last = sbrk(0);
303 	_tos->prof.next = _tos->prof.first;
304 	_tos->prof.pp = NULL;
305 	_tos->prof.pid = _tos->pid;
306 	atexit(_profdump);
307 	_tos->clock = 1;
308 }
309 
310 void prof(void (*fn)(void*), void *arg, int entries, int what)
311 {
312 	_profinit(entries, what);
313 	_tos->prof.pp = _tos->prof.next;
314 	fn(arg);
315 	_profdump();
316 }
317 
318 #pragma profile on
319 
320