xref: /netbsd-src/lib/libc/gmon/gmon.c (revision da5f4674a3fc214be3572d358b66af40ab9401e7)
1 /*	$NetBSD: gmon.c,v 1.21 2003/08/07 16:43:02 agc Exp $	*/
2 
3 /*-
4  * Copyright (c) 1983, 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #if !defined(lint) && defined(LIBC_SCCS)
34 #if 0
35 static char sccsid[] = "@(#)gmon.c	8.1 (Berkeley) 6/4/93";
36 #else
37 __RCSID("$NetBSD: gmon.c,v 1.21 2003/08/07 16:43:02 agc Exp $");
38 #endif
39 #endif
40 
41 #include "namespace.h"
42 #include <sys/param.h>
43 #include <sys/time.h>
44 #include <sys/gmon.h>
45 #include <sys/sysctl.h>
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <fcntl.h>
50 #include <limits.h>
51 #include <unistd.h>
52 #include <err.h>
53 #include "extern.h"
54 
55 struct gmonparam _gmonparam = { GMON_PROF_OFF };
56 
57 static u_int	s_scale;
58 /* see profil(2) where this is describe (incorrectly) */
59 #define		SCALE_1_TO_1	0x10000L
60 
61 #define ERR(s) write(STDERR_FILENO, s, sizeof(s))
62 
63 void	moncontrol __P((int));
64 void	monstartup __P((u_long, u_long));
65 void	_mcleanup __P((void));
66 static int hertz __P((void));
67 
68 
69 void
70 monstartup(lowpc, highpc)
71 	u_long lowpc;
72 	u_long highpc;
73 {
74 	u_long o;
75 	char *cp;
76 	struct gmonparam *p = &_gmonparam;
77 
78 	/*
79 	 * round lowpc and highpc to multiples of the density we're using
80 	 * so the rest of the scaling (here and in gprof) stays in ints.
81 	 */
82 	p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
83 	p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
84 	p->textsize = p->highpc - p->lowpc;
85 	p->kcountsize = p->textsize / HISTFRACTION;
86 	p->hashfraction = HASHFRACTION;
87 	p->fromssize = p->textsize / p->hashfraction;
88 	p->tolimit = p->textsize * ARCDENSITY / 100;
89 	if (p->tolimit < MINARCS)
90 		p->tolimit = MINARCS;
91 	else if (p->tolimit > MAXARCS)
92 		p->tolimit = MAXARCS;
93 	p->tossize = p->tolimit * sizeof(struct tostruct);
94 
95 	cp = sbrk((intptr_t)(p->kcountsize + p->fromssize + p->tossize));
96 	if (cp == (char *)-1) {
97 		ERR("monstartup: out of memory\n");
98 		return;
99 	}
100 #ifdef notdef
101 	memset(cp, 0, p->kcountsize + p->fromssize + p->tossize);
102 #endif
103 	p->tos = (struct tostruct *)(void *)cp;
104 	cp += (size_t)p->tossize;
105 	p->kcount = (u_short *)(void *)cp;
106 	cp += (size_t)p->kcountsize;
107 	p->froms = (u_short *)(void *)cp;
108 
109 	__minbrk = sbrk((intptr_t)0);
110 	p->tos[0].link = 0;
111 
112 	o = p->highpc - p->lowpc;
113 	if (p->kcountsize < o) {
114 #ifndef notdef
115 		s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
116 #else /* avoid floating point */
117 		u_long quot = o / p->kcountsize;
118 
119 		if (quot >= 0x10000)
120 			s_scale = 1;
121 		else if (quot >= 0x100)
122 			s_scale = 0x10000 / quot;
123 		else if (o >= 0x800000)
124 			s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
125 		else
126 			s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
127 #endif
128 	} else
129 		s_scale = SCALE_1_TO_1;
130 
131 	moncontrol(1);
132 }
133 
134 void
135 _mcleanup()
136 {
137 	int fd;
138 	int fromindex;
139 	int endfrom;
140 	u_long frompc;
141 	int toindex;
142 	struct rawarc rawarc;
143 	struct gmonparam *p = &_gmonparam;
144 	struct gmonhdr gmonhdr, *hdr;
145 	struct clockinfo clockinfo;
146 	int mib[2];
147 	size_t size;
148 	char *profdir;
149 	char *proffile;
150 	char  buf[PATH_MAX];
151 #ifdef DEBUG
152 	int log, len;
153 	char buf2[200];
154 #endif
155 
156 	/*
157 	 * We disallow writing to the profiling file, if we are a
158 	 * set{u,g}id program and our effective {u,g}id does not match
159 	 * our real one.
160 	 */
161 	if (issetugid() && (geteuid() != getuid() || getegid() != getgid())) {
162 		warnx("mcount: Profiling of set{u,g}id binaries is not"
163 		    " allowed");
164 		return;
165 	}
166 
167 	if (p->state == GMON_PROF_ERROR)
168 		ERR("_mcleanup: tos overflow\n");
169 
170 	size = sizeof(clockinfo);
171 	mib[0] = CTL_KERN;
172 	mib[1] = KERN_CLOCKRATE;
173 	if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) {
174 		/*
175 		 * Best guess
176 		 */
177 		clockinfo.profhz = hertz();
178 	} else if (clockinfo.profhz == 0) {
179 		if (clockinfo.hz != 0)
180 			clockinfo.profhz = clockinfo.hz;
181 		else
182 			clockinfo.profhz = hertz();
183 	}
184 
185 	moncontrol(0);
186 
187 	if ((profdir = getenv("PROFDIR")) != NULL) {
188 		/* If PROFDIR contains a null value, no profiling
189 		   output is produced */
190 		if (*profdir == '\0')
191 			return;
192 
193 		if (snprintf(buf, sizeof buf, "%s/%d.%s",
194 			    profdir, getpid(), getprogname()) >= sizeof buf) {
195 			warnx("_mcleanup: internal buffer overflow, PROFDIR too long");
196 			return;
197 		}
198 
199 		proffile = buf;
200 	} else {
201 		proffile = "gmon.out";
202 	}
203 
204 	fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0666);
205 	if (fd < 0) {
206 		warn("mcount: Cannot open `%s'", proffile);
207 		return;
208 	}
209 #ifdef DEBUG
210 	log = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664);
211 	if (log < 0) {
212 		warn("mcount: Cannot open `gmon.log'");
213 		return;
214 	}
215 	len = snprintf(buf2, sizeof buf2, "[mcleanup1] kcount 0x%x ssiz %d\n",
216 	    p->kcount, p->kcountsize);
217 	write(log, buf2, len);
218 #endif
219 	hdr = (struct gmonhdr *)&gmonhdr;
220 	hdr->lpc = p->lowpc;
221 	hdr->hpc = p->highpc;
222 	hdr->ncnt = (int)(p->kcountsize + sizeof(gmonhdr));
223 	hdr->version = GMONVERSION;
224 	hdr->profrate = clockinfo.profhz;
225 	(void)write(fd, hdr, sizeof *hdr);
226 	(void)write(fd, p->kcount, (size_t)p->kcountsize);
227 	endfrom = (int)(p->fromssize / sizeof(*p->froms));
228 	for (fromindex = 0; fromindex < endfrom; fromindex++) {
229 		if (p->froms[fromindex] == 0)
230 			continue;
231 
232 		frompc = p->lowpc;
233 		frompc += fromindex * p->hashfraction * sizeof(*p->froms);
234 		for (toindex = p->froms[fromindex]; toindex != 0;
235 		     toindex = p->tos[toindex].link) {
236 #ifdef DEBUG
237 			len = snprintf(buf2, sizeof buf2,
238 			"[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" ,
239 				frompc, p->tos[toindex].selfpc,
240 				p->tos[toindex].count);
241 			write(log, buf2, len);
242 #endif
243 			rawarc.raw_frompc = frompc;
244 			rawarc.raw_selfpc = p->tos[toindex].selfpc;
245 			rawarc.raw_count = p->tos[toindex].count;
246 			write(fd, &rawarc, sizeof rawarc);
247 		}
248 	}
249 	close(fd);
250 }
251 
252 /*
253  * Control profiling
254  *	profiling is what mcount checks to see if
255  *	all the data structures are ready.
256  */
257 void
258 moncontrol(mode)
259 	int mode;
260 {
261 	struct gmonparam *p = &_gmonparam;
262 
263 	if (mode) {
264 		/* start */
265 		profil((char *)(void *)p->kcount, (size_t)p->kcountsize,
266 		    p->lowpc, s_scale);
267 		p->state = GMON_PROF_ON;
268 	} else {
269 		/* stop */
270 		profil(NULL, 0, (u_long)0, 0);
271 		p->state = GMON_PROF_OFF;
272 	}
273 }
274 
275 /*
276  * discover the tick frequency of the machine
277  * if something goes wrong, we return 0, an impossible hertz.
278  */
279 static int
280 hertz()
281 {
282 	struct itimerval tim;
283 
284 	tim.it_interval.tv_sec = 0;
285 	tim.it_interval.tv_usec = 1;
286 	tim.it_value.tv_sec = 0;
287 	tim.it_value.tv_usec = 0;
288 	setitimer(ITIMER_REAL, &tim, 0);
289 	setitimer(ITIMER_REAL, 0, &tim);
290 	if (tim.it_interval.tv_usec < 2)
291 		return(0);
292 	return (int)(1000000 / tim.it_interval.tv_usec);
293 }
294