mcount.c revision 13107
1/*-
2 * Copyright (c) 1983, 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#if !defined(lint) && !defined(KERNEL) && defined(LIBC_SCCS)
35static char sccsid[] = "@(#)mcount.c	8.1 (Berkeley) 6/4/93";
36#endif
37
38#include <sys/param.h>
39#include <sys/gmon.h>
40#ifdef KERNEL
41#include <sys/systm.h>
42#include <vm/vm.h>
43#include <vm/vm_param.h>
44#include <vm/pmap.h>
45void	bintr __P((void));
46void	btrap __P((void));
47void	eintr __P((void));
48void	user __P((void));
49#endif
50
51/*
52 * mcount is called on entry to each function compiled with the profiling
53 * switch set.  _mcount(), which is declared in a machine-dependent way
54 * with _MCOUNT_DECL, does the actual work and is either inlined into a
55 * C routine or called by an assembly stub.  In any case, this magic is
56 * taken care of by the MCOUNT definition in <machine/profile.h>.
57 *
58 * _mcount updates data structures that represent traversals of the
59 * program's call graph edges.  frompc and selfpc are the return
60 * address and function address that represents the given call graph edge.
61 *
62 * Note: the original BSD code used the same variable (frompcindex) for
63 * both frompcindex and frompc.  Any reasonable, modern compiler will
64 * perform this optimization.
65 */
66_MCOUNT_DECL(frompc, selfpc)	/* _mcount; may be static, inline, etc */
67	register fptrint_t frompc, selfpc;
68{
69#ifdef GUPROF
70	u_int delta;
71#endif
72	register fptrdiff_t frompci;
73	register u_short *frompcindex;
74	register struct tostruct *top, *prevtop;
75	register struct gmonparam *p;
76	register long toindex;
77#ifdef KERNEL
78	register int s;		/* XXX */
79	u_long save_eflags;	/* XXX */
80#endif
81
82	p = &_gmonparam;
83#ifndef GUPROF			/* XXX */
84	/*
85	 * check that we are profiling
86	 * and that we aren't recursively invoked.
87	 */
88	if (p->state != GMON_PROF_ON)
89		return;
90#endif
91#ifdef KERNEL
92	MCOUNT_ENTER;
93#else
94	p->state = GMON_PROF_BUSY;
95#endif
96	frompci = frompc - p->lowpc;
97
98#ifdef KERNEL
99	/*
100	 * When we are called from an exception handler, frompci may be
101	 * for a user address.  Convert such frompci's to the index of
102	 * user() to merge all user counts.
103	 */
104	if (frompci >= p->textsize) {
105		if (frompci + p->lowpc
106		    >= (fptrint_t)(VM_MAXUSER_ADDRESS + UPAGES * NBPG))
107			goto done;
108		frompci = (fptrint_t)user - p->lowpc;
109		if (frompci >= p->textsize)
110		    goto done;
111	}
112#endif /* KERNEL */
113
114#ifdef GUPROF
115	if (p->state != GMON_PROF_HIRES)
116		goto skip_guprof_stuff;
117	/*
118	 * Look at the clock and add the count of clock cycles since the
119	 * clock was last looked at to a counter for frompc.  This
120	 * solidifies the count for the function containing frompc and
121	 * effectively starts another clock for the current function.
122	 * The count for the new clock will be solidified when another
123	 * function call is made or the function returns.
124	 *
125	 * We use the usual sampling counters since they can be located
126	 * efficiently.  4-byte counters are usually necessary.
127	 *
128	 * There are many complications for subtracting the profiling
129	 * overheads from the counts for normal functions and adding
130	 * them to the counts for mcount(), mexitcount() and cputime().
131	 * We attempt to handle fractional cycles, but the overheads
132	 * are usually underestimated because they are calibrated for
133	 * a simpler than usual setup.
134	 */
135	delta = cputime() - p->mcount_overhead;
136	p->cputime_overhead_resid += p->cputime_overhead_frac;
137	p->mcount_overhead_resid += p->mcount_overhead_frac;
138	if ((int)delta < 0)
139		*p->mcount_count += delta + p->mcount_overhead
140				    - p->cputime_overhead;
141	else if (delta != 0) {
142		if (p->cputime_overhead_resid >= CALIB_SCALE) {
143			p->cputime_overhead_resid -= CALIB_SCALE;
144			++*p->cputime_count;
145			--delta;
146		}
147		if (delta != 0) {
148			if (p->mcount_overhead_resid >= CALIB_SCALE) {
149				p->mcount_overhead_resid -= CALIB_SCALE;
150				++*p->mcount_count;
151				--delta;
152			}
153			KCOUNT(p, frompci) += delta;
154		}
155		*p->mcount_count += p->mcount_overhead_sub;
156	}
157	*p->cputime_count += p->cputime_overhead;
158skip_guprof_stuff:
159#endif /* GUPROF */
160
161#ifdef KERNEL
162	/*
163	 * When we are called from an exception handler, frompc is faked
164	 * to be for where the exception occurred.  We've just solidified
165	 * the count for there.  Now convert frompci to the index of btrap()
166	 * for trap handlers and bintr() for interrupt handlers to make
167	 * exceptions appear in the call graph as calls from btrap() and
168	 * bintr() instead of calls from all over.
169	 */
170	if ((fptrint_t)selfpc >= (fptrint_t)btrap
171	    && (fptrint_t)selfpc < (fptrint_t)eintr) {
172		if ((fptrint_t)selfpc >= (fptrint_t)bintr)
173			frompci = (fptrint_t)bintr - p->lowpc;
174		else
175			frompci = (fptrint_t)btrap - p->lowpc;
176	}
177#endif /* KERNEL */
178
179	/*
180	 * check that frompc is a reasonable pc value.
181	 * for example:	signal catchers get called from the stack,
182	 *		not from text space.  too bad.
183	 */
184	if (frompci >= p->textsize)
185		goto done;
186
187	frompcindex = &p->froms[frompci / (p->hashfraction * sizeof(*p->froms))];
188	toindex = *frompcindex;
189	if (toindex == 0) {
190		/*
191		 *	first time traversing this arc
192		 */
193		toindex = ++p->tos[0].link;
194		if (toindex >= p->tolimit)
195			/* halt further profiling */
196			goto overflow;
197
198		*frompcindex = toindex;
199		top = &p->tos[toindex];
200		top->selfpc = selfpc;
201		top->count = 1;
202		top->link = 0;
203		goto done;
204	}
205	top = &p->tos[toindex];
206	if (top->selfpc == selfpc) {
207		/*
208		 * arc at front of chain; usual case.
209		 */
210		top->count++;
211		goto done;
212	}
213	/*
214	 * have to go looking down chain for it.
215	 * top points to what we are looking at,
216	 * prevtop points to previous top.
217	 * we know it is not at the head of the chain.
218	 */
219	for (; /* goto done */; ) {
220		if (top->link == 0) {
221			/*
222			 * top is end of the chain and none of the chain
223			 * had top->selfpc == selfpc.
224			 * so we allocate a new tostruct
225			 * and link it to the head of the chain.
226			 */
227			toindex = ++p->tos[0].link;
228			if (toindex >= p->tolimit)
229				goto overflow;
230
231			top = &p->tos[toindex];
232			top->selfpc = selfpc;
233			top->count = 1;
234			top->link = *frompcindex;
235			*frompcindex = toindex;
236			goto done;
237		}
238		/*
239		 * otherwise, check the next arc on the chain.
240		 */
241		prevtop = top;
242		top = &p->tos[top->link];
243		if (top->selfpc == selfpc) {
244			/*
245			 * there it is.
246			 * increment its count
247			 * move it to the head of the chain.
248			 */
249			top->count++;
250			toindex = prevtop->link;
251			prevtop->link = top->link;
252			top->link = *frompcindex;
253			*frompcindex = toindex;
254			goto done;
255		}
256
257	}
258done:
259#ifdef KERNEL
260	MCOUNT_EXIT;
261#else
262	p->state = GMON_PROF_ON;
263#endif
264	return;
265overflow:
266	p->state = GMON_PROF_ERROR;
267#ifdef KERNEL
268	MCOUNT_EXIT;
269#endif
270	return;
271}
272
273/*
274 * Actual definition of mcount function.  Defined in <machine/profile.h>,
275 * which is included by <sys/gmon.h>.
276 */
277MCOUNT
278
279#ifdef GUPROF
280void
281mexitcount(selfpc)
282	fptrint_t selfpc;
283{
284	struct gmonparam *p;
285	fptrint_t selfpcdiff;
286
287	p = &_gmonparam;
288	selfpcdiff = selfpc - (fptrint_t)p->lowpc;
289	if (selfpcdiff < p->textsize) {
290		u_int delta;
291
292		/*
293		 * Solidify the count for the current function.
294		 */
295		delta = cputime() - p->mexitcount_overhead;
296		p->cputime_overhead_resid += p->cputime_overhead_frac;
297		p->mexitcount_overhead_resid += p->mexitcount_overhead_frac;
298		if ((int)delta < 0)
299			*p->mexitcount_count += delta + p->mexitcount_overhead
300						- p->cputime_overhead;
301		else if (delta != 0) {
302			if (p->cputime_overhead_resid >= CALIB_SCALE) {
303				p->cputime_overhead_resid -= CALIB_SCALE;
304				++*p->cputime_count;
305				--delta;
306			}
307			if (delta != 0) {
308				if (p->mexitcount_overhead_resid
309				    >= CALIB_SCALE) {
310					p->mexitcount_overhead_resid
311					    -= CALIB_SCALE;
312					++*p->mexitcount_count;
313					--delta;
314				}
315				KCOUNT(p, selfpcdiff) += delta;
316			}
317			*p->mexitcount_count += p->mexitcount_overhead_sub;
318		}
319		*p->cputime_count += p->cputime_overhead;
320	}
321}
322#endif /* GUPROF */
323