xref: /openbsd-src/lib/libc/gmon/gmon.c (revision 39152b4f449273467e8e34dae67ff62674628249)
1*39152b4fScheloha /*	$OpenBSD: gmon.c,v 1.33 2022/07/26 04:07:13 cheloha Exp $ */
2df930be7Sderaadt /*-
3df930be7Sderaadt  * Copyright (c) 1983, 1992, 1993
4df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
5df930be7Sderaadt  *
6df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
7df930be7Sderaadt  * modification, are permitted provided that the following conditions
8df930be7Sderaadt  * are met:
9df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
10df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
11df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
12df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
13df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
146580fee3Smillert  * 3. Neither the name of the University nor the names of its contributors
15df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
16df930be7Sderaadt  *    without specific prior written permission.
17df930be7Sderaadt  *
18df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28df930be7Sderaadt  * SUCH DAMAGE.
29df930be7Sderaadt  */
30df930be7Sderaadt 
31df930be7Sderaadt #include <sys/time.h>
32df930be7Sderaadt #include <sys/gmon.h>
337f1ef45aSmillert #include <sys/mman.h>
34df930be7Sderaadt #include <sys/sysctl.h>
35df930be7Sderaadt 
36df930be7Sderaadt #include <stdio.h>
3742fbf3d8Sderaadt #include <stdlib.h>
38c8f91e0dStholo #include <string.h>
39df930be7Sderaadt #include <fcntl.h>
4042fbf3d8Sderaadt #include <limits.h>
41df930be7Sderaadt #include <unistd.h>
42df930be7Sderaadt 
43df930be7Sderaadt struct gmonparam _gmonparam = { GMON_PROF_OFF };
44df930be7Sderaadt 
45df930be7Sderaadt static int	s_scale;
46df930be7Sderaadt /* see profil(2) where this is describe (incorrectly) */
47df930be7Sderaadt #define		SCALE_1_TO_1	0x10000L
48df930be7Sderaadt 
493bfa8ee4Sderaadt #define ERR(s) write(STDERR_FILENO, s, sizeof(s))
50df930be7Sderaadt 
51cbc0cb74Sguenther PROTO_NORMAL(moncontrol);
52dbf5b26bSguenther PROTO_DEPRECATED(monstartup);
53df930be7Sderaadt 
54df930be7Sderaadt void
monstartup(u_long lowpc,u_long highpc)55ee40e6f6Sotto monstartup(u_long lowpc, u_long highpc)
56df930be7Sderaadt {
57ee40e6f6Sotto 	int o;
587f1ef45aSmillert 	void *addr;
59df930be7Sderaadt 	struct gmonparam *p = &_gmonparam;
60df930be7Sderaadt 
61df930be7Sderaadt 	/*
62df930be7Sderaadt 	 * round lowpc and highpc to multiples of the density we're using
63df930be7Sderaadt 	 * so the rest of the scaling (here and in gprof) stays in ints.
64df930be7Sderaadt 	 */
65df930be7Sderaadt 	p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
66df930be7Sderaadt 	p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
67df930be7Sderaadt 	p->textsize = p->highpc - p->lowpc;
68df930be7Sderaadt 	p->kcountsize = p->textsize / HISTFRACTION;
69df930be7Sderaadt 	p->hashfraction = HASHFRACTION;
7064d71990Sderaadt 	p->fromssize = p->textsize / p->hashfraction;
71df930be7Sderaadt 	p->tolimit = p->textsize * ARCDENSITY / 100;
72df930be7Sderaadt 	if (p->tolimit < MINARCS)
73df930be7Sderaadt 		p->tolimit = MINARCS;
74df930be7Sderaadt 	else if (p->tolimit > MAXARCS)
75df930be7Sderaadt 		p->tolimit = MAXARCS;
76df930be7Sderaadt 	p->tossize = p->tolimit * sizeof(struct tostruct);
77df930be7Sderaadt 
7825a9e580Smmcc 	addr = mmap(NULL, p->kcountsize,  PROT_READ|PROT_WRITE,
793240e6a8Sguenther 	    MAP_ANON|MAP_PRIVATE, -1, 0);
807f1ef45aSmillert 	if (addr == MAP_FAILED)
817f1ef45aSmillert 		goto mapfailed;
827f1ef45aSmillert 	p->kcount = addr;
83df930be7Sderaadt 
8425a9e580Smmcc 	addr = mmap(NULL, p->fromssize,  PROT_READ|PROT_WRITE,
853240e6a8Sguenther 	    MAP_ANON|MAP_PRIVATE, -1, 0);
867f1ef45aSmillert 	if (addr == MAP_FAILED)
877f1ef45aSmillert 		goto mapfailed;
887f1ef45aSmillert 	p->froms = addr;
897f1ef45aSmillert 
9025a9e580Smmcc 	addr = mmap(NULL, p->tossize,  PROT_READ|PROT_WRITE,
913240e6a8Sguenther 	    MAP_ANON|MAP_PRIVATE, -1, 0);
927f1ef45aSmillert 	if (addr == MAP_FAILED)
937f1ef45aSmillert 		goto mapfailed;
947f1ef45aSmillert 	p->tos = addr;
95df930be7Sderaadt 	p->tos[0].link = 0;
96df930be7Sderaadt 
97df930be7Sderaadt 	o = p->highpc - p->lowpc;
98df930be7Sderaadt 	if (p->kcountsize < o) {
99df930be7Sderaadt #ifndef notdef
100df930be7Sderaadt 		s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
101df930be7Sderaadt #else /* avoid floating point */
102df930be7Sderaadt 		int quot = o / p->kcountsize;
103df930be7Sderaadt 
104df930be7Sderaadt 		if (quot >= 0x10000)
105df930be7Sderaadt 			s_scale = 1;
106df930be7Sderaadt 		else if (quot >= 0x100)
107df930be7Sderaadt 			s_scale = 0x10000 / quot;
108df930be7Sderaadt 		else if (o >= 0x800000)
109df930be7Sderaadt 			s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
110df930be7Sderaadt 		else
111df930be7Sderaadt 			s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
112df930be7Sderaadt #endif
113df930be7Sderaadt 	} else
114df930be7Sderaadt 		s_scale = SCALE_1_TO_1;
115df930be7Sderaadt 
116df930be7Sderaadt 	moncontrol(1);
1177f1ef45aSmillert 	return;
1187f1ef45aSmillert 
1197f1ef45aSmillert mapfailed:
1207f1ef45aSmillert 	if (p->kcount != NULL) {
1217f1ef45aSmillert 		munmap(p->kcount, p->kcountsize);
1227f1ef45aSmillert 		p->kcount = NULL;
1237f1ef45aSmillert 	}
1247f1ef45aSmillert 	if (p->froms != NULL) {
1257f1ef45aSmillert 		munmap(p->froms, p->fromssize);
1267f1ef45aSmillert 		p->froms = NULL;
1277f1ef45aSmillert 	}
1287f1ef45aSmillert 	if (p->tos != NULL) {
1297f1ef45aSmillert 		munmap(p->tos, p->tossize);
1307f1ef45aSmillert 		p->tos = NULL;
1317f1ef45aSmillert 	}
1327f1ef45aSmillert 	ERR("monstartup: out of memory\n");
133df930be7Sderaadt }
134dbf5b26bSguenther __strong_alias(_monstartup,monstartup);
135df930be7Sderaadt 
136df930be7Sderaadt void
_mcleanup(void)13774637cb2Sderaadt _mcleanup(void)
138df930be7Sderaadt {
139df930be7Sderaadt 	int fd;
140df930be7Sderaadt 	int fromindex;
141df930be7Sderaadt 	int endfrom;
142df930be7Sderaadt 	u_long frompc;
143df930be7Sderaadt 	int toindex;
144df930be7Sderaadt 	struct rawarc rawarc;
145df930be7Sderaadt 	struct gmonparam *p = &_gmonparam;
146df930be7Sderaadt 	struct gmonhdr gmonhdr, *hdr;
147df930be7Sderaadt 	struct clockinfo clockinfo;
14887afc19eSderaadt 	const int mib[2] = { CTL_KERN, KERN_CLOCKRATE };
149df930be7Sderaadt 	size_t size;
15042fbf3d8Sderaadt 	char *profdir;
15142fbf3d8Sderaadt 	char *proffile;
15242fbf3d8Sderaadt 	char  buf[PATH_MAX];
153df930be7Sderaadt #ifdef DEBUG
154df930be7Sderaadt 	int log, len;
1552f506d37Sderaadt 	char dbuf[200];
156df930be7Sderaadt #endif
157df930be7Sderaadt 
158df930be7Sderaadt 	if (p->state == GMON_PROF_ERROR)
159df930be7Sderaadt 		ERR("_mcleanup: tos overflow\n");
160df930be7Sderaadt 
161*39152b4fScheloha 	/*
162*39152b4fScheloha 	 * There is nothing we can do if sysctl(2) fails or if
163*39152b4fScheloha 	 * clockinfo.hz is unset.
164*39152b4fScheloha 	 */
165df930be7Sderaadt 	size = sizeof(clockinfo);
166df69c215Sderaadt 	if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) == -1) {
167*39152b4fScheloha 		clockinfo.profhz = 0;
168df930be7Sderaadt 	} else if (clockinfo.profhz == 0) {
169*39152b4fScheloha 		clockinfo.profhz = clockinfo.hz;	/* best guess */
170df930be7Sderaadt 	}
171df930be7Sderaadt 
172df930be7Sderaadt 	moncontrol(0);
17342fbf3d8Sderaadt 
1742f506d37Sderaadt 	if (issetugid() == 0 && (profdir = getenv("PROFDIR")) != NULL) {
1752f506d37Sderaadt 		char *s, *t, *limit;
17642fbf3d8Sderaadt 		pid_t pid;
17742fbf3d8Sderaadt 		long divisor;
17842fbf3d8Sderaadt 
17942fbf3d8Sderaadt 		/* If PROFDIR contains a null value, no profiling
18042fbf3d8Sderaadt 		   output is produced */
18142fbf3d8Sderaadt 		if (*profdir == '\0') {
18242fbf3d8Sderaadt 			return;
18342fbf3d8Sderaadt 		}
18442fbf3d8Sderaadt 
1852f506d37Sderaadt 		limit = buf + sizeof buf - 1 - 10 - 1 -
1862f506d37Sderaadt 		    strlen(__progname) - 1;
18742fbf3d8Sderaadt 		t = buf;
18842fbf3d8Sderaadt 		s = profdir;
1892f506d37Sderaadt 		while((*t = *s) != '\0' && t < limit) {
19042fbf3d8Sderaadt 			t++;
19142fbf3d8Sderaadt 			s++;
19242fbf3d8Sderaadt 		}
19342fbf3d8Sderaadt 		*t++ = '/';
19442fbf3d8Sderaadt 
19542fbf3d8Sderaadt 		/*
19642fbf3d8Sderaadt 		 * Copy and convert pid from a pid_t to a string.  For
19742fbf3d8Sderaadt 		 * best performance, divisor should be initialized to
19842fbf3d8Sderaadt 		 * the largest power of 10 less than PID_MAX.
19942fbf3d8Sderaadt 		 */
20042fbf3d8Sderaadt 		pid = getpid();
20142fbf3d8Sderaadt 		divisor=10000;
20242fbf3d8Sderaadt 		while (divisor > pid) divisor /= 10;	/* skip leading zeros */
20342fbf3d8Sderaadt 		do {
20442fbf3d8Sderaadt 			*t++ = (pid/divisor) + '0';
20542fbf3d8Sderaadt 			pid %= divisor;
20642fbf3d8Sderaadt 		} while (divisor /= 10);
20742fbf3d8Sderaadt 		*t++ = '.';
20842fbf3d8Sderaadt 
20942fbf3d8Sderaadt 		s = __progname;
21042fbf3d8Sderaadt 		while ((*t++ = *s++) != '\0')
21142fbf3d8Sderaadt 			;
21242fbf3d8Sderaadt 
21342fbf3d8Sderaadt 		proffile = buf;
21442fbf3d8Sderaadt 	} else {
21542fbf3d8Sderaadt 		proffile = "gmon.out";
21642fbf3d8Sderaadt 	}
21742fbf3d8Sderaadt 
2183e7612c7Smillert 	fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0664);
219df69c215Sderaadt 	if (fd == -1) {
22042fbf3d8Sderaadt 		perror( proffile );
221df930be7Sderaadt 		return;
222df930be7Sderaadt 	}
223df930be7Sderaadt #ifdef DEBUG
224df930be7Sderaadt 	log = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664);
225df69c215Sderaadt 	if (log == -1) {
226df930be7Sderaadt 		perror("mcount: gmon.log");
227ee993850Sjsg 		close(fd);
228df930be7Sderaadt 		return;
229df930be7Sderaadt 	}
230f2c97eb2Sderaadt 	snprintf(dbuf, sizeof dbuf, "[mcleanup1] kcount 0x%x ssiz %d\n",
231df930be7Sderaadt 	    p->kcount, p->kcountsize);
232f2c97eb2Sderaadt 	write(log, dbuf, strlen(dbuf));
233df930be7Sderaadt #endif
234df930be7Sderaadt 	hdr = (struct gmonhdr *)&gmonhdr;
235672e980cScloder 	bzero(hdr, sizeof(*hdr));
236df930be7Sderaadt 	hdr->lpc = p->lowpc;
237df930be7Sderaadt 	hdr->hpc = p->highpc;
238df930be7Sderaadt 	hdr->ncnt = p->kcountsize + sizeof(gmonhdr);
239df930be7Sderaadt 	hdr->version = GMONVERSION;
240df930be7Sderaadt 	hdr->profrate = clockinfo.profhz;
241df930be7Sderaadt 	write(fd, (char *)hdr, sizeof *hdr);
242df930be7Sderaadt 	write(fd, p->kcount, p->kcountsize);
243df930be7Sderaadt 	endfrom = p->fromssize / sizeof(*p->froms);
244df930be7Sderaadt 	for (fromindex = 0; fromindex < endfrom; fromindex++) {
245df930be7Sderaadt 		if (p->froms[fromindex] == 0)
246df930be7Sderaadt 			continue;
247df930be7Sderaadt 
248df930be7Sderaadt 		frompc = p->lowpc;
249df930be7Sderaadt 		frompc += fromindex * p->hashfraction * sizeof(*p->froms);
250df930be7Sderaadt 		for (toindex = p->froms[fromindex]; toindex != 0;
251df930be7Sderaadt 		     toindex = p->tos[toindex].link) {
252df930be7Sderaadt #ifdef DEBUG
253f2c97eb2Sderaadt 			(void) snprintf(dbuf, sizeof dbuf,
254df930be7Sderaadt 			"[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" ,
255df930be7Sderaadt 				frompc, p->tos[toindex].selfpc,
256df930be7Sderaadt 				p->tos[toindex].count);
257f2c97eb2Sderaadt 			write(log, dbuf, strlen(dbuf));
258df930be7Sderaadt #endif
259df930be7Sderaadt 			rawarc.raw_frompc = frompc;
260df930be7Sderaadt 			rawarc.raw_selfpc = p->tos[toindex].selfpc;
261df930be7Sderaadt 			rawarc.raw_count = p->tos[toindex].count;
262df930be7Sderaadt 			write(fd, &rawarc, sizeof rawarc);
263df930be7Sderaadt 		}
264df930be7Sderaadt 	}
265df930be7Sderaadt 	close(fd);
2667f1ef45aSmillert #ifdef notyet
2677f1ef45aSmillert 	if (p->kcount != NULL) {
2687f1ef45aSmillert 		munmap(p->kcount, p->kcountsize);
2697f1ef45aSmillert 		p->kcount = NULL;
2707f1ef45aSmillert 	}
2717f1ef45aSmillert 	if (p->froms != NULL) {
2727f1ef45aSmillert 		munmap(p->froms, p->fromssize);
2737f1ef45aSmillert 		p->froms = NULL;
2747f1ef45aSmillert 	}
2757f1ef45aSmillert 	if (p->tos != NULL) {
2767f1ef45aSmillert 		munmap(p->tos, p->tossize);
2777f1ef45aSmillert 		p->tos = NULL;
2787f1ef45aSmillert 	}
2797f1ef45aSmillert #endif
280df930be7Sderaadt }
281df930be7Sderaadt 
282df930be7Sderaadt /*
283df930be7Sderaadt  * Control profiling
284df930be7Sderaadt  *	profiling is what mcount checks to see if
285df930be7Sderaadt  *	all the data structures are ready.
286df930be7Sderaadt  */
287df930be7Sderaadt void
moncontrol(int mode)288ee40e6f6Sotto moncontrol(int mode)
289df930be7Sderaadt {
290df930be7Sderaadt 	struct gmonparam *p = &_gmonparam;
291df930be7Sderaadt 
292df930be7Sderaadt 	if (mode) {
293df930be7Sderaadt 		/* start */
29442fbf3d8Sderaadt 		profil((char *)p->kcount, p->kcountsize, p->lowpc,
295df930be7Sderaadt 		    s_scale);
296df930be7Sderaadt 		p->state = GMON_PROF_ON;
297df930be7Sderaadt 	} else {
298df930be7Sderaadt 		/* stop */
299e72220c0Smmcc 		profil(NULL, 0, 0, 0);
300df930be7Sderaadt 		p->state = GMON_PROF_OFF;
301df930be7Sderaadt 	}
302df930be7Sderaadt }
303cbc0cb74Sguenther DEF_WEAK(moncontrol);
304