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