xref: /netbsd-src/lib/libc/gmon/gmon.c (revision b2d5196452d4f37a5019e98f78d7ce5750c34c4c)
1 /*	$NetBSD: gmon.c,v 1.37 2022/05/06 04:49:13 rin 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.37 2022/05/06 04:49:13 rin 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 = { .state = 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 void	moncontrol(int);
108 void	monstartup(u_long, u_long);
109 void	_mcleanup(void);
110 static int hertz(void);
111 
112 #ifdef _REENTRANT
113 static void _m_gmon_destructor(void *);
114 struct gmonparam *_m_gmon_alloc(void)
115     __attribute__((__no_instrument_function__));
116 static void _m_gmon_merge(void);
117 static void _m_gmon_merge_two(struct gmonparam *, struct gmonparam *);
118 #endif
119 
120 void
monstartup(u_long lowpc,u_long highpc)121 monstartup(u_long lowpc, u_long highpc)
122 {
123 	u_long o;
124 	char *cp;
125 	struct gmonparam *p = &_gmonparam;
126 
127 	/*
128 	 * round lowpc and highpc to multiples of the density we're using
129 	 * so the rest of the scaling (here and in gprof) stays in ints.
130 	 */
131 	p->lowpc = rounddown(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
132 	p->highpc = roundup(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
133 	p->textsize = p->highpc - p->lowpc;
134 	p->kcountsize = p->textsize / HISTFRACTION;
135 	p->hashfraction = HASHFRACTION;
136 	p->fromssize = p->textsize / p->hashfraction;
137 	p->tolimit = p->textsize * ARCDENSITY / 100;
138 	if (p->tolimit < MINARCS)
139 		p->tolimit = MINARCS;
140 	else if (p->tolimit > MAXARCS)
141 		p->tolimit = MAXARCS;
142 	p->tossize = p->tolimit * sizeof(struct tostruct);
143 
144 	cp = sbrk((intptr_t)0);
145 
146 #define	GMON_ALLOC(buf, ptr, size)					\
147     do {								\
148 	(buf) = (void *)roundup((uintptr_t)(buf), __alignof(*(ptr)));	\
149 	(ptr) = (void *)(buf);						\
150 	(buf) += (size);						\
151     } while (0)
152 
153 	GMON_ALLOC(cp, p->kcount, p->kcountsize);
154 	GMON_ALLOC(cp, p->froms, p->fromssize);
155 	GMON_ALLOC(cp, p->tos, p->tossize);
156 
157 	if (brk(cp)) {
158 		warnx("%s: out of memory", __func__);
159 		return;
160 	}
161 	__minbrk = cp;
162 
163 	p->tos[0].link = 0;
164 
165 	o = p->highpc - p->lowpc;
166 	if (p->kcountsize < o) {
167 #ifndef notdef
168 		s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
169 #else /* avoid floating point */
170 		u_long quot = o / p->kcountsize;
171 
172 		if (quot >= 0x10000)
173 			s_scale = 1;
174 		else if (quot >= 0x100)
175 			s_scale = 0x10000 / quot;
176 		else if (o >= 0x800000)
177 			s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
178 		else
179 			s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
180 #endif
181 	} else
182 		s_scale = SCALE_1_TO_1;
183 
184 #ifdef _REENTRANT
185 	_gmondummy.state = GMON_PROF_BUSY;
186 	thr_keycreate(&_gmonkey, _m_gmon_destructor);
187 #endif
188 	moncontrol(1);
189 }
190 
191 #ifdef _REENTRANT
192 static void
_m_gmon_destructor(void * arg)193 _m_gmon_destructor(void *arg)
194 {
195 	struct gmonparam *p = arg, *q, **prev;
196 
197 	if (p == &_gmondummy)
198 		return;
199 
200 	thr_setspecific(_gmonkey, &_gmondummy);
201 
202 	mutex_lock(&_gmonlock);
203 	/* XXX eww, linear list traversal. */
204 	for (q = _gmoninuse, prev = &_gmoninuse;
205 	     q != NULL;
206 	     prev = (struct gmonparam **)(void *)&q->kcount,	/* XXX */
207 		 q = (struct gmonparam *)(void *)q->kcount) {
208 		if (q == p)
209 			*prev = (struct gmonparam *)(void *)q->kcount;
210 	}
211 	p->kcount = (u_short *)(void *)_gmonfree;
212 	_gmonfree = p;
213 	mutex_unlock(&_gmonlock);
214 
215 	thr_setspecific(_gmonkey, NULL);
216 }
217 
218 struct gmonparam *
_m_gmon_alloc(void)219 _m_gmon_alloc(void)
220 {
221 	struct gmonparam *p;
222 	char *cp;
223 
224 	mutex_lock(&_gmonlock);
225 	if (_gmonfree != NULL) {
226 		p = _gmonfree;
227 		_gmonfree = (struct gmonparam *)(void *)p->kcount;
228 		p->kcount = (u_short *)(void *)_gmoninuse;
229 		_gmoninuse = p;
230 	} else {
231 		mutex_unlock(&_gmonlock);
232 		cp = mmap(NULL,
233 		    (size_t)(__alignof(*p) + sizeof(*p) +
234 			__alignof(*_gmonparam.froms) + _gmonparam.fromssize +
235 			__alignof(*_gmonparam.tos) + _gmonparam.tossize),
236 		    PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, (off_t)0);
237 
238 		GMON_ALLOC(cp, p, sizeof(*p));
239 		*p = _gmonparam;
240 		p->state = GMON_PROF_ON;
241 		p->kcount = NULL;
242 
243 		GMON_ALLOC(cp, p->froms, p->fromssize);
244 		memset(p->froms, 0, p->fromssize);
245 
246 		GMON_ALLOC(cp, p->tos, p->tossize);
247 		memset(p->tos, 0, p->tossize);
248 
249 		mutex_lock(&_gmonlock);
250 		p->kcount = (u_short *)(void *)_gmoninuse;
251 		_gmoninuse = p;
252 	}
253 	mutex_unlock(&_gmonlock);
254 	thr_setspecific(_gmonkey, p);
255 
256 	return p;
257 }
258 
259 static void
_m_gmon_merge_two(struct gmonparam * p,struct gmonparam * q)260 _m_gmon_merge_two(struct gmonparam *p, struct gmonparam *q)
261 {
262 	u_long fromindex;
263 	u_short *frompcindex, qtoindex, toindex;
264 	u_long selfpc;
265 	u_long endfrom;
266 	long count;
267 	struct tostruct *top;
268 
269 	endfrom = (q->fromssize / sizeof(*q->froms));
270 	for (fromindex = 0; fromindex < endfrom; fromindex++) {
271 		if (q->froms[fromindex] == 0)
272 			continue;
273 		for (qtoindex = q->froms[fromindex]; qtoindex != 0;
274 		     qtoindex = q->tos[qtoindex].link) {
275 			selfpc = q->tos[qtoindex].selfpc;
276 			count = q->tos[qtoindex].count;
277 			/* cribbed from mcount */
278 			frompcindex = &p->froms[fromindex];
279 			toindex = *frompcindex;
280 			if (toindex == 0) {
281 				/*
282 				 *	first time traversing this arc
283 				 */
284 				toindex = ++p->tos[0].link;
285 				if (toindex >= p->tolimit)
286 					/* halt further profiling */
287 					goto overflow;
288 
289 				*frompcindex = (u_short)toindex;
290 				top = &p->tos[(size_t)toindex];
291 				top->selfpc = selfpc;
292 				top->count = count;
293 				top->link = 0;
294 				goto done;
295 			}
296 			top = &p->tos[(size_t)toindex];
297 			if (top->selfpc == selfpc) {
298 				/*
299 				 * arc at front of chain; usual case.
300 				 */
301 				top->count+= count;
302 				goto done;
303 			}
304 			/*
305 			 * have to go looking down chain for it.
306 			 * top points to what we are looking at,
307 			 * we know it is not at the head of the chain.
308 			 */
309 			for (; /* goto done */; ) {
310 				if (top->link == 0) {
311 					/*
312 					 * top is end of the chain and
313 					 * none of the chain had
314 					 * top->selfpc == selfpc.  so
315 					 * we allocate a new tostruct
316 					 * and link it to the head of
317 					 * the chain.
318 					 */
319 					toindex = ++p->tos[0].link;
320 					if (toindex >= p->tolimit)
321 						goto overflow;
322 
323 					top = &p->tos[(size_t)toindex];
324 					top->selfpc = selfpc;
325 					top->count = count;
326 					top->link = *frompcindex;
327 					*frompcindex = (u_short)toindex;
328 					goto done;
329 				}
330 				/*
331 				 * otherwise, check the next arc on the chain.
332 				 */
333 				top = &p->tos[top->link];
334 				if (top->selfpc == selfpc) {
335 					/*
336 					 * there it is.
337 					 * add to its count.
338 					 */
339 					top->count += count;
340 					goto done;
341 				}
342 
343 			}
344 
345 		done: ;
346 		}
347 
348 	}
349  overflow: ;
350 
351 }
352 
353 static void
_m_gmon_merge(void)354 _m_gmon_merge(void)
355 {
356 	struct gmonparam *q;
357 
358 	mutex_lock(&_gmonlock);
359 
360 	for (q = _gmonfree; q != NULL;
361 	    q = (struct gmonparam *)(void *)q->kcount)
362 		_m_gmon_merge_two(&_gmonparam, q);
363 
364 	for (q = _gmoninuse; q != NULL;
365 	    q = (struct gmonparam *)(void *)q->kcount) {
366 		q->state = GMON_PROF_OFF;
367 		_m_gmon_merge_two(&_gmonparam, q);
368 	}
369 
370 	mutex_unlock(&_gmonlock);
371 }
372 #endif
373 
374 void
_mcleanup(void)375 _mcleanup(void)
376 {
377 	int fd;
378 	int fromindex;
379 	int endfrom;
380 	u_long frompc;
381 	int toindex;
382 	struct rawarc rawarc;
383 	struct gmonparam *p = &_gmonparam;
384 	struct gmonhdr gmonhdr, *hdr;
385 	struct clockinfo clockinfo;
386 	int mib[2];
387 	size_t size;
388 	char *profdir;
389 	const char *proffile;
390 	char  buf[PATH_MAX];
391 #ifdef DEBUG
392 	int logfd, len;
393 	char buf2[200];
394 #endif
395 
396 	/*
397 	 * We disallow writing to the profiling file, if we are a
398 	 * set{u,g}id program and our effective {u,g}id does not match
399 	 * our real one.
400 	 */
401 	if (issetugid() && (geteuid() != getuid() || getegid() != getgid())) {
402 		warnx("%s: Profiling of set{u,g}id binaries is not"
403 		    " allowed", __func__);
404 		return;
405 	}
406 
407 	if (p->state == GMON_PROF_ERROR)
408 		warnx("%s: tos overflow", __func__);
409 
410 	size = sizeof(clockinfo);
411 	mib[0] = CTL_KERN;
412 	mib[1] = KERN_CLOCKRATE;
413 	if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) {
414 		/*
415 		 * Best guess
416 		 */
417 		clockinfo.profhz = hertz();
418 	} else if (clockinfo.profhz == 0) {
419 		if (clockinfo.hz != 0)
420 			clockinfo.profhz = clockinfo.hz;
421 		else
422 			clockinfo.profhz = hertz();
423 	}
424 
425 	moncontrol(0);
426 
427 	if ((profdir = getenv("PROFDIR")) != NULL) {
428 		/* If PROFDIR contains a null value, no profiling
429 		   output is produced */
430 		if (*profdir == '\0')
431 			return;
432 
433 		if (snprintf(buf, sizeof buf, "%s/%d.%s",
434 		    profdir, getpid(), getprogname()) >= (int)(sizeof buf)) {
435 			warnx("%s: internal buffer overflow, PROFDIR too long",
436 			    __func__);
437 			return;
438 		}
439 
440 		proffile = buf;
441 	} else {
442 		proffile = "gmon.out";
443 	}
444 
445 #define OPEN_FLAGS (O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC)
446 	fd = open(proffile, OPEN_FLAGS, 0666);
447 	if (fd < 0) {
448 		warn("%s: Cannot open `%s'", __func__, proffile);
449 		return;
450 	}
451 #ifdef DEBUG
452 	logfd = open("gmon.log", OPEN_FLAGS, 0664);
453 	if (logfd < 0) {
454 		warn("%s: Cannot open `%s'", __func__, "gmon.log");
455 		(void)close(fd);
456 		return;
457 	}
458 	len = snprintf(buf2, sizeof buf2, "[mcleanup1] kcount %p ssiz %lu\n",
459 	    p->kcount, p->kcountsize);
460 	(void)write(logfd, buf2, (size_t)len);
461 #endif
462 #ifdef _REENTRANT
463 	_m_gmon_merge();
464 #endif
465 	hdr = (struct gmonhdr *)&gmonhdr;
466 	hdr->lpc = p->lowpc;
467 	hdr->hpc = p->highpc;
468 	hdr->ncnt = (int)(p->kcountsize + sizeof(gmonhdr));
469 	hdr->version = GMONVERSION;
470 	hdr->profrate = clockinfo.profhz;
471 	(void)write(fd, hdr, sizeof *hdr);
472 	(void)write(fd, p->kcount, (size_t)p->kcountsize);
473 	endfrom = (int)(p->fromssize / sizeof(*p->froms));
474 	for (fromindex = 0; fromindex < endfrom; fromindex++) {
475 		if (p->froms[fromindex] == 0)
476 			continue;
477 
478 		frompc = p->lowpc;
479 		frompc += fromindex * p->hashfraction * sizeof(*p->froms);
480 		for (toindex = p->froms[fromindex]; toindex != 0;
481 		     toindex = p->tos[toindex].link) {
482 #ifdef DEBUG
483 			len = snprintf(buf2, sizeof buf2,
484 			"[mcleanup2] frompc 0x%lx selfpc 0x%lx count %lu\n" ,
485 				(u_long)frompc, (u_long)p->tos[toindex].selfpc,
486 				(u_long)p->tos[toindex].count);
487 			(void)write(logfd, buf2, (size_t)len);
488 #endif
489 			rawarc.raw_frompc = frompc;
490 			rawarc.raw_selfpc = p->tos[toindex].selfpc;
491 			rawarc.raw_count = p->tos[toindex].count;
492 			(void)write(fd, &rawarc, sizeof rawarc);
493 		}
494 	}
495 	(void)close(fd);
496 #ifdef DEBUG
497 	(void)close(logfd);
498 #endif
499 }
500 
501 /*
502  * Control profiling
503  *	profiling is what mcount checks to see if
504  *	all the data structures are ready.
505  */
506 void
moncontrol(int mode)507 moncontrol(int mode)
508 {
509 	struct gmonparam *p = &_gmonparam;
510 
511 	if (mode) {
512 		/* start */
513 		profil((char *)(void *)p->kcount, (size_t)p->kcountsize,
514 		    p->lowpc, s_scale);
515 		p->state = GMON_PROF_ON;
516 	} else {
517 		/* stop */
518 		profil(NULL, 0, (u_long)0, 0);
519 		p->state = GMON_PROF_OFF;
520 	}
521 }
522 
523 /*
524  * discover the tick frequency of the machine
525  * if something goes wrong, we return 0, an impossible hertz.
526  */
527 static int
hertz(void)528 hertz(void)
529 {
530         struct itimerspec tim;
531 	timer_t t;
532 	int rv = 0;
533 
534         tim.it_interval.tv_sec = 0;
535         tim.it_interval.tv_nsec = 1;
536         tim.it_value.tv_sec = 0;
537         tim.it_value.tv_nsec = 0;
538 
539 	if (timer_create(CLOCK_REALTIME, NULL, &t) == -1)
540 		return 0;
541 
542 	if (timer_settime(t, 0, &tim, NULL) == -1)
543 		goto out;
544 
545 	if (timer_gettime(t, &tim) == -1)
546 		goto out;
547 
548         if (tim.it_interval.tv_nsec < 2)
549 		goto out;
550 
551 	rv = (int)(1000000000LL / tim.it_interval.tv_nsec);
552 out:
553 	(void)timer_delete(t);
554 	return rv;
555 }
556