mcount.c revision 145959
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(_KERNEL) && defined(LIBC_SCCS) && !defined(lint)
35static char sccsid[] = "@(#)mcount.c	8.1 (Berkeley) 6/4/93";
36#endif
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/lib/libc/gmon/mcount.c 145959 2005-05-06 07:37:01Z davidxu $");
39
40#include <sys/param.h>
41#include <sys/gmon.h>
42#ifdef _KERNEL
43#include <sys/systm.h>
44#include <vm/vm.h>
45#include <vm/vm_param.h>
46#include <vm/pmap.h>
47void	bintr(void);
48void	btrap(void);
49void	eintr(void);
50void	user(void);
51#endif
52#include <machine/atomic.h>
53
54/*
55 * mcount is called on entry to each function compiled with the profiling
56 * switch set.  _mcount(), which is declared in a machine-dependent way
57 * with _MCOUNT_DECL, does the actual work and is either inlined into a
58 * C routine or called by an assembly stub.  In any case, this magic is
59 * taken care of by the MCOUNT definition in <machine/profile.h>.
60 *
61 * _mcount updates data structures that represent traversals of the
62 * program's call graph edges.  frompc and selfpc are the return
63 * address and function address that represents the given call graph edge.
64 *
65 * Note: the original BSD code used the same variable (frompcindex) for
66 * both frompcindex and frompc.  Any reasonable, modern compiler will
67 * perform this optimization.
68 */
69/* _mcount; may be static, inline, etc */
70_MCOUNT_DECL(uintfptr_t frompc, uintfptr_t selfpc)
71{
72#ifdef GUPROF
73	u_int delta;
74#endif
75	fptrdiff_t frompci;
76	u_short *frompcindex;
77	struct tostruct *top, *prevtop;
78	struct gmonparam *p;
79	long toindex;
80#ifdef _KERNEL
81	MCOUNT_DECL(s)
82#endif
83
84	p = &_gmonparam;
85#ifndef GUPROF			/* XXX */
86	/*
87	 * check that we are profiling
88	 * and that we aren't recursively invoked.
89	 */
90	if (p->state != GMON_PROF_ON)
91		return;
92#endif
93#ifdef _KERNEL
94	MCOUNT_ENTER(s);
95#else
96	if (!atomic_cmpset_acq_int(&p->state, GMON_PROF_ON, GMON_PROF_BUSY))
97		return;
98#endif
99	frompci = frompc - p->lowpc;
100
101#ifdef _KERNEL
102	/*
103	 * When we are called from an exception handler, frompci may be
104	 * for a user address.  Convert such frompci's to the index of
105	 * user() to merge all user counts.
106	 */
107	if (frompci >= p->textsize) {
108		if (frompci + p->lowpc
109		    >= (uintfptr_t)(VM_MAXUSER_ADDRESS + UPAGES * PAGE_SIZE))
110			goto done;
111		frompci = (uintfptr_t)user - p->lowpc;
112		if (frompci >= p->textsize)
113		    goto done;
114	}
115#endif
116
117#ifdef GUPROF
118	if (p->state != GMON_PROF_HIRES)
119		goto skip_guprof_stuff;
120	/*
121	 * Look at the clock and add the count of clock cycles since the
122	 * clock was last looked at to a counter for frompc.  This
123	 * solidifies the count for the function containing frompc and
124	 * effectively starts another clock for the current function.
125	 * The count for the new clock will be solidified when another
126	 * function call is made or the function returns.
127	 *
128	 * We use the usual sampling counters since they can be located
129	 * efficiently.  4-byte counters are usually necessary.
130	 *
131	 * There are many complications for subtracting the profiling
132	 * overheads from the counts for normal functions and adding
133	 * them to the counts for mcount(), mexitcount() and cputime().
134	 * We attempt to handle fractional cycles, but the overheads
135	 * are usually underestimated because they are calibrated for
136	 * a simpler than usual setup.
137	 */
138	delta = cputime() - p->mcount_overhead;
139	p->cputime_overhead_resid += p->cputime_overhead_frac;
140	p->mcount_overhead_resid += p->mcount_overhead_frac;
141	if ((int)delta < 0)
142		*p->mcount_count += delta + p->mcount_overhead
143				    - p->cputime_overhead;
144	else if (delta != 0) {
145		if (p->cputime_overhead_resid >= CALIB_SCALE) {
146			p->cputime_overhead_resid -= CALIB_SCALE;
147			++*p->cputime_count;
148			--delta;
149		}
150		if (delta != 0) {
151			if (p->mcount_overhead_resid >= CALIB_SCALE) {
152				p->mcount_overhead_resid -= CALIB_SCALE;
153				++*p->mcount_count;
154				--delta;
155			}
156			KCOUNT(p, frompci) += delta;
157		}
158		*p->mcount_count += p->mcount_overhead_sub;
159	}
160	*p->cputime_count += p->cputime_overhead;
161skip_guprof_stuff:
162#endif /* GUPROF */
163
164#ifdef _KERNEL
165	/*
166	 * When we are called from an exception handler, frompc is faked
167	 * to be for where the exception occurred.  We've just solidified
168	 * the count for there.  Now convert frompci to the index of btrap()
169	 * for trap handlers and bintr() for interrupt handlers to make
170	 * exceptions appear in the call graph as calls from btrap() and
171	 * bintr() instead of calls from all over.
172	 */
173	if ((uintfptr_t)selfpc >= (uintfptr_t)btrap
174	    && (uintfptr_t)selfpc < (uintfptr_t)eintr) {
175		if ((uintfptr_t)selfpc >= (uintfptr_t)bintr)
176			frompci = (uintfptr_t)bintr - p->lowpc;
177		else
178			frompci = (uintfptr_t)btrap - p->lowpc;
179	}
180#endif
181
182	/*
183	 * check that frompc is a reasonable pc value.
184	 * for example:	signal catchers get called from the stack,
185	 *		not from text space.  too bad.
186	 */
187	if (frompci >= p->textsize)
188		goto done;
189
190	frompcindex = &p->froms[frompci / (p->hashfraction * sizeof(*p->froms))];
191	toindex = *frompcindex;
192	if (toindex == 0) {
193		/*
194		 *	first time traversing this arc
195		 */
196		toindex = ++p->tos[0].link;
197		if (toindex >= p->tolimit)
198			/* halt further profiling */
199			goto overflow;
200
201		*frompcindex = toindex;
202		top = &p->tos[toindex];
203		top->selfpc = selfpc;
204		top->count = 1;
205		top->link = 0;
206		goto done;
207	}
208	top = &p->tos[toindex];
209	if (top->selfpc == selfpc) {
210		/*
211		 * arc at front of chain; usual case.
212		 */
213		top->count++;
214		goto done;
215	}
216	/*
217	 * have to go looking down chain for it.
218	 * top points to what we are looking at,
219	 * prevtop points to previous top.
220	 * we know it is not at the head of the chain.
221	 */
222	for (; /* goto done */; ) {
223		if (top->link == 0) {
224			/*
225			 * top is end of the chain and none of the chain
226			 * had top->selfpc == selfpc.
227			 * so we allocate a new tostruct
228			 * and link it to the head of the chain.
229			 */
230			toindex = ++p->tos[0].link;
231			if (toindex >= p->tolimit)
232				goto overflow;
233
234			top = &p->tos[toindex];
235			top->selfpc = selfpc;
236			top->count = 1;
237			top->link = *frompcindex;
238			*frompcindex = toindex;
239			goto done;
240		}
241		/*
242		 * otherwise, check the next arc on the chain.
243		 */
244		prevtop = top;
245		top = &p->tos[top->link];
246		if (top->selfpc == selfpc) {
247			/*
248			 * there it is.
249			 * increment its count
250			 * move it to the head of the chain.
251			 */
252			top->count++;
253			toindex = prevtop->link;
254			prevtop->link = top->link;
255			top->link = *frompcindex;
256			*frompcindex = toindex;
257			goto done;
258		}
259
260	}
261done:
262#ifdef _KERNEL
263	MCOUNT_EXIT(s);
264#else
265	atomic_store_rel_int(&p->state, GMON_PROF_ON);
266#endif
267	return;
268overflow:
269	atomic_store_rel_int(&p->state, GMON_PROF_ERROR);
270#ifdef _KERNEL
271	MCOUNT_EXIT(s);
272#endif
273	return;
274}
275
276/*
277 * Actual definition of mcount function.  Defined in <machine/profile.h>,
278 * which is included by <sys/gmon.h>.
279 */
280MCOUNT
281
282#ifdef GUPROF
283void
284mexitcount(selfpc)
285	uintfptr_t selfpc;
286{
287	struct gmonparam *p;
288	uintfptr_t selfpcdiff;
289
290	p = &_gmonparam;
291	selfpcdiff = selfpc - (uintfptr_t)p->lowpc;
292	if (selfpcdiff < p->textsize) {
293		u_int delta;
294
295		/*
296		 * Solidify the count for the current function.
297		 */
298		delta = cputime() - p->mexitcount_overhead;
299		p->cputime_overhead_resid += p->cputime_overhead_frac;
300		p->mexitcount_overhead_resid += p->mexitcount_overhead_frac;
301		if ((int)delta < 0)
302			*p->mexitcount_count += delta + p->mexitcount_overhead
303						- p->cputime_overhead;
304		else if (delta != 0) {
305			if (p->cputime_overhead_resid >= CALIB_SCALE) {
306				p->cputime_overhead_resid -= CALIB_SCALE;
307				++*p->cputime_count;
308				--delta;
309			}
310			if (delta != 0) {
311				if (p->mexitcount_overhead_resid
312				    >= CALIB_SCALE) {
313					p->mexitcount_overhead_resid
314					    -= CALIB_SCALE;
315					++*p->mexitcount_count;
316					--delta;
317				}
318				KCOUNT(p, selfpcdiff) += delta;
319			}
320			*p->mexitcount_count += p->mexitcount_overhead_sub;
321		}
322		*p->cputime_count += p->cputime_overhead;
323	}
324}
325#endif /* GUPROF */
326