xref: /netbsd-src/lib/libc/gmon/gmon.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: gmon.c,v 1.22 2004/05/10 22:16:42 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 2003, 2004 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Nathan J. Williams for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed for the NetBSD Project by
20  *	Wasabi Systems, Inc.
21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22  *    or promote products derived from this software without specific prior
23  *    written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 /*-
39  * Copyright (c) 1983, 1992, 1993
40  *	The Regents of the University of California.  All rights reserved.
41  *
42  * Redistribution and use in source and binary forms, with or without
43  * modification, are permitted provided that the following conditions
44  * are met:
45  * 1. Redistributions of source code must retain the above copyright
46  *    notice, this list of conditions and the following disclaimer.
47  * 2. Redistributions in binary form must reproduce the above copyright
48  *    notice, this list of conditions and the following disclaimer in the
49  *    documentation and/or other materials provided with the distribution.
50  * 3. Neither the name of the University nor the names of its contributors
51  *    may be used to endorse or promote products derived from this software
52  *    without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64  * SUCH DAMAGE.
65  */
66 
67 #include <sys/cdefs.h>
68 #if !defined(lint) && defined(LIBC_SCCS)
69 #if 0
70 static char sccsid[] = "@(#)gmon.c	8.1 (Berkeley) 6/4/93";
71 #else
72 __RCSID("$NetBSD: gmon.c,v 1.22 2004/05/10 22:16:42 thorpej Exp $");
73 #endif
74 #endif
75 
76 #include "namespace.h"
77 #include <sys/param.h>
78 #include <sys/time.h>
79 #include <sys/gmon.h>
80 #include <sys/mman.h>
81 #include <sys/sysctl.h>
82 
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <string.h>
86 #include <fcntl.h>
87 #include <limits.h>
88 #include <unistd.h>
89 #include <err.h>
90 #include "extern.h"
91 #include "reentrant.h"
92 
93 struct gmonparam _gmonparam = { GMON_PROF_OFF };
94 
95 #ifdef _REENTRANT
96 struct gmonparam *_gmonfree;
97 struct gmonparam *_gmoninuse;
98 mutex_t _gmonlock = MUTEX_INITIALIZER;
99 thread_key_t _gmonkey;
100 struct gmonparam _gmondummy;
101 #endif
102 
103 static u_int	s_scale;
104 /* see profil(2) where this is describe (incorrectly) */
105 #define		SCALE_1_TO_1	0x10000L
106 
107 #define ERR(s) write(STDERR_FILENO, s, sizeof(s))
108 
109 void	moncontrol __P((int));
110 void	monstartup __P((u_long, u_long));
111 void	_mcleanup __P((void));
112 static int hertz __P((void));
113 
114 #ifdef _REENTRANT
115 static void _m_gmon_destructor(void *);
116 struct gmonparam *_m_gmon_alloc(void)  __attribute__((__no_instrument_function__));
117 static void _m_gmon_merge(void);
118 static void _m_gmon_merge_two(struct gmonparam *, struct gmonparam *);
119 #endif
120 
121 void
122 monstartup(lowpc, highpc)
123 	u_long lowpc;
124 	u_long highpc;
125 {
126 	u_long o;
127 	char *cp;
128 	struct gmonparam *p = &_gmonparam;
129 
130 	/*
131 	 * round lowpc and highpc to multiples of the density we're using
132 	 * so the rest of the scaling (here and in gprof) stays in ints.
133 	 */
134 	p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
135 	p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
136 	p->textsize = p->highpc - p->lowpc;
137 	p->kcountsize = p->textsize / HISTFRACTION;
138 	p->hashfraction = HASHFRACTION;
139 	p->fromssize = p->textsize / p->hashfraction;
140 	p->tolimit = p->textsize * ARCDENSITY / 100;
141 	if (p->tolimit < MINARCS)
142 		p->tolimit = MINARCS;
143 	else if (p->tolimit > MAXARCS)
144 		p->tolimit = MAXARCS;
145 	p->tossize = p->tolimit * sizeof(struct tostruct);
146 
147 	cp = sbrk((intptr_t)(p->kcountsize + p->fromssize + p->tossize));
148 	if (cp == (char *)-1) {
149 		ERR("monstartup: out of memory\n");
150 		return;
151 	}
152 #ifdef notdef
153 	memset(cp, 0, p->kcountsize + p->fromssize + p->tossize);
154 #endif
155 	p->tos = (struct tostruct *)(void *)cp;
156 	cp += (size_t)p->tossize;
157 	p->kcount = (u_short *)(void *)cp;
158 	cp += (size_t)p->kcountsize;
159 	p->froms = (u_short *)(void *)cp;
160 
161 	__minbrk = sbrk((intptr_t)0);
162 	p->tos[0].link = 0;
163 
164 	o = p->highpc - p->lowpc;
165 	if (p->kcountsize < o) {
166 #ifndef notdef
167 		s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
168 #else /* avoid floating point */
169 		u_long quot = o / p->kcountsize;
170 
171 		if (quot >= 0x10000)
172 			s_scale = 1;
173 		else if (quot >= 0x100)
174 			s_scale = 0x10000 / quot;
175 		else if (o >= 0x800000)
176 			s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
177 		else
178 			s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
179 #endif
180 	} else
181 		s_scale = SCALE_1_TO_1;
182 
183 #ifdef _REENTRANT
184 	_gmondummy.state = GMON_PROF_BUSY;
185 	thr_keycreate(&_gmonkey, _m_gmon_destructor);
186 #endif
187 	moncontrol(1);
188 }
189 
190 #ifdef _REENTRANT
191 static void
192 _m_gmon_destructor(void *arg)
193 {
194 	struct gmonparam *p = arg, *q, **prev;
195 
196 	if (p == &_gmondummy)
197 		return;
198 
199 	thr_setspecific(_gmonkey, &_gmondummy);
200 
201 	mutex_lock(&_gmonlock);
202 	/* XXX eww, linear list traversal. */
203 	for (q = _gmoninuse, prev = &_gmoninuse;
204 	     q != NULL;
205 	     prev = (struct gmonparam **)&q->kcount,
206 		 q = (struct gmonparam *)(void *)q->kcount) {
207 		if (q == p)
208 			*prev = (struct gmonparam *)(void *)q->kcount;
209 	}
210 	p->kcount = (u_short *)(void *)_gmonfree;
211 	_gmonfree = p;
212 	mutex_unlock(&_gmonlock);
213 
214 	thr_setspecific(_gmonkey, NULL);
215 }
216 
217 struct gmonparam *
218 _m_gmon_alloc(void)
219 {
220 	struct gmonparam *p;
221 	char *cp;
222 
223 	mutex_lock(&_gmonlock);
224 	if (_gmonfree != NULL) {
225 		p = _gmonfree;
226 		_gmonfree = (struct gmonparam *)(void *)p->kcount;
227 		p->kcount = (u_short *)(void *)_gmoninuse;
228 		_gmoninuse = p;
229 	} else {
230 		mutex_unlock(&_gmonlock);
231 		cp = mmap(NULL,
232 		    (size_t)(sizeof (struct gmonparam) +
233 			_gmonparam.fromssize + _gmonparam.tossize),
234 		    PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0LL);
235 		p = (void *)cp;
236 		*p = _gmonparam;
237 		p->kcount = NULL;
238 		cp += sizeof (struct gmonparam);
239 		memset(cp, 0, (size_t)(p->fromssize + p->tossize));
240 		p->froms = (u_short *)(void *)cp;
241 		p->tos = (struct tostruct *)(void *)(cp + p->fromssize);
242 		mutex_lock(&_gmonlock);
243 		p->kcount = (u_short *)(void *)_gmoninuse;
244 		_gmoninuse = p;
245 	}
246 	mutex_unlock(&_gmonlock);
247 	thr_setspecific(_gmonkey, p);
248 
249 	return p;
250 }
251 
252 static void
253 _m_gmon_merge_two(struct gmonparam *p, struct gmonparam *q)
254 {
255 	u_long fromindex;
256 	u_short *frompcindex, qtoindex, toindex;
257 	u_long selfpc;
258 	int endfrom;
259 	long count;
260 	struct tostruct *top;
261 
262 	endfrom = (int)(q->fromssize / sizeof(*q->froms));
263 	for (fromindex = 0; fromindex < endfrom; fromindex++) {
264 		if (q->froms[fromindex] == 0)
265 			continue;
266 		for (qtoindex = q->froms[fromindex]; qtoindex != 0;
267 		     qtoindex = q->tos[qtoindex].link) {
268 			selfpc = q->tos[qtoindex].selfpc;
269 			count = q->tos[qtoindex].count;
270 			/* cribbed from mcount */
271 			frompcindex = &p->froms[fromindex];
272 			toindex = *frompcindex;
273 			if (toindex == 0) {
274 				/*
275 				 *	first time traversing this arc
276 				 */
277 				toindex = ++p->tos[0].link;
278 				if (toindex >= p->tolimit)
279 					/* halt further profiling */
280 					goto overflow;
281 
282 				*frompcindex = (u_short)toindex;
283 				top = &p->tos[(size_t)toindex];
284 				top->selfpc = selfpc;
285 				top->count = count;
286 				top->link = 0;
287 				goto done;
288 			}
289 			top = &p->tos[(size_t)toindex];
290 			if (top->selfpc == selfpc) {
291 				/*
292 				 * arc at front of chain; usual case.
293 				 */
294 				top->count+= count;
295 				goto done;
296 			}
297 			/*
298 			 * have to go looking down chain for it.
299 			 * top points to what we are looking at,
300 			 * we know it is not at the head of the chain.
301 			 */
302 			for (; /* goto done */; ) {
303 				if (top->link == 0) {
304 					/*
305 					 * top is end of the chain and
306 					 * none of the chain had
307 					 * top->selfpc == selfpc.  so
308 					 * we allocate a new tostruct
309 					 * and link it to the head of
310 					 * the chain.
311 					 */
312 					toindex = ++p->tos[0].link;
313 					if (toindex >= p->tolimit)
314 						goto overflow;
315 
316 					top = &p->tos[(size_t)toindex];
317 					top->selfpc = selfpc;
318 					top->count = count;
319 					top->link = *frompcindex;
320 					*frompcindex = (u_short)toindex;
321 					goto done;
322 				}
323 				/*
324 				 * otherwise, check the next arc on the chain.
325 				 */
326 				top = &p->tos[top->link];
327 				if (top->selfpc == selfpc) {
328 					/*
329 					 * there it is.
330 					 * add to its count.
331 					 */
332 					top->count += count;
333 					goto done;
334 				}
335 
336 			}
337 
338 		done: ;
339 		}
340 
341 	}
342  overflow: ;
343 
344 }
345 
346 static void
347 _m_gmon_merge(void)
348 {
349 	struct gmonparam *q;
350 
351 	mutex_lock(&_gmonlock);
352 
353 	for (q = _gmonfree; q != NULL; q = (struct gmonparam *)(void *)q->kcount)
354 		_m_gmon_merge_two(&_gmonparam, q);
355 
356 	for (q = _gmoninuse; q != NULL; q = (struct gmonparam *)(void *)q->kcount) {
357 		q->state = GMON_PROF_OFF;
358 		_m_gmon_merge_two(&_gmonparam, q);
359 	}
360 
361 	mutex_unlock(&_gmonlock);
362 }
363 #endif
364 
365 void
366 _mcleanup()
367 {
368 	int fd;
369 	int fromindex;
370 	int endfrom;
371 	u_long frompc;
372 	int toindex;
373 	struct rawarc rawarc;
374 	struct gmonparam *p = &_gmonparam;
375 	struct gmonhdr gmonhdr, *hdr;
376 	struct clockinfo clockinfo;
377 	int mib[2];
378 	size_t size;
379 	char *profdir;
380 	char *proffile;
381 	char  buf[PATH_MAX];
382 #ifdef DEBUG
383 	int log, len;
384 	char buf2[200];
385 #endif
386 
387 	/*
388 	 * We disallow writing to the profiling file, if we are a
389 	 * set{u,g}id program and our effective {u,g}id does not match
390 	 * our real one.
391 	 */
392 	if (issetugid() && (geteuid() != getuid() || getegid() != getgid())) {
393 		warnx("mcount: Profiling of set{u,g}id binaries is not"
394 		    " allowed");
395 		return;
396 	}
397 
398 	if (p->state == GMON_PROF_ERROR)
399 		ERR("_mcleanup: tos overflow\n");
400 
401 	size = sizeof(clockinfo);
402 	mib[0] = CTL_KERN;
403 	mib[1] = KERN_CLOCKRATE;
404 	if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) {
405 		/*
406 		 * Best guess
407 		 */
408 		clockinfo.profhz = hertz();
409 	} else if (clockinfo.profhz == 0) {
410 		if (clockinfo.hz != 0)
411 			clockinfo.profhz = clockinfo.hz;
412 		else
413 			clockinfo.profhz = hertz();
414 	}
415 
416 	moncontrol(0);
417 
418 	if ((profdir = getenv("PROFDIR")) != NULL) {
419 		/* If PROFDIR contains a null value, no profiling
420 		   output is produced */
421 		if (*profdir == '\0')
422 			return;
423 
424 		if (snprintf(buf, sizeof buf, "%s/%d.%s",
425 			    profdir, getpid(), getprogname()) >= sizeof buf) {
426 			warnx("_mcleanup: internal buffer overflow, PROFDIR too long");
427 			return;
428 		}
429 
430 		proffile = buf;
431 	} else {
432 		proffile = "gmon.out";
433 	}
434 
435 	fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0666);
436 	if (fd < 0) {
437 		warn("mcount: Cannot open `%s'", proffile);
438 		return;
439 	}
440 #ifdef DEBUG
441 	log = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664);
442 	if (log < 0) {
443 		warn("mcount: Cannot open `gmon.log'");
444 		return;
445 	}
446 	len = snprintf(buf2, sizeof buf2, "[mcleanup1] kcount 0x%x ssiz %d\n",
447 	    p->kcount, p->kcountsize);
448 	write(log, buf2, len);
449 #endif
450 #ifdef _REENTRANT
451 	_m_gmon_merge();
452 #endif
453 	hdr = (struct gmonhdr *)&gmonhdr;
454 	hdr->lpc = p->lowpc;
455 	hdr->hpc = p->highpc;
456 	hdr->ncnt = (int)(p->kcountsize + sizeof(gmonhdr));
457 	hdr->version = GMONVERSION;
458 	hdr->profrate = clockinfo.profhz;
459 	(void)write(fd, hdr, sizeof *hdr);
460 	(void)write(fd, p->kcount, (size_t)p->kcountsize);
461 	endfrom = (int)(p->fromssize / sizeof(*p->froms));
462 	for (fromindex = 0; fromindex < endfrom; fromindex++) {
463 		if (p->froms[fromindex] == 0)
464 			continue;
465 
466 		frompc = p->lowpc;
467 		frompc += fromindex * p->hashfraction * sizeof(*p->froms);
468 		for (toindex = p->froms[fromindex]; toindex != 0;
469 		     toindex = p->tos[toindex].link) {
470 #ifdef DEBUG
471 			len = snprintf(buf2, sizeof buf2,
472 			"[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" ,
473 				frompc, p->tos[toindex].selfpc,
474 				p->tos[toindex].count);
475 			write(log, buf2, len);
476 #endif
477 			rawarc.raw_frompc = frompc;
478 			rawarc.raw_selfpc = p->tos[toindex].selfpc;
479 			rawarc.raw_count = p->tos[toindex].count;
480 			write(fd, &rawarc, sizeof rawarc);
481 		}
482 	}
483 	close(fd);
484 }
485 
486 /*
487  * Control profiling
488  *	profiling is what mcount checks to see if
489  *	all the data structures are ready.
490  */
491 void
492 moncontrol(mode)
493 	int mode;
494 {
495 	struct gmonparam *p = &_gmonparam;
496 
497 	if (mode) {
498 		/* start */
499 		profil((char *)(void *)p->kcount, (size_t)p->kcountsize,
500 		    p->lowpc, s_scale);
501 		p->state = GMON_PROF_ON;
502 	} else {
503 		/* stop */
504 		profil(NULL, 0, (u_long)0, 0);
505 		p->state = GMON_PROF_OFF;
506 	}
507 }
508 
509 /*
510  * discover the tick frequency of the machine
511  * if something goes wrong, we return 0, an impossible hertz.
512  */
513 static int
514 hertz()
515 {
516 	struct itimerval tim;
517 
518 	tim.it_interval.tv_sec = 0;
519 	tim.it_interval.tv_usec = 1;
520 	tim.it_value.tv_sec = 0;
521 	tim.it_value.tv_usec = 0;
522 	setitimer(ITIMER_REAL, &tim, 0);
523 	setitimer(ITIMER_REAL, 0, &tim);
524 	if (tim.it_interval.tv_usec < 2)
525 		return(0);
526 	return (int)(1000000 / tim.it_interval.tv_usec);
527 }
528