xref: /freebsd-13-stable/sys/libkern/mcount.c (revision 3bc80996974a61a4223eae4c1ccd47b6ee32a48a)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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 #include <sys/param.h>
34 #include <sys/gmon.h>
35 #ifdef _KERNEL
36 #ifndef GUPROF
37 #include <sys/systm.h>
38 #endif
39 #include <vm/vm.h>
40 #include <vm/vm_param.h>
41 #include <vm/pmap.h>
42 #endif
43 
44 /*
45  * mcount is called on entry to each function compiled with the profiling
46  * switch set.  _mcount(), which is declared in a machine-dependent way
47  * with _MCOUNT_DECL, does the actual work and is either inlined into a
48  * C routine or called by an assembly stub.  In any case, this magic is
49  * taken care of by the MCOUNT definition in <machine/profile.h>.
50  *
51  * _mcount updates data structures that represent traversals of the
52  * program's call graph edges.  frompc and selfpc are the return
53  * address and function address that represents the given call graph edge.
54  *
55  * Note: the original BSD code used the same variable (frompcindex) for
56  * both frompcindex and frompc.  Any reasonable, modern compiler will
57  * perform this optimization.
58  */
59 /* _mcount; may be static, inline, etc */
_MCOUNT_DECL(uintfptr_t frompc,uintfptr_t selfpc)60 _MCOUNT_DECL(uintfptr_t frompc, uintfptr_t selfpc)
61 {
62 #ifdef GUPROF
63 	int delta;
64 #endif
65 	fptrdiff_t frompci;
66 	u_short *frompcindex;
67 	struct tostruct *top, *prevtop;
68 	struct gmonparam *p;
69 	long toindex;
70 #ifdef _KERNEL
71 	MCOUNT_DECL(s)
72 #endif
73 
74 	p = &_gmonparam;
75 #ifndef GUPROF			/* XXX */
76 	/*
77 	 * check that we are profiling
78 	 * and that we aren't recursively invoked.
79 	 */
80 	if (p->state != GMON_PROF_ON)
81 		return;
82 #endif
83 #ifdef _KERNEL
84 	MCOUNT_ENTER(s);
85 #else
86 	p->state = GMON_PROF_BUSY;
87 #endif
88 
89 #ifdef _KERNEL
90 	/* De-relocate any addresses in a (single) trampoline. */
91 #ifdef MCOUNT_DETRAMP
92 	MCOUNT_DETRAMP(frompc);
93 	MCOUNT_DETRAMP(selfpc);
94 #endif
95 	/*
96 	 * When we are called from an exception handler, frompc may be
97 	 * a user address.  Convert such frompc's to some representation
98 	 * in kernel address space.
99 	 */
100 #ifdef MCOUNT_FROMPC_USER
101 	frompc = MCOUNT_FROMPC_USER(frompc);
102 #elif defined(MCOUNT_USERPC)
103 	/*
104 	 * For separate address spaces, we can only guess that addresses
105 	 * in the range known to us are actually kernel addresses.  Outside
106 	 * of this range, conerting to the user address is fail-safe.
107 	 */
108 	if (frompc < p->lowpc || frompc - p->lowpc >= p->textsize)
109 		frompc = MCOUNT_USERPC;
110 #endif
111 #endif /* _KERNEL */
112 
113 	frompci = frompc - p->lowpc;
114 	if (frompci >= p->textsize)
115 		goto done;
116 
117 #ifdef GUPROF
118 	if (p->state == GMON_PROF_HIRES) {
119 		/*
120 		 * Count the time since cputime() was previously called
121 		 * against `frompc'.  Compensate for overheads.
122 		 *
123 		 * cputime() sets its prev_count variable to the count when
124 		 * it is called.  This in effect starts a counter for
125 		 * the next period of execution (normally from now until
126 		 * the next call to mcount() or mexitcount()).  We set
127 		 * cputime_bias to compensate for our own overhead.
128 		 *
129 		 * We use the usual sampling counters since they can be
130 		 * located efficiently.  4-byte counters are usually
131 		 * necessary.  gprof will add up the scattered counts
132 		 * just like it does for statistical profiling.  All
133 		 * counts are signed so that underflow in the subtractions
134 		 * doesn't matter much (negative counts are normally
135 		 * compensated for by larger counts elsewhere).  Underflow
136 		 * shouldn't occur, but may be caused by slightly wrong
137 		 * calibrations or from not clearing cputime_bias.
138 		 */
139 		delta = cputime() - cputime_bias - p->mcount_pre_overhead;
140 		cputime_bias = p->mcount_post_overhead;
141 		KCOUNT(p, frompci) += delta;
142 		*p->cputime_count += p->cputime_overhead;
143 		*p->mcount_count += p->mcount_overhead;
144 	}
145 #endif /* GUPROF */
146 
147 #ifdef _KERNEL
148 	/*
149 	 * When we are called from an exception handler, frompc is faked
150 	 * to be for where the exception occurred.  We've just solidified
151 	 * the count for there.  Now convert frompci to an index that
152 	 * represents the kind of exception so that interruptions appear
153 	 * in the call graph as calls from those index instead of calls
154 	 * from all over.
155 	 */
156 	frompc = MCOUNT_FROMPC_INTR(selfpc);
157 	if ((frompc - p->lowpc) < p->textsize)
158 		frompci = frompc - p->lowpc;
159 #endif
160 
161 	/*
162 	 * check that frompc is a reasonable pc value.
163 	 * for example:	signal catchers get called from the stack,
164 	 *		not from text space.  too bad.
165 	 */
166 	if (frompci >= p->textsize)
167 		goto done;
168 
169 	frompcindex = &p->froms[frompci / (p->hashfraction * sizeof(*p->froms))];
170 	toindex = *frompcindex;
171 	if (toindex == 0) {
172 		/*
173 		 *	first time traversing this arc
174 		 */
175 		toindex = ++p->tos[0].link;
176 		if (toindex >= p->tolimit)
177 			/* halt further profiling */
178 			goto overflow;
179 
180 		*frompcindex = toindex;
181 		top = &p->tos[toindex];
182 		top->selfpc = selfpc;
183 		top->count = 1;
184 		top->link = 0;
185 		goto done;
186 	}
187 	top = &p->tos[toindex];
188 	if (top->selfpc == selfpc) {
189 		/*
190 		 * arc at front of chain; usual case.
191 		 */
192 		top->count++;
193 		goto done;
194 	}
195 	/*
196 	 * have to go looking down chain for it.
197 	 * top points to what we are looking at,
198 	 * prevtop points to previous top.
199 	 * we know it is not at the head of the chain.
200 	 */
201 	for (; /* goto done */; ) {
202 		if (top->link == 0) {
203 			/*
204 			 * top is end of the chain and none of the chain
205 			 * had top->selfpc == selfpc.
206 			 * so we allocate a new tostruct
207 			 * and link it to the head of the chain.
208 			 */
209 			toindex = ++p->tos[0].link;
210 			if (toindex >= p->tolimit)
211 				goto overflow;
212 
213 			top = &p->tos[toindex];
214 			top->selfpc = selfpc;
215 			top->count = 1;
216 			top->link = *frompcindex;
217 			*frompcindex = toindex;
218 			goto done;
219 		}
220 		/*
221 		 * otherwise, check the next arc on the chain.
222 		 */
223 		prevtop = top;
224 		top = &p->tos[top->link];
225 		if (top->selfpc == selfpc) {
226 			/*
227 			 * there it is.
228 			 * increment its count
229 			 * move it to the head of the chain.
230 			 */
231 			top->count++;
232 			toindex = prevtop->link;
233 			prevtop->link = top->link;
234 			top->link = *frompcindex;
235 			*frompcindex = toindex;
236 			goto done;
237 		}
238 	}
239 done:
240 #ifdef _KERNEL
241 	MCOUNT_EXIT(s);
242 #else
243 	p->state = GMON_PROF_ON;
244 #endif
245 	return;
246 overflow:
247 	p->state = GMON_PROF_ERROR;
248 #ifdef _KERNEL
249 	MCOUNT_EXIT(s);
250 #endif
251 	return;
252 }
253 
254 /*
255  * Actual definition of mcount function.  Defined in <machine/profile.h>,
256  * which is included by <sys/gmon.h>.
257  */
258 MCOUNT
259 
260 #ifdef GUPROF
261 void
mexitcount(uintfptr_t selfpc)262 mexitcount(uintfptr_t selfpc)
263 {
264 	struct gmonparam *p;
265 	uintfptr_t selfpcdiff;
266 
267 	p = &_gmonparam;
268 #ifdef MCOUNT_DETRAMP
269 	MCOUNT_DETRAMP(selfpc);
270 #endif
271 	selfpcdiff = selfpc - (uintfptr_t)p->lowpc;
272 	if (selfpcdiff < p->textsize) {
273 		int delta;
274 
275 		/*
276 		 * Count the time since cputime() was previously called
277 		 * against `selfpc'.  Compensate for overheads.
278 		 */
279 		delta = cputime() - cputime_bias - p->mexitcount_pre_overhead;
280 		cputime_bias = p->mexitcount_post_overhead;
281 		KCOUNT(p, selfpcdiff) += delta;
282 		*p->cputime_count += p->cputime_overhead;
283 		*p->mexitcount_count += p->mexitcount_overhead;
284 	}
285 }
286 
287 #ifndef __GNUCLIKE_ASM
288 #error "This file uses null asms to prevent timing loops being optimized away."
289 #endif
290 
291 void
empty_loop(void)292 empty_loop(void)
293 {
294 	int i;
295 
296 	for (i = 0; i < CALIB_SCALE; i++)
297 		__asm __volatile("");
298 }
299 
300 void
nullfunc(void)301 nullfunc(void)
302 {
303 	__asm __volatile("");
304 }
305 
306 void
nullfunc_loop(void)307 nullfunc_loop(void)
308 {
309 	int i;
310 
311 	for (i = 0; i < CALIB_SCALE; i++)
312 		nullfunc();
313 }
314 #endif /* GUPROF */
315