subr_prof.c revision 13107
11541Srgrimes/*- 21541Srgrimes * Copyright (c) 1982, 1986, 1993 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 51541Srgrimes * Redistribution and use in source and binary forms, with or without 61541Srgrimes * modification, are permitted provided that the following conditions 71541Srgrimes * are met: 81541Srgrimes * 1. Redistributions of source code must retain the above copyright 91541Srgrimes * notice, this list of conditions and the following disclaimer. 101541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111541Srgrimes * notice, this list of conditions and the following disclaimer in the 121541Srgrimes * documentation and/or other materials provided with the distribution. 131541Srgrimes * 3. All advertising materials mentioning features or use of this software 141541Srgrimes * must display the following acknowledgement: 151541Srgrimes * This product includes software developed by the University of 161541Srgrimes * California, Berkeley and its contributors. 171541Srgrimes * 4. Neither the name of the University nor the names of its contributors 181541Srgrimes * may be used to endorse or promote products derived from this software 191541Srgrimes * without specific prior written permission. 201541Srgrimes * 211541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311541Srgrimes * SUCH DAMAGE. 321541Srgrimes * 331541Srgrimes * @(#)subr_prof.c 8.3 (Berkeley) 9/23/93 3413107Sbde * $Id: subr_prof.c,v 1.15 1995/12/26 01:21:39 bde Exp $ 351541Srgrimes */ 361541Srgrimes 371541Srgrimes#include <sys/param.h> 381541Srgrimes#include <sys/systm.h> 3912221Sbde#include <sys/sysproto.h> 401541Srgrimes#include <sys/kernel.h> 411541Srgrimes#include <sys/proc.h> 4212657Sbde#include <sys/resourcevar.h> 437090Sbde#include <sys/sysctl.h> 447090Sbde 451541Srgrimes#include <machine/cpu.h> 461541Srgrimes 471541Srgrimes#ifdef GPROF 481541Srgrimes#include <sys/malloc.h> 491541Srgrimes#include <sys/gmon.h> 501541Srgrimes 5110653Sdgstatic void kmstartup __P((void *)); 5210358SjulianSYSINIT(kmem, SI_SUB_KPROF, SI_ORDER_FIRST, kmstartup, NULL) 5310358Sjulian 541541Srgrimesstruct gmonparam _gmonparam = { GMON_PROF_OFF }; 551541Srgrimes 566009Sbdeextern char btext[]; 571541Srgrimesextern char etext[]; 581541Srgrimes 5910407Sbdestatic void 6012569Sbdekmstartup(dummy) 6112569Sbde void *dummy; 621541Srgrimes{ 631541Srgrimes char *cp; 641541Srgrimes struct gmonparam *p = &_gmonparam; 6513107Sbde#ifdef GUPROF 6613107Sbde fptrint_t kmstartup_addr; 6713107Sbde int i; 6813107Sbde#endif 6913107Sbde 701541Srgrimes /* 711541Srgrimes * Round lowpc and highpc to multiples of the density we're using 721541Srgrimes * so the rest of the scaling (here and in gprof) stays in ints. 731541Srgrimes */ 746009Sbde p->lowpc = ROUNDDOWN((u_long)btext, HISTFRACTION * sizeof(HISTCOUNTER)); 751541Srgrimes p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER)); 761541Srgrimes p->textsize = p->highpc - p->lowpc; 771541Srgrimes printf("Profiling kernel, textsize=%d [%x..%x]\n", 781541Srgrimes p->textsize, p->lowpc, p->highpc); 791541Srgrimes p->kcountsize = p->textsize / HISTFRACTION; 801541Srgrimes p->hashfraction = HASHFRACTION; 811541Srgrimes p->fromssize = p->textsize / HASHFRACTION; 821541Srgrimes p->tolimit = p->textsize * ARCDENSITY / 100; 831541Srgrimes if (p->tolimit < MINARCS) 841541Srgrimes p->tolimit = MINARCS; 851541Srgrimes else if (p->tolimit > MAXARCS) 861541Srgrimes p->tolimit = MAXARCS; 871541Srgrimes p->tossize = p->tolimit * sizeof(struct tostruct); 881541Srgrimes cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize, 891541Srgrimes M_GPROF, M_NOWAIT); 901541Srgrimes if (cp == 0) { 911541Srgrimes printf("No memory for profiling.\n"); 921541Srgrimes return; 931541Srgrimes } 941541Srgrimes bzero(cp, p->kcountsize + p->tossize + p->fromssize); 951541Srgrimes p->tos = (struct tostruct *)cp; 961541Srgrimes cp += p->tossize; 9713107Sbde p->kcount = (HISTCOUNTER *)cp; 981541Srgrimes cp += p->kcountsize; 991541Srgrimes p->froms = (u_short *)cp; 10013107Sbde 10113107Sbde#ifdef GUPROF 10213107Sbde /* 10313107Sbde * Initialize pointers to overhead counters. 10413107Sbde */ 10513107Sbde p->cputime_count = &KCOUNT(p, PC_TO_I(p, cputime)); 10613107Sbde p->mcount_count = &KCOUNT(p, PC_TO_I(p, mcount)); 10713107Sbde p->mexitcount_count = &KCOUNT(p, PC_TO_I(p, mexitcount)); 10813107Sbde 10913107Sbde /* 11013107Sbde * Determine overheads. 11113107Sbde */ 11213107Sbde disable_intr(); 11313107Sbde p->state = GMON_PROF_HIRES; 11413107Sbde 11513107Sbde p->cputime_overhead = 0; 11613107Sbde (void)cputime(); 11713107Sbde for (i = 0; i < CALIB_SCALE; i++) 11813107Sbde p->cputime_overhead += cputime(); 11913107Sbde 12013107Sbde (void)cputime(); 12113107Sbde for (i = 0; i < CALIB_SCALE; i++) 12213107Sbde#if defined(i386) && __GNUC__ >= 2 12313107Sbde /* 12413107Sbde * Underestimate slightly by always calling __mcount, never 12513107Sbde * mcount. 12613107Sbde */ 12713107Sbde asm("pushl %0; call __mcount; popl %%ecx" 12813107Sbde : 12913107Sbde : "i" (kmstartup) 13013107Sbde : "ax", "bx", "cx", "dx", "memory"); 13113107Sbde#else 13213107Sbde#error 13313107Sbde#endif 13413107Sbde p->mcount_overhead = KCOUNT(p, PC_TO_I(p, kmstartup)); 13513107Sbde 13613107Sbde (void)cputime(); 13713107Sbde for (i = 0; i < CALIB_SCALE; i++) 13813107Sbde#if defined(i386) && __GNUC__ >= 2 13913107Sbde asm("call mexitcount; 1:" 14013107Sbde : : : "ax", "bx", "cx", "dx", "memory"); 14113107Sbde asm("movl $1b,%0" : "=rm" (kmstartup_addr)); 14213107Sbde#else 14313107Sbde#error 14413107Sbde#endif 14513107Sbde p->mexitcount_overhead = KCOUNT(p, PC_TO_I(p, kmstartup_addr)); 14613107Sbde 14713107Sbde p->state = GMON_PROF_OFF; 14813107Sbde enable_intr(); 14913107Sbde 15013107Sbde p->mcount_overhead_sub = p->mcount_overhead - p->cputime_overhead; 15113107Sbde p->mexitcount_overhead_sub = p->mexitcount_overhead 15213107Sbde - p->cputime_overhead; 15313107Sbde printf("Profiling overheads: %u+%u %u+%u\n", 15413107Sbde p->cputime_overhead, p->mcount_overhead_sub, 15513107Sbde p->cputime_overhead, p->mexitcount_overhead_sub); 15613107Sbde p->cputime_overhead_frac = p->cputime_overhead % CALIB_SCALE; 15713107Sbde p->cputime_overhead /= CALIB_SCALE; 15813107Sbde p->mcount_overhead_frac = p->mcount_overhead_sub % CALIB_SCALE; 15913107Sbde p->mcount_overhead_sub /= CALIB_SCALE; 16013107Sbde p->mcount_overhead /= CALIB_SCALE; 16113107Sbde p->mexitcount_overhead_frac = p->mexitcount_overhead_sub % CALIB_SCALE; 16213107Sbde p->mexitcount_overhead_sub /= CALIB_SCALE; 16313107Sbde p->mexitcount_overhead /= CALIB_SCALE; 16413107Sbde#endif /* GUPROF */ 1651541Srgrimes} 1661541Srgrimes 1671541Srgrimes/* 1681541Srgrimes * Return kernel profiling information. 1691541Srgrimes */ 17012429Sphkstatic int 17112429Sphksysctl_kern_prof SYSCTL_HANDLER_ARGS 1721541Srgrimes{ 17312429Sphk int *name = (int *) arg1; 17412429Sphk u_int namelen = arg2; 1751541Srgrimes struct gmonparam *gp = &_gmonparam; 1761541Srgrimes int error; 17713107Sbde int state; 1781541Srgrimes 1791541Srgrimes /* all sysctl names at this level are terminal */ 1801541Srgrimes if (namelen != 1) 1811541Srgrimes return (ENOTDIR); /* overloaded */ 1821541Srgrimes 1831541Srgrimes switch (name[0]) { 1841541Srgrimes case GPROF_STATE: 18513107Sbde state = gp->state; 18613107Sbde error = sysctl_handle_int(oidp, &state, 0, req); 1871541Srgrimes if (error) 1881541Srgrimes return (error); 18913107Sbde if (!req->newptr) 19013107Sbde return (0); 19113107Sbde if (state == GMON_PROF_OFF) { 1921541Srgrimes stopprofclock(&proc0); 19313107Sbde gp->state = state; 19413107Sbde } else if (state == GMON_PROF_ON) { 19513107Sbde gp->profrate = profhz; 19613107Sbde gp->state = state; 1971541Srgrimes startprofclock(&proc0); 19813107Sbde#ifdef GUPROF 19913107Sbde } else if (state == GMON_PROF_HIRES) { 20013107Sbde gp->profrate = 1193182; /* XXX */ 20113107Sbde stopprofclock(&proc0); 20213107Sbde gp->state = state; 20313107Sbde#endif 20413107Sbde } else if (state != gp->state) 20513107Sbde return (EINVAL); 2061541Srgrimes return (0); 2071541Srgrimes case GPROF_COUNT: 20812429Sphk return (sysctl_handle_opaque(oidp, 20912429Sphk gp->kcount, gp->kcountsize, req)); 2101541Srgrimes case GPROF_FROMS: 21112429Sphk return (sysctl_handle_opaque(oidp, 21212429Sphk gp->froms, gp->fromssize, req)); 2131541Srgrimes case GPROF_TOS: 21412429Sphk return (sysctl_handle_opaque(oidp, 21512429Sphk gp->tos, gp->tossize, req)); 2161541Srgrimes case GPROF_GMONPARAM: 21712429Sphk return (sysctl_handle_opaque(oidp, gp, sizeof *gp, req)); 2181541Srgrimes default: 2191541Srgrimes return (EOPNOTSUPP); 2201541Srgrimes } 2211541Srgrimes /* NOTREACHED */ 2221541Srgrimes} 22312429Sphk 22412429SphkSYSCTL_NODE(_kern, KERN_PROF, prof, CTLFLAG_RW, sysctl_kern_prof, ""); 2251541Srgrimes#endif /* GPROF */ 2261541Srgrimes 2271541Srgrimes/* 2281541Srgrimes * Profiling system call. 2291541Srgrimes * 2301541Srgrimes * The scale factor is a fixed point number with 16 bits of fraction, so that 2311541Srgrimes * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling. 2321541Srgrimes */ 23312221Sbde#ifndef _SYS_SYSPROTO_H_ 2341541Srgrimesstruct profil_args { 2351541Srgrimes caddr_t samples; 2361541Srgrimes u_int size; 2371541Srgrimes u_int offset; 2381541Srgrimes u_int scale; 2391541Srgrimes}; 24012221Sbde#endif 2411541Srgrimes/* ARGSUSED */ 2421549Srgrimesint 2431541Srgrimesprofil(p, uap, retval) 2441541Srgrimes struct proc *p; 2451541Srgrimes register struct profil_args *uap; 2461541Srgrimes int *retval; 2471541Srgrimes{ 2481541Srgrimes register struct uprof *upp; 2491541Srgrimes int s; 2501541Srgrimes 2511541Srgrimes if (uap->scale > (1 << 16)) 2521541Srgrimes return (EINVAL); 2531541Srgrimes if (uap->scale == 0) { 2541541Srgrimes stopprofclock(p); 2551541Srgrimes return (0); 2561541Srgrimes } 2571541Srgrimes upp = &p->p_stats->p_prof; 2581541Srgrimes 2591541Srgrimes /* Block profile interrupts while changing state. */ 2601541Srgrimes s = splstatclock(); 2611541Srgrimes upp->pr_off = uap->offset; 2621541Srgrimes upp->pr_scale = uap->scale; 2631541Srgrimes upp->pr_base = uap->samples; 2641541Srgrimes upp->pr_size = uap->size; 2651541Srgrimes startprofclock(p); 2661541Srgrimes splx(s); 2671541Srgrimes 2681541Srgrimes return (0); 2691541Srgrimes} 2701541Srgrimes 2711541Srgrimes/* 2721541Srgrimes * Scale is a fixed-point number with the binary point 16 bits 2731541Srgrimes * into the value, and is <= 1.0. pc is at most 32 bits, so the 2741541Srgrimes * intermediate result is at most 48 bits. 2751541Srgrimes */ 2761541Srgrimes#define PC_TO_INDEX(pc, prof) \ 2771541Srgrimes ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ 2781541Srgrimes (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) 2791541Srgrimes 2801541Srgrimes/* 2811541Srgrimes * Collect user-level profiling statistics; called on a profiling tick, 2821541Srgrimes * when a process is running in user-mode. This routine may be called 2831541Srgrimes * from an interrupt context. We try to update the user profiling buffers 2841541Srgrimes * cheaply with fuswintr() and suswintr(). If that fails, we revert to 2851541Srgrimes * an AST that will vector us to trap() with a context in which copyin 2861541Srgrimes * and copyout will work. Trap will then call addupc_task(). 2871541Srgrimes * 2881541Srgrimes * Note that we may (rarely) not get around to the AST soon enough, and 2891541Srgrimes * lose profile ticks when the next tick overwrites this one, but in this 2901541Srgrimes * case the system is overloaded and the profile is probably already 2911541Srgrimes * inaccurate. 2921541Srgrimes */ 2931541Srgrimesvoid 2941541Srgrimesaddupc_intr(p, pc, ticks) 2951541Srgrimes register struct proc *p; 2961541Srgrimes register u_long pc; 2971541Srgrimes u_int ticks; 2981541Srgrimes{ 2991541Srgrimes register struct uprof *prof; 3001541Srgrimes register caddr_t addr; 3011541Srgrimes register u_int i; 3021541Srgrimes register int v; 3031541Srgrimes 3041541Srgrimes if (ticks == 0) 3051541Srgrimes return; 3061541Srgrimes prof = &p->p_stats->p_prof; 3071541Srgrimes if (pc < prof->pr_off || 3081541Srgrimes (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) 3091541Srgrimes return; /* out of range; ignore */ 3101541Srgrimes 3111541Srgrimes addr = prof->pr_base + i; 3121541Srgrimes if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) { 3131541Srgrimes prof->pr_addr = pc; 3141541Srgrimes prof->pr_ticks = ticks; 3151541Srgrimes need_proftick(p); 3161541Srgrimes } 3171541Srgrimes} 3181541Srgrimes 3191541Srgrimes/* 3201541Srgrimes * Much like before, but we can afford to take faults here. If the 3211541Srgrimes * update fails, we simply turn off profiling. 3221541Srgrimes */ 32313017Sbdevoid 3241541Srgrimesaddupc_task(p, pc, ticks) 3251541Srgrimes register struct proc *p; 3261541Srgrimes register u_long pc; 3271541Srgrimes u_int ticks; 3281541Srgrimes{ 3291541Srgrimes register struct uprof *prof; 3301541Srgrimes register caddr_t addr; 3311541Srgrimes register u_int i; 3321541Srgrimes u_short v; 3331541Srgrimes 3341541Srgrimes /* Testing P_PROFIL may be unnecessary, but is certainly safe. */ 3351541Srgrimes if ((p->p_flag & P_PROFIL) == 0 || ticks == 0) 3361541Srgrimes return; 3371541Srgrimes 3381541Srgrimes prof = &p->p_stats->p_prof; 3391541Srgrimes if (pc < prof->pr_off || 3401541Srgrimes (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) 3411541Srgrimes return; 3421541Srgrimes 3431541Srgrimes addr = prof->pr_base + i; 3441541Srgrimes if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) { 3451541Srgrimes v += ticks; 3461541Srgrimes if (copyout((caddr_t)&v, addr, sizeof(v)) == 0) 3471541Srgrimes return; 3481541Srgrimes } 3491541Srgrimes stopprofclock(p); 3501541Srgrimes} 351