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