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