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