prof_machdep.c revision 163727
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: head/sys/amd64/amd64/prof_machdep.c 163727 2006-10-28 06:38:51Z 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/timerreg.h> 47 48#define CPUTIME_CLOCK_UNINITIALIZED 0 49#define CPUTIME_CLOCK_I8254 1 50#define CPUTIME_CLOCK_TSC 2 51#define CPUTIME_CLOCK_I586_PMC 3 52#define CPUTIME_CLOCK_I8254_SHIFT 7 53 54int cputime_bias = 1; /* initialize for locality of reference */ 55 56static int cputime_clock = CPUTIME_CLOCK_UNINITIALIZED; 57#ifdef I586_PMC_GUPROF 58static u_int cputime_clock_pmc_conf = I586_PMC_GUPROF; 59static int cputime_clock_pmc_init; 60static struct gmonparam saved_gmp; 61#endif 62#endif /* GUPROF */ 63 64#ifdef __GNUCLIKE_ASM 65__asm(" \n\ 66GM_STATE = 0 \n\ 67GMON_PROF_OFF = 3 \n\ 68 \n\ 69 .text \n\ 70 .p2align 4,0x90 \n\ 71 .globl __mcount \n\ 72 .type __mcount,@function \n\ 73__mcount: \n\ 74 # \n\ 75 # Check that we are profiling. Do it early for speed. \n\ 76 # \n\ 77 cmpl $GMON_PROF_OFF,_gmonparam+GM_STATE \n\ 78 je .mcount_exit \n\ 79 # \n\ 80 # __mcount is the same as [.]mcount except the caller \n\ 81 # hasn't changed the stack except to call here, so the \n\ 82 # caller's raddr is above our raddr. \n\ 83 # \n\ 84 pushq %rax \n\ 85 pushq %rdx \n\ 86 pushq %rcx \n\ 87 pushq %rsi \n\ 88 pushq %rdi \n\ 89 pushq %r8 \n\ 90 pushq %r9 \n\ 91 movq 7*8+8(%rsp),%rdi \n\ 92 jmp .got_frompc \n\ 93 \n\ 94 .p2align 4,0x90 \n\ 95 .globl .mcount \n\ 96.mcount: \n\ 97 .globl __cyg_profile_func_enter \n\ 98__cyg_profile_func_enter: \n\ 99 cmpl $GMON_PROF_OFF,_gmonparam+GM_STATE \n\ 100 je .mcount_exit \n\ 101 # \n\ 102 # The caller's stack frame has already been built, so \n\ 103 # %rbp is the caller's frame pointer. The caller's \n\ 104 # raddr is in the caller's frame following the caller's \n\ 105 # caller's frame pointer. \n\ 106 # \n\ 107 pushq %rax \n\ 108 pushq %rdx \n\ 109 pushq %rcx \n\ 110 pushq %rsi \n\ 111 pushq %rdi \n\ 112 pushq %r8 \n\ 113 pushq %r9 \n\ 114 movq 8(%rbp),%rdi \n\ 115.got_frompc: \n\ 116 # \n\ 117 # Our raddr is the caller's pc. \n\ 118 # \n\ 119 movq 7*8(%rsp),%rsi \n\ 120 \n\ 121 pushfq \n\ 122 cli \n\ 123 call mcount \n\ 124 popfq \n\ 125 popq %r9 \n\ 126 popq %r8 \n\ 127 popq %rdi \n\ 128 popq %rsi \n\ 129 popq %rcx \n\ 130 popq %rdx \n\ 131 popq %rax \n\ 132.mcount_exit: \n\ 133 ret \n\ 134"); 135#else /* !__GNUCLIKE_ASM */ 136#error "this file needs to be ported to your compiler" 137#endif /* __GNUCLIKE_ASM */ 138 139#ifdef GUPROF 140/* 141 * [.]mexitcount saves the return register(s), loads selfpc and calls 142 * mexitcount(selfpc) to do the work. Someday it should be in a machine 143 * dependent file together with cputime(), __mcount and [.]mcount. cputime() 144 * can't just be put in machdep.c because it has to be compiled without -pg. 145 */ 146#ifdef __GNUCLIKE_ASM 147__asm(" \n\ 148 .text \n\ 149# \n\ 150# Dummy label to be seen when gprof -u hides [.]mexitcount. \n\ 151# \n\ 152 .p2align 4,0x90 \n\ 153 .globl __mexitcount \n\ 154 .type __mexitcount,@function \n\ 155__mexitcount: \n\ 156 nop \n\ 157 \n\ 158GMON_PROF_HIRES = 4 \n\ 159 \n\ 160 .p2align 4,0x90 \n\ 161 .globl .mexitcount \n\ 162.mexitcount: \n\ 163 .globl __cyg_profile_func_exit \n\ 164__cyg_profile_func_exit: \n\ 165 cmpl $GMON_PROF_HIRES,_gmonparam+GM_STATE \n\ 166 jne .mexitcount_exit \n\ 167 pushq %rax \n\ 168 pushq %rdx \n\ 169 pushq %rcx \n\ 170 pushq %rsi \n\ 171 pushq %rdi \n\ 172 pushq %r8 \n\ 173 pushq %r9 \n\ 174 movq 7*8(%rsp),%rdi \n\ 175 pushfq \n\ 176 cli \n\ 177 call mexitcount \n\ 178 popfq \n\ 179 popq %r9 \n\ 180 popq %r8 \n\ 181 popq %rdi \n\ 182 popq %rsi \n\ 183 popq %rcx \n\ 184 popq %rdx \n\ 185 popq %rax \n\ 186.mexitcount_exit: \n\ 187 ret \n\ 188"); 189#endif /* __GNUCLIKE_ASM */ 190 191/* 192 * Return the time elapsed since the last call. The units are machine- 193 * dependent. 194 */ 195int 196cputime() 197{ 198 u_int count; 199 int delta; 200#if (defined(I586_CPU) || defined(I686_CPU)) && !defined(SMP) && \ 201 defined(PERFMON) && defined(I586_PMC_GUPROF) 202 u_quad_t event_count; 203#endif 204 u_char high, low; 205 static u_int prev_count; 206 207#ifndef SMP 208 if (cputime_clock == CPUTIME_CLOCK_TSC) { 209 /* 210 * Scale the TSC a little to make cputime()'s frequency 211 * fit in an int, assuming that the TSC frequency fits 212 * in a u_int. Use a fixed scale since dynamic scaling 213 * would be slower and we can't really use the low bit 214 * of precision. 215 */ 216 count = (u_int)rdtsc() & ~1u; 217 delta = (int)(count - prev_count) >> 1; 218 prev_count = count; 219 return (delta); 220 } 221#if defined(PERFMON) && defined(I586_PMC_GUPROF) 222 if (cputime_clock == CPUTIME_CLOCK_I586_PMC) { 223 /* 224 * XXX permon_read() should be inlined so that the 225 * perfmon module doesn't need to be compiled with 226 * profiling disabled and so that it is fast. 227 */ 228 perfmon_read(0, &event_count); 229 230 count = (u_int)event_count; 231 delta = (int)(count - prev_count); 232 prev_count = count; 233 return (delta); 234 } 235#endif /* PERFMON && I586_PMC_GUPROF */ 236#endif /* !SMP */ 237 238 /* 239 * Read the current value of the 8254 timer counter 0. 240 */ 241 outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 242 low = inb(TIMER_CNTR0); 243 high = inb(TIMER_CNTR0); 244 count = ((high << 8) | low) << CPUTIME_CLOCK_I8254_SHIFT; 245 246 /* 247 * The timer counts down from TIMER_CNTR0_MAX to 0 and then resets. 248 * While profiling is enabled, this routine is called at least twice 249 * per timer reset (for mcounting and mexitcounting hardclock()), 250 * so at most one reset has occurred since the last call, and one 251 * has occurred iff the current count is larger than the previous 252 * count. This allows counter underflow to be detected faster 253 * than in microtime(). 254 */ 255 delta = prev_count - count; 256 prev_count = count; 257 if ((int) delta <= 0) 258 return (delta + (timer0_max_count << CPUTIME_CLOCK_I8254_SHIFT)); 259 return (delta); 260} 261 262static int 263sysctl_machdep_cputime_clock(SYSCTL_HANDLER_ARGS) 264{ 265 int clock; 266 int error; 267#if defined(PERFMON) && defined(I586_PMC_GUPROF) 268 int event; 269 struct pmc pmc; 270#endif 271 272 clock = cputime_clock; 273#if defined(PERFMON) && defined(I586_PMC_GUPROF) 274 if (clock == CPUTIME_CLOCK_I586_PMC) { 275 pmc.pmc_val = cputime_clock_pmc_conf; 276 clock += pmc.pmc_event; 277 } 278#endif 279 error = sysctl_handle_opaque(oidp, &clock, sizeof clock, req); 280 if (error == 0 && req->newptr != NULL) { 281#if defined(PERFMON) && defined(I586_PMC_GUPROF) 282 if (clock >= CPUTIME_CLOCK_I586_PMC) { 283 event = clock - CPUTIME_CLOCK_I586_PMC; 284 if (event >= 256) 285 return (EINVAL); 286 pmc.pmc_num = 0; 287 pmc.pmc_event = event; 288 pmc.pmc_unit = 0; 289 pmc.pmc_flags = PMCF_E | PMCF_OS | PMCF_USR; 290 pmc.pmc_mask = 0; 291 cputime_clock_pmc_conf = pmc.pmc_val; 292 cputime_clock = CPUTIME_CLOCK_I586_PMC; 293 } else 294#endif 295 { 296 if (clock < 0 || clock >= CPUTIME_CLOCK_I586_PMC) 297 return (EINVAL); 298 cputime_clock = clock; 299 } 300 } 301 return (error); 302} 303 304SYSCTL_PROC(_machdep, OID_AUTO, cputime_clock, CTLTYPE_INT | CTLFLAG_RW, 305 0, sizeof(u_int), sysctl_machdep_cputime_clock, "I", ""); 306 307/* 308 * The start and stop routines need not be here since we turn off profiling 309 * before calling them. They are here for convenience. 310 */ 311 312void 313startguprof(gp) 314 struct gmonparam *gp; 315{ 316 if (cputime_clock == CPUTIME_CLOCK_UNINITIALIZED) { 317 cputime_clock = CPUTIME_CLOCK_I8254; 318#ifndef SMP 319 if (tsc_freq != 0) 320 cputime_clock = CPUTIME_CLOCK_TSC; 321#endif 322 } 323 gp->profrate = timer_freq << CPUTIME_CLOCK_I8254_SHIFT; 324#ifndef SMP 325 if (cputime_clock == CPUTIME_CLOCK_TSC) 326 gp->profrate = tsc_freq >> 1; 327#if defined(PERFMON) && defined(I586_PMC_GUPROF) 328 else if (cputime_clock == CPUTIME_CLOCK_I586_PMC) { 329 if (perfmon_avail() && 330 perfmon_setup(0, cputime_clock_pmc_conf) == 0) { 331 if (perfmon_start(0) != 0) 332 perfmon_fini(0); 333 else { 334 /* XXX 1 event == 1 us. */ 335 gp->profrate = 1000000; 336 337 saved_gmp = *gp; 338 339 /* Zap overheads. They are invalid. */ 340 gp->cputime_overhead = 0; 341 gp->mcount_overhead = 0; 342 gp->mcount_post_overhead = 0; 343 gp->mcount_pre_overhead = 0; 344 gp->mexitcount_overhead = 0; 345 gp->mexitcount_post_overhead = 0; 346 gp->mexitcount_pre_overhead = 0; 347 348 cputime_clock_pmc_init = TRUE; 349 } 350 } 351 } 352#endif /* PERFMON && I586_PMC_GUPROF */ 353#endif /* !SMP */ 354 cputime_bias = 0; 355 cputime(); 356} 357 358void 359stopguprof(gp) 360 struct gmonparam *gp; 361{ 362#if defined(PERFMON) && defined(I586_PMC_GUPROF) 363 if (cputime_clock_pmc_init) { 364 *gp = saved_gmp; 365 perfmon_fini(0); 366 cputime_clock_pmc_init = FALSE; 367 } 368#endif 369} 370#endif /* GUPROF */ 371