diff options
Diffstat (limited to 'sys/i386/isa/prof_machdep.c')
-rw-r--r-- | sys/i386/isa/prof_machdep.c | 168 |
1 files changed, 154 insertions, 14 deletions
diff --git a/sys/i386/isa/prof_machdep.c b/sys/i386/isa/prof_machdep.c index f140b84..62c8df5 100644 --- a/sys/i386/isa/prof_machdep.c +++ b/sys/i386/isa/prof_machdep.c @@ -1,17 +1,64 @@ -/* - * NEED A COPYRIGHT NOPTICE HERE +/*- + * Copyright (c) 1996 Bruce D. Evans. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * $Id$ + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: prof_machdep.c,v 1.2 1996/04/08 16:41:06 wollman Exp $ */ + +#ifdef GUPROF +#include "opt_cpu.h" +#include "opt_i586_guprof.h" +#include "opt_perfmon.h" + #include <sys/param.h> #include <sys/systm.h> +#include <sys/gmon.h> + #include <machine/clock.h> +#include <machine/perfmon.h> +#include <machine/profile.h> +#endif + #include <i386/isa/isa.h> #include <i386/isa/timerreg.h> #ifdef GUPROF -extern u_int cputime __P((void)); +#define CPUTIME_CLOCK_UNINITIALIZED 0 +#define CPUTIME_CLOCK_I8254 1 +#define CPUTIME_CLOCK_I586_CTR 2 +#define CPUTIME_CLOCK_I586_PMC 3 +#define CPUTIME_CLOCK_I8254_SHIFT 7 + +int cputime_bias = 1; /* initialize for locality of reference */ + +static int cputime_clock = CPUTIME_CLOCK_UNINITIALIZED; +#ifdef I586_PMC_GUPROF +static u_int cputime_clock_pmc_conf = I586_PMC_GUPROF; +static int cputime_clock_pmc_init; +static struct gmonparam saved_gmp; #endif +#endif /* GUPROF */ #ifdef __GNUC__ asm(" @@ -52,13 +99,13 @@ Lgot_frompc: # movl (%esp),%eax - pushf + pushfl pushl %eax pushl %edx cli call _mcount addl $8,%esp - popf + popfl Lmcount_exit: ret "); @@ -94,12 +141,12 @@ mexitcount: pushl %edx pushl %eax movl 8(%esp),%eax - pushf + pushfl pushl %eax cli call _mexitcount addl $4,%esp - popf + popfl popl %eax popl %edx Lmexitcount_exit: @@ -113,20 +160,48 @@ Lmexitcount_exit: * Return the time elapsed since the last call. The units are machine- * dependent. */ -u_int +int cputime() { u_int count; - u_int delta; - u_char low; + int delta; +#ifdef I586_PMC_GUPROF + u_quad_t event_count; +#endif + u_char high, low; static u_int prev_count; +#if defined(I586_CPU) || defined(I686_CPU) + if (cputime_clock == CPUTIME_CLOCK_I586_CTR) { + count = (u_int)rdtsc(); + delta = (int)(count - prev_count); + prev_count = count; + return (delta); + } +#ifdef I586_PMC_GUPROF + if (cputime_clock == CPUTIME_CLOCK_I586_PMC) { + /* + * XXX permon_read() should be inlined so that the + * perfmon module doesn't need to be compiled with + * profiling disabled and so that it is fast. + */ + perfmon_read(0, &event_count); + + count = (u_int)event_count; + delta = (int)(count - prev_count); + prev_count = count; + return (delta); + } +#endif /* I586_PMC_GUPROF */ +#endif /* I586_CPU or I686_CPU */ + /* * Read the current value of the 8254 timer counter 0. */ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); low = inb(TIMER_CNTR0); - count = low | (inb(TIMER_CNTR0) << 8); + high = inb(TIMER_CNTR0); + count = ((high << 8) | low) << CPUTIME_CLOCK_I8254_SHIFT; /* * The timer counts down from TIMER_CNTR0_MAX to 0 and then resets. @@ -140,10 +215,75 @@ cputime() delta = prev_count - count; prev_count = count; if ((int) delta <= 0) - return (delta + timer0_max_count); + return (delta + (timer0_max_count << CPUTIME_CLOCK_I8254_SHIFT)); return (delta); } -#else /* not GUPROF */ + +/* + * The start and stop routines need not be here since we turn off profiling + * before calling them. They are here for convenience. + */ + +void +startguprof(gp) + struct gmonparam *gp; +{ + if (cputime_clock == CPUTIME_CLOCK_UNINITIALIZED) { + cputime_clock = CPUTIME_CLOCK_I8254; +#if defined(I586_CPU) || defined(I686_CPU) + if (i586_ctr_freq != 0) + cputime_clock = CPUTIME_CLOCK_I586_CTR; +#endif + } + gp->profrate = timer_freq << CPUTIME_CLOCK_I8254_SHIFT; +#if defined(I586_CPU) || defined(I686_CPU) + if (cputime_clock == CPUTIME_CLOCK_I586_CTR) + gp->profrate = i586_ctr_freq; +#ifdef I586_PMC_GUPROF + else if (cputime_clock == CPUTIME_CLOCK_I586_PMC) { + if (perfmon_avail() && + perfmon_setup(0, cputime_clock_pmc_conf) == 0) { + if (perfmon_start(0) != 0) + perfmon_fini(0); + else { + /* XXX 1 event == 1 us. */ + gp->profrate = 1000000; + + saved_gmp = *gp; + + /* Zap overheads. They are invalid. */ + gp->cputime_overhead = 0; + gp->mcount_overhead = 0; + gp->mcount_post_overhead = 0; + gp->mcount_pre_overhead = 0; + gp->mexitcount_overhead = 0; + gp->mexitcount_post_overhead = 0; + gp->mexitcount_pre_overhead = 0; + + cputime_clock_pmc_init = TRUE; + } + } + } +#endif /* I586_PMC_GUPROF */ +#endif /* I586_CPU or I686_CPU */ + cputime_bias = 0; + cputime(); +} + +void +stopguprof(gp) + struct gmonparam *gp; +{ +#if defined(PERFMON) && defined(I586_PMC_GUPROF) + if (cputime_clock_pmc_init) { + *gp = saved_gmp; + perfmon_fini(0); + cputime_clock_pmc_init = FALSE; + } +#endif +} + +#else /* !GUPROF */ #ifdef __GNUC__ asm(" .text |