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