prof_machdep.c revision 99932
119000Sbde/*- 219000Sbde * Copyright (c) 1996 Bruce D. Evans. 319000Sbde * All rights reserved. 415146Swollman * 519000Sbde * Redistribution and use in source and binary forms, with or without 619000Sbde * modification, are permitted provided that the following conditions 719000Sbde * are met: 819000Sbde * 1. Redistributions of source code must retain the above copyright 919000Sbde * notice, this list of conditions and the following disclaimer. 1019000Sbde * 2. Redistributions in binary form must reproduce the above copyright 1119000Sbde * notice, this list of conditions and the following disclaimer in the 1219000Sbde * documentation and/or other materials provided with the distribution. 1319000Sbde * 1419000Sbde * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1519000Sbde * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1619000Sbde * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1719000Sbde * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1819000Sbde * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1919000Sbde * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2019000Sbde * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2119000Sbde * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2219000Sbde * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2319000Sbde * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2419000Sbde * SUCH DAMAGE. 2519000Sbde * 2650477Speter * $FreeBSD: head/sys/amd64/amd64/prof_machdep.c 99932 2002-07-13 22:28:34Z bde $ 2715146Swollman */ 2819000Sbde 2919000Sbde#ifdef GUPROF 3019000Sbde#include "opt_i586_guprof.h" 3119000Sbde#include "opt_perfmon.h" 3219000Sbde 3313107Sbde#include <sys/param.h> 3413107Sbde#include <sys/systm.h> 3519000Sbde#include <sys/gmon.h> 3631395Sbde#include <sys/kernel.h> 3731395Sbde#include <sys/sysctl.h> 3819000Sbde 3913107Sbde#include <machine/clock.h> 4019000Sbde#include <machine/perfmon.h> 4119000Sbde#include <machine/profile.h> 4246548Sbde#undef MCOUNT 4319000Sbde#endif 4419000Sbde 4546548Sbde#include <machine/asmacros.h> 4646548Sbde 4719269Sasami#ifdef PC98 4819269Sasami#include <pc98/pc98/pc98.h> 4919269Sasami#else 5013107Sbde#include <i386/isa/isa.h> 5119269Sasami#endif 5213107Sbde#include <i386/isa/timerreg.h> 5313107Sbde 5413107Sbde#ifdef GUPROF 5519000Sbde#define CPUTIME_CLOCK_UNINITIALIZED 0 5619000Sbde#define CPUTIME_CLOCK_I8254 1 5732005Sphk#define CPUTIME_CLOCK_TSC 2 5819000Sbde#define CPUTIME_CLOCK_I586_PMC 3 5919000Sbde#define CPUTIME_CLOCK_I8254_SHIFT 7 6019000Sbde 6119000Sbdeint cputime_bias = 1; /* initialize for locality of reference */ 6219000Sbde 6319000Sbdestatic int cputime_clock = CPUTIME_CLOCK_UNINITIALIZED; 6419000Sbde#ifdef I586_PMC_GUPROF 6519000Sbdestatic u_int cputime_clock_pmc_conf = I586_PMC_GUPROF; 6619000Sbdestatic int cputime_clock_pmc_init; 6719000Sbdestatic struct gmonparam saved_gmp; 6813107Sbde#endif 6919000Sbde#endif /* GUPROF */ 7013107Sbde 7113107Sbde#ifdef __GNUC__ 7235303Sbde__asm(" \n\ 7335303SbdeGM_STATE = 0 \n\ 7435303SbdeGMON_PROF_OFF = 3 \n\ 7535303Sbde \n\ 7635303Sbde .text \n\ 7750379Speter .p2align 4,0x90 \n\ 7835303Sbde .globl __mcount \n\ 7946548Sbde .type __mcount,@function \n\ 8035303Sbde__mcount: \n\ 8135303Sbde # \n\ 8235303Sbde # Check that we are profiling. Do it early for speed. \n\ 8335303Sbde # \n\ 8446548Sbde cmpl $GMON_PROF_OFF," __XSTRING(CNAME(_gmonparam)) "+GM_STATE \n\ 8546548Sbde je .mcount_exit \n\ 8635303Sbde # \n\ 8746548Sbde # __mcount is the same as [.]mcount except the caller \n\ 8835303Sbde # hasn't changed the stack except to call here, so the \n\ 8935303Sbde # caller's raddr is above our raddr. \n\ 9035303Sbde # \n\ 9135303Sbde movl 4(%esp),%edx \n\ 9246548Sbde jmp .got_frompc \n\ 9335303Sbde \n\ 9450379Speter .p2align 4,0x90 \n\ 9546548Sbde .globl " __XSTRING(HIDENAME(mcount)) " \n\ 9646548Sbde" __XSTRING(HIDENAME(mcount)) ": \n\ 9799932Sbde .globl __cyg_profile_func_enter \n\ 9899932Sbde__cyg_profile_func_enter: \n\ 9946548Sbde cmpl $GMON_PROF_OFF," __XSTRING(CNAME(_gmonparam)) "+GM_STATE \n\ 10046548Sbde je .mcount_exit \n\ 10135303Sbde # \n\ 10235303Sbde # The caller's stack frame has already been built, so \n\ 10335303Sbde # %ebp is the caller's frame pointer. The caller's \n\ 10435303Sbde # raddr is in the caller's frame following the caller's \n\ 10535303Sbde # caller's frame pointer. \n\ 10635303Sbde # \n\ 10735303Sbde movl 4(%ebp),%edx \n\ 10846548Sbde.got_frompc: \n\ 10935303Sbde # \n\ 11035303Sbde # Our raddr is the caller's pc. \n\ 11135303Sbde # \n\ 11235303Sbde movl (%esp),%eax \n\ 11335303Sbde \n\ 11435303Sbde pushfl \n\ 11535303Sbde pushl %eax \n\ 11635303Sbde pushl %edx \n\ 11735303Sbde cli \n\ 11846548Sbde call " __XSTRING(CNAME(mcount)) " \n\ 11935303Sbde addl $8,%esp \n\ 12035303Sbde popfl \n\ 12146548Sbde.mcount_exit: \n\ 12235303Sbde ret \n\ 12313107Sbde"); 12413107Sbde#else /* !__GNUC__ */ 12513107Sbde#error 12613107Sbde#endif /* __GNUC__ */ 12713107Sbde 12813107Sbde#ifdef GUPROF 12913107Sbde/* 13046548Sbde * [.]mexitcount saves the return register(s), loads selfpc and calls 13113107Sbde * mexitcount(selfpc) to do the work. Someday it should be in a machine 13246548Sbde * dependent file together with cputime(), __mcount and [.]mcount. cputime() 13313107Sbde * can't just be put in machdep.c because it has to be compiled without -pg. 13413107Sbde */ 13513107Sbde#ifdef __GNUC__ 13635303Sbde__asm(" \n\ 13735303Sbde .text \n\ 13835303Sbde# \n\ 13946548Sbde# Dummy label to be seen when gprof -u hides [.]mexitcount. \n\ 14035303Sbde# \n\ 14150379Speter .p2align 4,0x90 \n\ 14235303Sbde .globl __mexitcount \n\ 14346548Sbde .type __mexitcount,@function \n\ 14435303Sbde__mexitcount: \n\ 14535303Sbde nop \n\ 14635303Sbde \n\ 14735303SbdeGMON_PROF_HIRES = 4 \n\ 14835303Sbde \n\ 14950379Speter .p2align 4,0x90 \n\ 15046548Sbde .globl " __XSTRING(HIDENAME(mexitcount)) " \n\ 15146548Sbde" __XSTRING(HIDENAME(mexitcount)) ": \n\ 15299932Sbde .globl __cyg_profile_func_exit \n\ 15399932Sbde__cyg_profile_func_exit: \n\ 15446548Sbde cmpl $GMON_PROF_HIRES," __XSTRING(CNAME(_gmonparam)) "+GM_STATE \n\ 15546548Sbde jne .mexitcount_exit \n\ 15635303Sbde pushl %edx \n\ 15735303Sbde pushl %eax \n\ 15835303Sbde movl 8(%esp),%eax \n\ 15935303Sbde pushfl \n\ 16035303Sbde pushl %eax \n\ 16135303Sbde cli \n\ 16246548Sbde call " __XSTRING(CNAME(mexitcount)) " \n\ 16335303Sbde addl $4,%esp \n\ 16435303Sbde popfl \n\ 16535303Sbde popl %eax \n\ 16635303Sbde popl %edx \n\ 16746548Sbde.mexitcount_exit: \n\ 16835303Sbde ret \n\ 16913107Sbde"); 17013107Sbde#else /* !__GNUC__ */ 17113107Sbde#error 17213107Sbde#endif /* __GNUC__ */ 17313107Sbde 17413107Sbde/* 17513107Sbde * Return the time elapsed since the last call. The units are machine- 17613107Sbde * dependent. 17713107Sbde */ 17819000Sbdeint 17913107Sbdecputime() 18013107Sbde{ 18113107Sbde u_int count; 18219000Sbde int delta; 18341794Sbde#if (defined(I586_CPU) || defined(I686_CPU)) && !defined(SMP) && \ 18441794Sbde defined(PERFMON) && defined(I586_PMC_GUPROF) 18519000Sbde u_quad_t event_count; 18619000Sbde#endif 18719000Sbde u_char high, low; 18813107Sbde static u_int prev_count; 18913107Sbde 19031395Sbde#if (defined(I586_CPU) || defined(I686_CPU)) && !defined(SMP) 19132005Sphk if (cputime_clock == CPUTIME_CLOCK_TSC) { 19219000Sbde count = (u_int)rdtsc(); 19319000Sbde delta = (int)(count - prev_count); 19419000Sbde prev_count = count; 19519000Sbde return (delta); 19619000Sbde } 19731395Sbde#if defined(PERFMON) && defined(I586_PMC_GUPROF) 19819000Sbde if (cputime_clock == CPUTIME_CLOCK_I586_PMC) { 19919000Sbde /* 20019000Sbde * XXX permon_read() should be inlined so that the 20119000Sbde * perfmon module doesn't need to be compiled with 20219000Sbde * profiling disabled and so that it is fast. 20319000Sbde */ 20419000Sbde perfmon_read(0, &event_count); 20519000Sbde 20619000Sbde count = (u_int)event_count; 20719000Sbde delta = (int)(count - prev_count); 20819000Sbde prev_count = count; 20919000Sbde return (delta); 21019000Sbde } 21131395Sbde#endif /* PERFMON && I586_PMC_GUPROF */ 21231395Sbde#endif /* (I586_CPU || I686_CPU) && !SMP */ 21319000Sbde 21413107Sbde /* 21513107Sbde * Read the current value of the 8254 timer counter 0. 21613107Sbde */ 21713107Sbde outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); 21813107Sbde low = inb(TIMER_CNTR0); 21919000Sbde high = inb(TIMER_CNTR0); 22019000Sbde count = ((high << 8) | low) << CPUTIME_CLOCK_I8254_SHIFT; 22113107Sbde 22213107Sbde /* 22313107Sbde * The timer counts down from TIMER_CNTR0_MAX to 0 and then resets. 22413107Sbde * While profiling is enabled, this routine is called at least twice 22513107Sbde * per timer reset (for mcounting and mexitcounting hardclock()), 22613107Sbde * so at most one reset has occurred since the last call, and one 22713107Sbde * has occurred iff the current count is larger than the previous 22813107Sbde * count. This allows counter underflow to be detected faster 22913107Sbde * than in microtime(). 23013107Sbde */ 23113107Sbde delta = prev_count - count; 23213107Sbde prev_count = count; 23313107Sbde if ((int) delta <= 0) 23419000Sbde return (delta + (timer0_max_count << CPUTIME_CLOCK_I8254_SHIFT)); 23513107Sbde return (delta); 23613107Sbde} 23719000Sbde 23831395Sbdestatic int 23962573Sphksysctl_machdep_cputime_clock(SYSCTL_HANDLER_ARGS) 24031395Sbde{ 24131395Sbde int clock; 24241794Sbde int error; 24341794Sbde#if defined(PERFMON) && defined(I586_PMC_GUPROF) 24431395Sbde int event; 24531395Sbde struct pmc pmc; 24641794Sbde#endif 24731395Sbde 24831395Sbde clock = cputime_clock; 24931395Sbde#if defined(PERFMON) && defined(I586_PMC_GUPROF) 25031395Sbde if (clock == CPUTIME_CLOCK_I586_PMC) { 25131395Sbde pmc.pmc_val = cputime_clock_pmc_conf; 25231395Sbde clock += pmc.pmc_event; 25331395Sbde } 25431395Sbde#endif 25531395Sbde error = sysctl_handle_opaque(oidp, &clock, sizeof clock, req); 25631395Sbde if (error == 0 && req->newptr != NULL) { 25731395Sbde#if defined(PERFMON) && defined(I586_PMC_GUPROF) 25831395Sbde if (clock >= CPUTIME_CLOCK_I586_PMC) { 25931395Sbde event = clock - CPUTIME_CLOCK_I586_PMC; 26031395Sbde if (event >= 256) 26131395Sbde return (EINVAL); 26231395Sbde pmc.pmc_num = 0; 26331395Sbde pmc.pmc_event = event; 26431395Sbde pmc.pmc_unit = 0; 26531395Sbde pmc.pmc_flags = PMCF_E | PMCF_OS | PMCF_USR; 26631395Sbde pmc.pmc_mask = 0; 26731395Sbde cputime_clock_pmc_conf = pmc.pmc_val; 26831395Sbde cputime_clock = CPUTIME_CLOCK_I586_PMC; 26931395Sbde } else 27031395Sbde#endif 27131395Sbde { 27231395Sbde if (clock < 0 || clock >= CPUTIME_CLOCK_I586_PMC) 27331395Sbde return (EINVAL); 27431395Sbde cputime_clock = clock; 27531395Sbde } 27631395Sbde } 27731395Sbde return (error); 27831395Sbde} 27931395Sbde 28031395SbdeSYSCTL_PROC(_machdep, OID_AUTO, cputime_clock, CTLTYPE_INT | CTLFLAG_RW, 28131395Sbde 0, sizeof(u_int), sysctl_machdep_cputime_clock, "I", ""); 28231395Sbde 28319000Sbde/* 28419000Sbde * The start and stop routines need not be here since we turn off profiling 28519000Sbde * before calling them. They are here for convenience. 28619000Sbde */ 28719000Sbde 28819000Sbdevoid 28919000Sbdestartguprof(gp) 29019000Sbde struct gmonparam *gp; 29119000Sbde{ 29219000Sbde if (cputime_clock == CPUTIME_CLOCK_UNINITIALIZED) { 29319000Sbde cputime_clock = CPUTIME_CLOCK_I8254; 29431395Sbde#if (defined(I586_CPU) || defined(I686_CPU)) && !defined(SMP) 29532005Sphk if (tsc_freq != 0) 29632005Sphk cputime_clock = CPUTIME_CLOCK_TSC; 29719000Sbde#endif 29819000Sbde } 29919000Sbde gp->profrate = timer_freq << CPUTIME_CLOCK_I8254_SHIFT; 30031395Sbde#if (defined(I586_CPU) || defined(I686_CPU)) && !defined(SMP) 30132005Sphk if (cputime_clock == CPUTIME_CLOCK_TSC) 30232005Sphk gp->profrate = tsc_freq; 30331395Sbde#if defined(PERFMON) && defined(I586_PMC_GUPROF) 30419000Sbde else if (cputime_clock == CPUTIME_CLOCK_I586_PMC) { 30519000Sbde if (perfmon_avail() && 30619000Sbde perfmon_setup(0, cputime_clock_pmc_conf) == 0) { 30719000Sbde if (perfmon_start(0) != 0) 30819000Sbde perfmon_fini(0); 30919000Sbde else { 31019000Sbde /* XXX 1 event == 1 us. */ 31119000Sbde gp->profrate = 1000000; 31219000Sbde 31319000Sbde saved_gmp = *gp; 31419000Sbde 31519000Sbde /* Zap overheads. They are invalid. */ 31619000Sbde gp->cputime_overhead = 0; 31719000Sbde gp->mcount_overhead = 0; 31819000Sbde gp->mcount_post_overhead = 0; 31919000Sbde gp->mcount_pre_overhead = 0; 32019000Sbde gp->mexitcount_overhead = 0; 32119000Sbde gp->mexitcount_post_overhead = 0; 32219000Sbde gp->mexitcount_pre_overhead = 0; 32319000Sbde 32419000Sbde cputime_clock_pmc_init = TRUE; 32519000Sbde } 32619000Sbde } 32719000Sbde } 32831395Sbde#endif /* PERFMON && I586_PMC_GUPROF */ 32931395Sbde#endif /* (I586_CPU || I686_CPU) && !SMP */ 33019000Sbde cputime_bias = 0; 33119000Sbde cputime(); 33219000Sbde} 33319000Sbde 33419000Sbdevoid 33519000Sbdestopguprof(gp) 33619000Sbde struct gmonparam *gp; 33719000Sbde{ 33819000Sbde#if defined(PERFMON) && defined(I586_PMC_GUPROF) 33919000Sbde if (cputime_clock_pmc_init) { 34019000Sbde *gp = saved_gmp; 34119000Sbde perfmon_fini(0); 34219000Sbde cputime_clock_pmc_init = FALSE; 34319000Sbde } 34419000Sbde#endif 34519000Sbde} 34619000Sbde 34719000Sbde#else /* !GUPROF */ 34813107Sbde#ifdef __GNUC__ 34935303Sbde__asm(" \n\ 35035303Sbde .text \n\ 35150379Speter .p2align 4,0x90 \n\ 35246548Sbde .globl " __XSTRING(HIDENAME(mexitcount)) " \n\ 35346548Sbde" __XSTRING(HIDENAME(mexitcount)) ": \n\ 35435303Sbde ret \n\ 35513107Sbde"); 35613107Sbde#else /* !__GNUC__ */ 35713107Sbde#error 35813107Sbde#endif /* __GNUC__ */ 35913107Sbde#endif /* GUPROF */ 360