xref: /plan9-contrib/sys/src/ape/lib/ap/plan9/profile.c (revision 0ceffd3c74a1de03d13dc3aa8fa06e39d209dbd4)
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
_profin(void)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
_profout(void)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
err(char * fmt,...)135 err(char *fmt, ...)
136 {
137 	int 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 	/*
145 	 * C99 now requires *snprintf to return the number of characters
146 	 * that *would* have been emitted, had there been room for them,
147 	 * or a negative value on an `encoding error'.  Arrgh!
148 	 */
149 	vsnprintf(buf, sizeof buf, fmt, arg);
150 	va_end(arg);
151 	write(fd, buf, strlen(buf));
152 	close(fd);
153 }
154 
155 void
_profdump(void)156 _profdump(void)
157 {
158 	int f;
159 	long n;
160 	Plink *p;
161 	char *vp;
162 	char filename[64];
163 
164 	if (_tos->prof.what == 0)
165 		return;	/* No profiling */
166 	if (_tos->prof.pid != 0 && _tos->pid != _tos->prof.pid)
167 		return;	/* Not our process */
168 	if(perr)
169 		err("%lud Prof errors\n", perr);
170 	_tos->prof.pp = NULL;
171 	if (_tos->prof.pid)
172 		snprintf(filename, sizeof filename - 1, "prof.%ld", _tos->prof.pid);
173 	else
174 		snprintf(filename, sizeof filename - 1, "prof.out");
175 	f = creat(filename, 0666);
176 	if(f < 0) {
177 		err("%s: cannot create - %s\n", filename, strerror(errno));
178 		return;
179 	}
180 	_tos->prof.pid = ~0;	/* make sure data gets dumped once */
181 	switch(_tos->prof.what){
182 	case Profkernel:
183 		_cycles((uvlong*)&_tos->prof.first->time);
184 		_tos->prof.first->time = _tos->prof.first->time + _tos->pcycles;
185 		break;
186 	case Profuser:
187 		_cycles((uvlong*)&_tos->prof.first->time);
188 		_tos->prof.first->time = _tos->prof.first->time - _tos->kcycles;
189 		break;
190 	case Proftime:
191 		_cycles((uvlong*)&_tos->prof.first->time);
192 		break;
193 	case Profsample:
194 		_tos->prof.first->time = _tos->clock;
195 		break;
196 	}
197 	vp = (char*)_tos->prof.first;
198 
199 	for(p = _tos->prof.first; p <= _tos->prof.next; p++) {
200 
201 		/*
202 		 * short down
203 		 */
204 		n = 0xffff;
205 		if(p->down)
206 			n = p->down - _tos->prof.first;
207 		vp[0] = n>>8;
208 		vp[1] = n;
209 
210 		/*
211 		 * short right
212 		 */
213 		n = 0xffff;
214 		if(p->link)
215 			n = p->link - _tos->prof.first;
216 		vp[2] = n>>8;
217 		vp[3] = n;
218 		vp += 4;
219 
220 		/*
221 		 * long pc
222 		 */
223 		n = p->pc;
224 		vp[0] = n>>24;
225 		vp[1] = n>>16;
226 		vp[2] = n>>8;
227 		vp[3] = n;
228 		vp += 4;
229 
230 		/*
231 		 * long count
232 		 */
233 		n = p->count;
234 		vp[0] = n>>24;
235 		vp[1] = n>>16;
236 		vp[2] = n>>8;
237 		vp[3] = n;
238 		vp += 4;
239 
240 		/*
241 		 * vlong time
242 		 */
243 		if (havecycles){
244 			n = (vlong)(p->time / (vlong)khz);
245 		}else
246 			n = p->time;
247 
248 		vp[0] = n>>24;
249 		vp[1] = n>>16;
250 		vp[2] = n>>8;
251 		vp[3] = n;
252 		vp += 4;
253 	}
254 	write(f, (char*)_tos->prof.first, vp - (char*)_tos->prof.first);
255 	close(f);
256 
257 }
258 
259 void
_profinit(int entries,int what)260 _profinit(int entries, int what)
261 {
262 	if (_tos->prof.what == 0)
263 		return;	/* Profiling not linked in */
264 	_tos->prof.pp = NULL;
265 	_tos->prof.first = calloc(entries*sizeof(Plink),1);
266 	_tos->prof.last = _tos->prof.first + entries;
267 	_tos->prof.next = _tos->prof.first;
268 	_tos->prof.pid = _tos->pid;
269 	_tos->prof.what = what;
270 	_tos->clock = 1;
271 }
272 
273 void
_profmain(void)274 _profmain(void)
275 {
276 	char ename[50];
277 	int n, f;
278 
279 	n = 2000;
280 	if (_tos->cyclefreq != 0LL){
281 		khz = _tos->cyclefreq / 1000;	/* Report times in milliseconds */
282 		havecycles = 1;
283 	}
284 	f = open("/env/profsize", OREAD);
285 	if(f >= 0) {
286 		memset(ename, 0, sizeof(ename));
287 		read(f, ename, sizeof(ename)-1);
288 		close(f);
289 		n = atol(ename);
290 	}
291 	_tos->prof.what = Profuser;
292 	f = open("/env/proftype", OREAD);
293 	if(f >= 0) {
294 		memset(ename, 0, sizeof(ename));
295 		read(f, ename, sizeof(ename)-1);
296 		close(f);
297 		if (strcmp(ename, "user") == 0)
298 			_tos->prof.what = Profuser;
299 		else if (strcmp(ename, "kernel") == 0)
300 			_tos->prof.what = Profkernel;
301 		else if (strcmp(ename, "elapsed") == 0 || strcmp(ename, "time") == 0)
302 			_tos->prof.what = Proftime;
303 		else if (strcmp(ename, "sample") == 0)
304 			_tos->prof.what = Profsample;
305 	}
306 	_tos->prof.first = sbrk(n*sizeof(Plink));
307 	_tos->prof.last = sbrk(0);
308 	_tos->prof.next = _tos->prof.first;
309 	_tos->prof.pp = NULL;
310 	_tos->prof.pid = _tos->pid;
311 	atexit(_profdump);
312 	_tos->clock = 1;
313 }
314 
prof(void (* fn)(void *),void * arg,int entries,int what)315 void prof(void (*fn)(void*), void *arg, int entries, int what)
316 {
317 	_profinit(entries, what);
318 	_tos->prof.pp = _tos->prof.next;
319 	fn(arg);
320 	_profdump();
321 }
322 
323 #pragma profile on
324 
325