1 /*-
2 * Copyright (c) 1996 Bruce D. Evans.
3 * 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 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD: releng/5.3/sys/amd64/amd64/prof_machdep.c 129744 2004-05-26 09:43:38Z bde $");
29
30 #ifdef GUPROF
31 #if 0
32 #include "opt_i586_guprof.h"
33 #include "opt_perfmon.h"
34 #endif
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/gmon.h>
39 #include <sys/kernel.h>
40 #include <sys/sysctl.h>
41
42 #include <machine/clock.h>
43 #if 0
44 #include <machine/perfmon.h>
45 #endif
46 #include <machine/profile.h>
47 #undef MCOUNT
48 #endif
49
50 #include <machine/asmacros.h>
51
52 #ifdef PC98
53 #include <pc98/pc98/pc98.h>
54 #else
55 #include <i386/isa/isa.h>
56 #endif
57 #include <i386/isa/timerreg.h>
58
59 #ifdef GUPROF
60 #define CPUTIME_CLOCK_UNINITIALIZED 0
61 #define CPUTIME_CLOCK_I8254 1
62 #define CPUTIME_CLOCK_TSC 2
63 #define CPUTIME_CLOCK_I586_PMC 3
64 #define CPUTIME_CLOCK_I8254_SHIFT 7
65
66 int cputime_bias = 1; /* initialize for locality of reference */
67
68 static int cputime_clock = CPUTIME_CLOCK_UNINITIALIZED;
69 #ifdef I586_PMC_GUPROF
70 static u_int cputime_clock_pmc_conf = I586_PMC_GUPROF;
71 static int cputime_clock_pmc_init;
72 static struct gmonparam saved_gmp;
73 #endif
74 #endif /* GUPROF */
75
76 #if defined(__GNUC__) || defined(__INTEL_COMPILER)
77 __asm(" \n\
78 GM_STATE = 0 \n\
79 GMON_PROF_OFF = 3 \n\
80 \n\
81 .text \n\
82 .p2align 4,0x90 \n\
83 .globl __mcount \n\
84 .type __mcount,@function \n\
85 __mcount: \n\
86 # \n\
87 # Check that we are profiling. Do it early for speed. \n\
88 # \n\
89 cmpl $GMON_PROF_OFF," __XSTRING(CNAME(_gmonparam)) "+GM_STATE \n\
90 je .mcount_exit \n\
91 # \n\
92 # __mcount is the same as [.]mcount except the caller \n\
93 # hasn't changed the stack except to call here, so the \n\
94 # caller's raddr is above our raddr. \n\
95 # \n\
96 pushq %rax \n\
97 pushq %rdx \n\
98 pushq %rcx \n\
99 pushq %rsi \n\
100 pushq %rdi \n\
101 pushq %r8 \n\
102 pushq %r9 \n\
103 movq 7*8+8(%rsp),%rdi \n\
104 jmp .got_frompc \n\
105 \n\
106 .p2align 4,0x90 \n\
107 .globl " __XSTRING(HIDENAME(mcount)) " \n\
108 " __XSTRING(HIDENAME(mcount)) ": \n\
109 .globl __cyg_profile_func_enter \n\
110 __cyg_profile_func_enter: \n\
111 cmpl $GMON_PROF_OFF," __XSTRING(CNAME(_gmonparam)) "+GM_STATE \n\
112 je .mcount_exit \n\
113 # \n\
114 # The caller's stack frame has already been built, so \n\
115 # %rbp is the caller's frame pointer. The caller's \n\
116 # raddr is in the caller's frame following the caller's \n\
117 # caller's frame pointer. \n\
118 # \n\
119 pushq %rax \n\
120 pushq %rdx \n\
121 pushq %rcx \n\
122 pushq %rsi \n\
123 pushq %rdi \n\
124 pushq %r8 \n\
125 pushq %r9 \n\
126 movq 8(%rbp),%rdi \n\
127 .got_frompc: \n\
128 # \n\
129 # Our raddr is the caller's pc. \n\
130 # \n\
131 movq 7*8(%rsp),%rsi \n\
132 \n\
133 pushfq \n\
134 cli \n\
135 call " __XSTRING(CNAME(mcount)) " \n\
136 popfq \n\
137 popq %r9 \n\
138 popq %r8 \n\
139 popq %rdi \n\
140 popq %rsi \n\
141 popq %rcx \n\
142 popq %rdx \n\
143 popq %rax \n\
144 .mcount_exit: \n\
145 ret \n\
146 ");
147 #else /* !(__GNUC__ || __INTEL_COMPILER) */
148 #error
149 #endif /* __GNUC__ || __INTEL_COMPILER */
150
151 #ifdef GUPROF
152 /*
153 * [.]mexitcount saves the return register(s), loads selfpc and calls
154 * mexitcount(selfpc) to do the work. Someday it should be in a machine
155 * dependent file together with cputime(), __mcount and [.]mcount. cputime()
156 * can't just be put in machdep.c because it has to be compiled without -pg.
157 */
158 #if defined(__GNUC__) || defined(__INTEL_COMPILER)
159 __asm(" \n\
160 .text \n\
161 # \n\
162 # Dummy label to be seen when gprof -u hides [.]mexitcount. \n\
163 # \n\
164 .p2align 4,0x90 \n\
165 .globl __mexitcount \n\
166 .type __mexitcount,@function \n\
167 __mexitcount: \n\
168 nop \n\
169 \n\
170 GMON_PROF_HIRES = 4 \n\
171 \n\
172 .p2align 4,0x90 \n\
173 .globl " __XSTRING(HIDENAME(mexitcount)) " \n\
174 " __XSTRING(HIDENAME(mexitcount)) ": \n\
175 .globl __cyg_profile_func_exit \n\
176 __cyg_profile_func_exit: \n\
177 cmpl $GMON_PROF_HIRES," __XSTRING(CNAME(_gmonparam)) "+GM_STATE \n\
178 jne .mexitcount_exit \n\
179 pushq %rax \n\
180 pushq %rdx \n\
181 pushq %rcx \n\
182 pushq %rsi \n\
183 pushq %rdi \n\
184 pushq %r8 \n\
185 pushq %r9 \n\
186 movq 7*8(%rsp),%rdi \n\
187 pushfq \n\
188 cli \n\
189 call " __XSTRING(CNAME(mexitcount)) " \n\
190 popfq \n\
191 popq %r9 \n\
192 popq %r8 \n\
193 popq %rdi \n\
194 popq %rsi \n\
195 popq %rcx \n\
196 popq %rdx \n\
197 popq %rax \n\
198 .mexitcount_exit: \n\
199 ret \n\
200 ");
201 #else /* !(__GNUC__ || __INTEL_COMPILER) */
202 #error
203 #endif /* __GNUC__ || __INTEL_COMPILER */
204
205 /*
206 * Return the time elapsed since the last call. The units are machine-
207 * dependent.
208 */
209 int
210 cputime()
211 {
212 u_int count;
213 int delta;
214 #if (defined(I586_CPU) || defined(I686_CPU)) && !defined(SMP) && \
215 defined(PERFMON) && defined(I586_PMC_GUPROF)
216 u_quad_t event_count;
217 #endif
218 u_char high, low;
219 static u_int prev_count;
220
221 #ifndef SMP
222 if (cputime_clock == CPUTIME_CLOCK_TSC) {
223 /*
224 * Scale the TSC a little to make cputime()'s frequency
225 * fit in an int, assuming that the TSC frequency fits
226 * in a u_int. Use a fixed scale since dynamic scaling
227 * would be slower and we can't really use the low bit
228 * of precision.
229 */
230 count = (u_int)rdtsc() & ~1u;
231 delta = (int)(count - prev_count) >> 1;
232 prev_count = count;
233 return (delta);
234 }
235 #if defined(PERFMON) && defined(I586_PMC_GUPROF)
236 if (cputime_clock == CPUTIME_CLOCK_I586_PMC) {
237 /*
238 * XXX permon_read() should be inlined so that the
239 * perfmon module doesn't need to be compiled with
240 * profiling disabled and so that it is fast.
241 */
242 perfmon_read(0, &event_count);
243
244 count = (u_int)event_count;
245 delta = (int)(count - prev_count);
246 prev_count = count;
247 return (delta);
248 }
249 #endif /* PERFMON && I586_PMC_GUPROF */
250 #endif /* !SMP */
251
252 /*
253 * Read the current value of the 8254 timer counter 0.
254 */
255 outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
256 low = inb(TIMER_CNTR0);
257 high = inb(TIMER_CNTR0);
258 count = ((high << 8) | low) << CPUTIME_CLOCK_I8254_SHIFT;
259
260 /*
261 * The timer counts down from TIMER_CNTR0_MAX to 0 and then resets.
262 * While profiling is enabled, this routine is called at least twice
263 * per timer reset (for mcounting and mexitcounting hardclock()),
264 * so at most one reset has occurred since the last call, and one
265 * has occurred iff the current count is larger than the previous
266 * count. This allows counter underflow to be detected faster
267 * than in microtime().
268 */
269 delta = prev_count - count;
270 prev_count = count;
271 if ((int) delta <= 0)
272 return (delta + (timer0_max_count << CPUTIME_CLOCK_I8254_SHIFT));
273 return (delta);
274 }
275
276 static int
277 sysctl_machdep_cputime_clock(SYSCTL_HANDLER_ARGS)
278 {
279 int clock;
280 int error;
281 #if defined(PERFMON) && defined(I586_PMC_GUPROF)
282 int event;
283 struct pmc pmc;
284 #endif
285
286 clock = cputime_clock;
287 #if defined(PERFMON) && defined(I586_PMC_GUPROF)
288 if (clock == CPUTIME_CLOCK_I586_PMC) {
289 pmc.pmc_val = cputime_clock_pmc_conf;
290 clock += pmc.pmc_event;
291 }
292 #endif
293 error = sysctl_handle_opaque(oidp, &clock, sizeof clock, req);
294 if (error == 0 && req->newptr != NULL) {
295 #if defined(PERFMON) && defined(I586_PMC_GUPROF)
296 if (clock >= CPUTIME_CLOCK_I586_PMC) {
297 event = clock - CPUTIME_CLOCK_I586_PMC;
298 if (event >= 256)
299 return (EINVAL);
300 pmc.pmc_num = 0;
301 pmc.pmc_event = event;
302 pmc.pmc_unit = 0;
303 pmc.pmc_flags = PMCF_E | PMCF_OS | PMCF_USR;
304 pmc.pmc_mask = 0;
305 cputime_clock_pmc_conf = pmc.pmc_val;
306 cputime_clock = CPUTIME_CLOCK_I586_PMC;
307 } else
308 #endif
309 {
310 if (clock < 0 || clock >= CPUTIME_CLOCK_I586_PMC)
311 return (EINVAL);
312 cputime_clock = clock;
313 }
314 }
315 return (error);
316 }
317
318 SYSCTL_PROC(_machdep, OID_AUTO, cputime_clock, CTLTYPE_INT | CTLFLAG_RW,
319 0, sizeof(u_int), sysctl_machdep_cputime_clock, "I", "");
320
321 /*
322 * The start and stop routines need not be here since we turn off profiling
323 * before calling them. They are here for convenience.
324 */
325
326 void
327 startguprof(gp)
328 struct gmonparam *gp;
329 {
330 if (cputime_clock == CPUTIME_CLOCK_UNINITIALIZED) {
331 cputime_clock = CPUTIME_CLOCK_I8254;
332 #ifndef SMP
333 if (tsc_freq != 0)
334 cputime_clock = CPUTIME_CLOCK_TSC;
335 #endif
336 }
337 gp->profrate = timer_freq << CPUTIME_CLOCK_I8254_SHIFT;
338 #ifndef SMP
339 if (cputime_clock == CPUTIME_CLOCK_TSC)
340 gp->profrate = tsc_freq >> 1;
341 #if defined(PERFMON) && defined(I586_PMC_GUPROF)
342 else if (cputime_clock == CPUTIME_CLOCK_I586_PMC) {
343 if (perfmon_avail() &&
344 perfmon_setup(0, cputime_clock_pmc_conf) == 0) {
345 if (perfmon_start(0) != 0)
346 perfmon_fini(0);
347 else {
348 /* XXX 1 event == 1 us. */
349 gp->profrate = 1000000;
350
351 saved_gmp = *gp;
352
353 /* Zap overheads. They are invalid. */
354 gp->cputime_overhead = 0;
355 gp->mcount_overhead = 0;
356 gp->mcount_post_overhead = 0;
357 gp->mcount_pre_overhead = 0;
358 gp->mexitcount_overhead = 0;
359 gp->mexitcount_post_overhead = 0;
360 gp->mexitcount_pre_overhead = 0;
361
362 cputime_clock_pmc_init = TRUE;
363 }
364 }
365 }
366 #endif /* PERFMON && I586_PMC_GUPROF */
367 #endif /* !SMP */
368 cputime_bias = 0;
369 cputime();
370 }
371
372 void
373 stopguprof(gp)
374 struct gmonparam *gp;
375 {
376 #if defined(PERFMON) && defined(I586_PMC_GUPROF)
377 if (cputime_clock_pmc_init) {
378 *gp = saved_gmp;
379 perfmon_fini(0);
380 cputime_clock_pmc_init = FALSE;
381 }
382 #endif
383 }
384
385 #else /* !GUPROF */
386 #if defined(__GNUC__) || defined(__INTEL_COMPILER)
387 __asm(" \n\
388 .text \n\
389 .p2align 4,0x90 \n\
390 .globl " __XSTRING(HIDENAME(mexitcount)) " \n\
391 " __XSTRING(HIDENAME(mexitcount)) ": \n\
392 ret \n\
393 ");
394 #else /* !(__GNUC__ || __INTEL_COMPILER) */
395 #error
396 #endif /* __GNUC__ || __INTEL_COMPILER */
397 #endif /* GUPROF */
Cache object: 8c553bd214e214ee1f90dd01e4f27410
|