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