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