gmon.c revision 1.6
1238106Sdes/*	$NetBSD: gmon.c,v 1.6 1996/06/12 04:15:34 cgd Exp $	*/
2238106Sdes
3238106Sdes/*-
4238106Sdes * Copyright (c) 1983, 1992, 1993
5238106Sdes *	The Regents of the University of California.  All rights reserved.
6238106Sdes *
7238106Sdes * Redistribution and use in source and binary forms, with or without
8238106Sdes * modification, are permitted provided that the following conditions
9238106Sdes * are met:
10238106Sdes * 1. Redistributions of source code must retain the above copyright
11238106Sdes *    notice, this list of conditions and the following disclaimer.
12238106Sdes * 2. Redistributions in binary form must reproduce the above copyright
13238106Sdes *    notice, this list of conditions and the following disclaimer in the
14238106Sdes *    documentation and/or other materials provided with the distribution.
15238106Sdes * 3. All advertising materials mentioning features or use of this software
16238106Sdes *    must display the following acknowledgement:
17238106Sdes *	This product includes software developed by the University of
18238106Sdes *	California, Berkeley and its contributors.
19238106Sdes * 4. Neither the name of the University nor the names of its contributors
20238106Sdes *    may be used to endorse or promote products derived from this software
21238106Sdes *    without specific prior written permission.
22238106Sdes *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24266114Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25266114Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26266114Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27266114Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28266114Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29266114Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30266114Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31266114Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32266114Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33266114Sdes * SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes#if !defined(lint) && defined(LIBC_SCCS)
37238106Sdes#if 0
38238106Sdesstatic char sccsid[] = "@(#)gmon.c	8.1 (Berkeley) 6/4/93";
39238106Sdes#else
40266114Sdesstatic char rcsid[] = "$NetBSD: gmon.c,v 1.6 1996/06/12 04:15:34 cgd Exp $";
41266114Sdes#endif
42266114Sdes#endif
43266114Sdes
44266114Sdes#include <sys/param.h>
45266114Sdes#include <sys/time.h>
46266114Sdes#include <sys/gmon.h>
47266114Sdes#include <sys/sysctl.h>
48266114Sdes
49266114Sdes#include <stdio.h>
50266114Sdes#include <stdlib.h>
51266114Sdes#include <fcntl.h>
52266114Sdes#include <limits.h>
53266114Sdes#include <unistd.h>
54266114Sdes
55266114Sdesextern char *minbrk asm ("minbrk");
56266114Sdes
57266114Sdesstruct gmonparam _gmonparam = { GMON_PROF_OFF };
58266114Sdes
59266114Sdesstatic int	s_scale;
60266114Sdes/* see profil(2) where this is describe (incorrectly) */
61266114Sdes#define		SCALE_1_TO_1	0x10000L
62266114Sdes
63266114Sdes#define ERR(s) write(2, s, sizeof(s))
64266114Sdes
65266114Sdesvoid	moncontrol __P((int));
66266114Sdesstatic int hertz __P((void));
67266114Sdes
68266114Sdesvoid
69266114Sdesmonstartup(lowpc, highpc)
70266114Sdes	u_long lowpc;
71266114Sdes	u_long highpc;
72266114Sdes{
73266114Sdes	register int o;
74266114Sdes	char *cp;
75266114Sdes	struct gmonparam *p = &_gmonparam;
76266114Sdes
77266114Sdes	/*
78266114Sdes	 * round lowpc and highpc to multiples of the density we're using
79266114Sdes	 * so the rest of the scaling (here and in gprof) stays in ints.
80266114Sdes	 */
81266114Sdes	p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
82266114Sdes	p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
83266114Sdes	p->textsize = p->highpc - p->lowpc;
84266114Sdes	p->kcountsize = p->textsize / HISTFRACTION;
85266114Sdes	p->hashfraction = HASHFRACTION;
86266114Sdes	p->fromssize = p->textsize / p->hashfraction;
87266114Sdes	p->tolimit = p->textsize * ARCDENSITY / 100;
88266114Sdes	if (p->tolimit < MINARCS)
89266114Sdes		p->tolimit = MINARCS;
90266114Sdes	else if (p->tolimit > MAXARCS)
91266114Sdes		p->tolimit = MAXARCS;
92266114Sdes	p->tossize = p->tolimit * sizeof(struct tostruct);
93266114Sdes
94266114Sdes	cp = sbrk(p->kcountsize + p->fromssize + p->tossize);
95266114Sdes	if (cp == (char *)-1) {
96266114Sdes		ERR("monstartup: out of memory\n");
97266114Sdes		return;
98266114Sdes	}
99266114Sdes#ifdef notdef
100266114Sdes	bzero(cp, p->kcountsize + p->fromssize + p->tossize);
101266114Sdes#endif
102266114Sdes	p->tos = (struct tostruct *)cp;
103266114Sdes	cp += p->tossize;
104266114Sdes	p->kcount = (u_short *)cp;
105266114Sdes	cp += p->kcountsize;
106266114Sdes	p->froms = (u_short *)cp;
107266114Sdes
108266114Sdes	minbrk = sbrk(0);
109266114Sdes	p->tos[0].link = 0;
110266114Sdes
111266114Sdes	o = p->highpc - p->lowpc;
112266114Sdes	if (p->kcountsize < o) {
113266114Sdes#ifndef notdef
114266114Sdes		s_scale = ((float)p->kcountsize / o ) * SCALE_1_TO_1;
115266114Sdes#else /* avoid floating point */
116266114Sdes		int quot = o / p->kcountsize;
117266114Sdes
118266114Sdes		if (quot >= 0x10000)
119266114Sdes			s_scale = 1;
120266114Sdes		else if (quot >= 0x100)
121266114Sdes			s_scale = 0x10000 / quot;
122266114Sdes		else if (o >= 0x800000)
123266114Sdes			s_scale = 0x1000000 / (o / (p->kcountsize >> 8));
124266114Sdes		else
125266114Sdes			s_scale = 0x1000000 / ((o << 8) / p->kcountsize);
126266114Sdes#endif
127266114Sdes	} else
128266114Sdes		s_scale = SCALE_1_TO_1;
129266114Sdes
130266114Sdes	moncontrol(1);
131266114Sdes}
132266114Sdes
133266114Sdesvoid
134266114Sdes_mcleanup()
135266114Sdes{
136266114Sdes	int fd;
137266114Sdes	int fromindex;
138266114Sdes	int endfrom;
139266114Sdes	u_long frompc;
140266114Sdes	int toindex;
141266114Sdes	struct rawarc rawarc;
142266114Sdes	struct gmonparam *p = &_gmonparam;
143266114Sdes	struct gmonhdr gmonhdr, *hdr;
144266114Sdes	struct clockinfo clockinfo;
145266114Sdes	int mib[2];
146266114Sdes	size_t size;
147266114Sdes	char *profdir;
148266114Sdes	char *proffile;
149266114Sdes	char  buf[PATH_MAX];
150266114Sdes#ifdef DEBUG
151266114Sdes	int log, len;
152238106Sdes	char buf[200];
153238106Sdes#endif
154238106Sdes
155238106Sdes	if (p->state == GMON_PROF_ERROR)
156238106Sdes		ERR("_mcleanup: tos overflow\n");
157238106Sdes
158238106Sdes	size = sizeof(clockinfo);
159266114Sdes	mib[0] = CTL_KERN;
160238106Sdes	mib[1] = KERN_CLOCKRATE;
161238106Sdes	if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) < 0) {
162238106Sdes		/*
163238106Sdes		 * Best guess
164238106Sdes		 */
165238106Sdes		clockinfo.profhz = hertz();
166238106Sdes	} else if (clockinfo.profhz == 0) {
167238106Sdes		if (clockinfo.hz != 0)
168238106Sdes			clockinfo.profhz = clockinfo.hz;
169238106Sdes		else
170238106Sdes			clockinfo.profhz = hertz();
171238106Sdes	}
172238106Sdes
173238106Sdes	moncontrol(0);
174238106Sdes
175238106Sdes	if ((profdir = getenv("PROFDIR")) != NULL) {
176238106Sdes		extern char *__progname;
177238106Sdes		char *s, *t;
178238106Sdes		pid_t pid;
179238106Sdes		long divisor;
180238106Sdes
181238106Sdes		/* If PROFDIR contains a null value, no profiling
182238106Sdes		   output is produced */
183238106Sdes		if (*profdir == '\0') {
184238106Sdes			return;
185238106Sdes		}
186238106Sdes
187238106Sdes		t = buf;
188238106Sdes		s = profdir;
189238106Sdes		while((*t = *s) != '\0') {
190238106Sdes			t++;
191238106Sdes			s++;
192238106Sdes		}
193238106Sdes		*t++ = '/';
194238106Sdes
195238106Sdes		/*
196238106Sdes		 * Copy and convert pid from a pid_t to a string.  For
197238106Sdes		 * best performance, divisor should be initialized to
198238106Sdes		 * the largest power of 10 less than PID_MAX.
199238106Sdes		 */
200238106Sdes		pid = getpid();
201238106Sdes		divisor=10000;
202238106Sdes		while (divisor > pid) divisor /= 10;	/* skip leading zeros */
203238106Sdes		do {
204238106Sdes			*t++ = (pid/divisor) + '0';
205238106Sdes			pid %= divisor;
206238106Sdes		} while (divisor /= 10);
207238106Sdes		*t++ = '.';
208238106Sdes
209238106Sdes		s = __progname;
210238106Sdes		while ((*t++ = *s++) != '\0')
211238106Sdes			;
212238106Sdes
213238106Sdes		proffile = buf;
214238106Sdes	} else {
215276605Sdes		proffile = "gmon.out";
216276605Sdes	}
217238106Sdes
218238106Sdes	fd = open(proffile , O_CREAT|O_TRUNC|O_WRONLY, 0666);
219238106Sdes	if (fd < 0) {
220238106Sdes		perror( proffile );
221238106Sdes		return;
222238106Sdes	}
223238106Sdes#ifdef DEBUG
224238106Sdes	log = open("gmon.log", O_CREAT|O_TRUNC|O_WRONLY, 0664);
225238106Sdes	if (log < 0) {
226238106Sdes		perror("mcount: gmon.log");
227238106Sdes		return;
228238106Sdes	}
229276605Sdes	len = sprintf(buf, "[mcleanup1] kcount 0x%x ssiz %d\n",
230238106Sdes	    p->kcount, p->kcountsize);
231238106Sdes	write(log, buf, len);
232238106Sdes#endif
233238106Sdes	hdr = (struct gmonhdr *)&gmonhdr;
234238106Sdes	hdr->lpc = p->lowpc;
235238106Sdes	hdr->hpc = p->highpc;
236238106Sdes	hdr->ncnt = p->kcountsize + sizeof(gmonhdr);
237238106Sdes	hdr->version = GMONVERSION;
238238106Sdes	hdr->profrate = clockinfo.profhz;
239238106Sdes	write(fd, (char *)hdr, sizeof *hdr);
240238106Sdes	write(fd, p->kcount, p->kcountsize);
241238106Sdes	endfrom = p->fromssize / sizeof(*p->froms);
242238106Sdes	for (fromindex = 0; fromindex < endfrom; fromindex++) {
243238106Sdes		if (p->froms[fromindex] == 0)
244238106Sdes			continue;
245238106Sdes
246238106Sdes		frompc = p->lowpc;
247238106Sdes		frompc += fromindex * p->hashfraction * sizeof(*p->froms);
248238106Sdes		for (toindex = p->froms[fromindex]; toindex != 0;
249238106Sdes		     toindex = p->tos[toindex].link) {
250238106Sdes#ifdef DEBUG
251238106Sdes			len = sprintf(buf,
252238106Sdes			"[mcleanup2] frompc 0x%x selfpc 0x%x count %d\n" ,
253238106Sdes				frompc, p->tos[toindex].selfpc,
254238106Sdes				p->tos[toindex].count);
255238106Sdes			write(log, buf, len);
256238106Sdes#endif
257238106Sdes			rawarc.raw_frompc = frompc;
258238106Sdes			rawarc.raw_selfpc = p->tos[toindex].selfpc;
259276605Sdes			rawarc.raw_count = p->tos[toindex].count;
260238106Sdes			write(fd, &rawarc, sizeof rawarc);
261238106Sdes		}
262238106Sdes	}
263238106Sdes	close(fd);
264238106Sdes}
265238106Sdes
266276605Sdes/*
267238106Sdes * Control profiling
268238106Sdes *	profiling is what mcount checks to see if
269238106Sdes *	all the data structures are ready.
270238106Sdes */
271238106Sdesvoid
272238106Sdesmoncontrol(mode)
273238106Sdes	int mode;
274238106Sdes{
275238106Sdes	struct gmonparam *p = &_gmonparam;
276238106Sdes
277238106Sdes	if (mode) {
278238106Sdes		/* start */
279238106Sdes		profil((char *)p->kcount, p->kcountsize, p->lowpc,
280238106Sdes		    s_scale);
281238106Sdes		p->state = GMON_PROF_ON;
282238106Sdes	} else {
283238106Sdes		/* stop */
284276605Sdes		profil((char *)0, 0, 0, 0);
285238106Sdes		p->state = GMON_PROF_OFF;
286238106Sdes	}
287238106Sdes}
288238106Sdes
289238106Sdes/*
290238106Sdes * discover the tick frequency of the machine
291276605Sdes * if something goes wrong, we return 0, an impossible hertz.
292276605Sdes */
293238106Sdesstatic int
294238106Sdeshertz()
295238106Sdes{
296238106Sdes	struct itimerval tim;
297266114Sdes
298238106Sdes	tim.it_interval.tv_sec = 0;
299238106Sdes	tim.it_interval.tv_usec = 1;
300238106Sdes	tim.it_value.tv_sec = 0;
301238106Sdes	tim.it_value.tv_usec = 0;
302238106Sdes	setitimer(ITIMER_REAL, &tim, 0);
303238106Sdes	setitimer(ITIMER_REAL, 0, &tim);
304238106Sdes	if (tim.it_interval.tv_usec < 2)
305238106Sdes		return(0);
306238106Sdes	return (1000000 / tim.it_interval.tv_usec);
307266114Sdes}
308238106Sdes
309238106Sdes
310238106Sdes