subr_prof.c revision 99012
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 3450477Speter * $FreeBSD: head/sys/kern/subr_prof.c 99012 2002-06-29 02:00:02Z alfred $ 351541Srgrimes */ 361541Srgrimes 371541Srgrimes#include <sys/param.h> 381541Srgrimes#include <sys/systm.h> 3912221Sbde#include <sys/sysproto.h> 4052147Sbde#include <sys/kernel.h> 4176166Smarkm#include <sys/lock.h> 4276166Smarkm#include <sys/mutex.h> 431541Srgrimes#include <sys/proc.h> 4412657Sbde#include <sys/resourcevar.h> 4552147Sbde#include <sys/sysctl.h> 467090Sbde 471541Srgrimes#include <machine/cpu.h> 481541Srgrimes 491541Srgrimes#ifdef GPROF 501541Srgrimes#include <sys/malloc.h> 511541Srgrimes#include <sys/gmon.h> 5246548Sbde#undef MCOUNT 531541Srgrimes 5430354Sphkstatic MALLOC_DEFINE(M_GPROF, "gprof", "kernel profiling buffer"); 5530309Sphk 5692723Salfredstatic void kmstartup(void *); 5710358SjulianSYSINIT(kmem, SI_SUB_KPROF, SI_ORDER_FIRST, kmstartup, NULL) 5810358Sjulian 591541Srgrimesstruct gmonparam _gmonparam = { GMON_PROF_OFF }; 601541Srgrimes 6119000Sbde#ifdef GUPROF 6246548Sbde#include <machine/asmacros.h> 6346548Sbde 6419000Sbdevoid 6519000Sbdenullfunc_loop_profiled() 6619000Sbde{ 6719000Sbde int i; 6819000Sbde 6919000Sbde for (i = 0; i < CALIB_SCALE; i++) 7019000Sbde nullfunc_profiled(); 7119000Sbde} 7219000Sbde 7320396Sbde#define nullfunc_loop_profiled_end nullfunc_profiled /* XXX */ 7420396Sbde 7519000Sbdevoid 7619000Sbdenullfunc_profiled() 7719000Sbde{ 7819000Sbde} 7919000Sbde#endif /* GUPROF */ 8019000Sbde 8185733Sgreen/* 8285733Sgreen * Update the histograms to support extending the text region arbitrarily. 8385733Sgreen * This is done slightly naively (no sparse regions), so will waste slight 8485733Sgreen * amounts of memory, but will overall work nicely enough to allow profiling 8585733Sgreen * of KLDs. 8685733Sgreen */ 8785733Sgreenvoid 8885733Sgreenkmupetext(uintfptr_t nhighpc) 8985733Sgreen{ 9085733Sgreen struct gmonparam np; /* slightly large */ 9185733Sgreen struct gmonparam *p = &_gmonparam; 9285733Sgreen char *cp; 9385733Sgreen 9485733Sgreen GIANT_REQUIRED; 9585733Sgreen bcopy(p, &np, sizeof(*p)); 9685733Sgreen np.highpc = ROUNDUP(nhighpc, HISTFRACTION * sizeof(HISTCOUNTER)); 9785733Sgreen if (np.highpc <= p->highpc) 9885733Sgreen return; 9985733Sgreen np.textsize = np.highpc - p->lowpc; 10085733Sgreen np.kcountsize = np.textsize / HISTFRACTION; 10185733Sgreen np.hashfraction = HASHFRACTION; 10285733Sgreen np.fromssize = np.textsize / HASHFRACTION; 10385733Sgreen np.tolimit = np.textsize * ARCDENSITY / 100; 10485733Sgreen if (np.tolimit < MINARCS) 10585733Sgreen np.tolimit = MINARCS; 10685733Sgreen else if (np.tolimit > MAXARCS) 10785733Sgreen np.tolimit = MAXARCS; 10885733Sgreen np.tossize = np.tolimit * sizeof(struct tostruct); 10985733Sgreen cp = malloc(np.kcountsize + np.fromssize + np.tossize, 11085733Sgreen M_GPROF, M_WAITOK); 11185733Sgreen /* 11285733Sgreen * Check for something else extending highpc while we slept. 11385733Sgreen */ 11485733Sgreen if (np.highpc <= p->highpc) { 11585733Sgreen free(cp, M_GPROF); 11685733Sgreen return; 11785733Sgreen } 11885733Sgreen np.tos = (struct tostruct *)cp; 11985733Sgreen cp += np.tossize; 12085733Sgreen np.kcount = (HISTCOUNTER *)cp; 12185733Sgreen cp += np.kcountsize; 12285733Sgreen np.froms = (u_short *)cp; 12385733Sgreen#ifdef GUPROF 12485733Sgreen /* Reinitialize pointers to overhead counters. */ 12585733Sgreen np.cputime_count = &KCOUNT(&np, PC_TO_I(&np, cputime)); 12685733Sgreen np.mcount_count = &KCOUNT(&np, PC_TO_I(&np, mcount)); 12785733Sgreen np.mexitcount_count = &KCOUNT(&np, PC_TO_I(&np, mexitcount)); 12885733Sgreen#endif 12988088Sjhb critical_enter(); 13085733Sgreen bcopy(p->tos, np.tos, p->tossize); 13185733Sgreen bzero((char *)np.tos + p->tossize, np.tossize - p->tossize); 13285733Sgreen bcopy(p->kcount, np.kcount, p->kcountsize); 13385733Sgreen bzero((char *)np.kcount + p->kcountsize, np.kcountsize - 13485733Sgreen p->kcountsize); 13585733Sgreen bcopy(p->froms, np.froms, p->fromssize); 13685733Sgreen bzero((char *)np.froms + p->fromssize, np.fromssize - p->fromssize); 13785733Sgreen cp = (char *)p->tos; 13885733Sgreen bcopy(&np, p, sizeof(*p)); 13988088Sjhb critical_exit(); 14085733Sgreen free(cp, M_GPROF); 14185733Sgreen} 14285733Sgreen 14310407Sbdestatic void 14412569Sbdekmstartup(dummy) 14512569Sbde void *dummy; 1461541Srgrimes{ 1471541Srgrimes char *cp; 1481541Srgrimes struct gmonparam *p = &_gmonparam; 14913107Sbde#ifdef GUPROF 15019000Sbde int cputime_overhead; 15119000Sbde int empty_loop_time; 15219000Sbde int i; 15319000Sbde int mcount_overhead; 15419000Sbde int mexitcount_overhead; 15519000Sbde int nullfunc_loop_overhead; 15619000Sbde int nullfunc_loop_profiled_time; 15737629Sbde uintfptr_t tmp_addr; 15813107Sbde#endif 15913107Sbde 1601541Srgrimes /* 1611541Srgrimes * Round lowpc and highpc to multiples of the density we're using 1621541Srgrimes * so the rest of the scaling (here and in gprof) stays in ints. 1631541Srgrimes */ 1646009Sbde p->lowpc = ROUNDDOWN((u_long)btext, HISTFRACTION * sizeof(HISTCOUNTER)); 1651541Srgrimes p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER)); 1661541Srgrimes p->textsize = p->highpc - p->lowpc; 16719000Sbde printf("Profiling kernel, textsize=%lu [%x..%x]\n", 1681541Srgrimes p->textsize, p->lowpc, p->highpc); 1691541Srgrimes p->kcountsize = p->textsize / HISTFRACTION; 1701541Srgrimes p->hashfraction = HASHFRACTION; 1711541Srgrimes p->fromssize = p->textsize / HASHFRACTION; 1721541Srgrimes p->tolimit = p->textsize * ARCDENSITY / 100; 1731541Srgrimes if (p->tolimit < MINARCS) 1741541Srgrimes p->tolimit = MINARCS; 1751541Srgrimes else if (p->tolimit > MAXARCS) 1761541Srgrimes p->tolimit = MAXARCS; 1771541Srgrimes p->tossize = p->tolimit * sizeof(struct tostruct); 1781541Srgrimes cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize, 17985733Sgreen M_GPROF, M_WAITOK | M_ZERO); 1801541Srgrimes p->tos = (struct tostruct *)cp; 1811541Srgrimes cp += p->tossize; 18213107Sbde p->kcount = (HISTCOUNTER *)cp; 1831541Srgrimes cp += p->kcountsize; 1841541Srgrimes p->froms = (u_short *)cp; 18513107Sbde 18613107Sbde#ifdef GUPROF 18719000Sbde /* Initialize pointers to overhead counters. */ 18813107Sbde p->cputime_count = &KCOUNT(p, PC_TO_I(p, cputime)); 18913107Sbde p->mcount_count = &KCOUNT(p, PC_TO_I(p, mcount)); 19013107Sbde p->mexitcount_count = &KCOUNT(p, PC_TO_I(p, mexitcount)); 19113107Sbde 19213107Sbde /* 19319000Sbde * Disable interrupts to avoid interference while we calibrate 19419000Sbde * things. 19513107Sbde */ 19688088Sjhb critical_enter(); 19713107Sbde 19819000Sbde /* 19919000Sbde * Determine overheads. 20019000Sbde * XXX this needs to be repeated for each useful timer/counter. 20119000Sbde */ 20219000Sbde cputime_overhead = 0; 20319000Sbde startguprof(p); 20413107Sbde for (i = 0; i < CALIB_SCALE; i++) 20519000Sbde cputime_overhead += cputime(); 20613107Sbde 20719000Sbde empty_loop(); 20819000Sbde startguprof(p); 20919000Sbde empty_loop(); 21019000Sbde empty_loop_time = cputime(); 21119000Sbde 21219000Sbde nullfunc_loop_profiled(); 21319000Sbde 21419000Sbde /* 21519000Sbde * Start profiling. There won't be any normal function calls since 21619000Sbde * interrupts are disabled, but we will call the profiling routines 21719000Sbde * directly to determine their overheads. 21819000Sbde */ 21919000Sbde p->state = GMON_PROF_HIRES; 22019000Sbde 22119000Sbde startguprof(p); 22219000Sbde nullfunc_loop_profiled(); 22319000Sbde 22419000Sbde startguprof(p); 22513107Sbde for (i = 0; i < CALIB_SCALE; i++) 22635596Sbde#if defined(__i386__) && __GNUC__ >= 2 22735210Sbde __asm("pushl %0; call __mcount; popl %%ecx" 22835210Sbde : 22935210Sbde : "i" (profil) 23035210Sbde : "ax", "bx", "cx", "dx", "memory"); 23113107Sbde#else 23213107Sbde#error 23313107Sbde#endif 23419000Sbde mcount_overhead = KCOUNT(p, PC_TO_I(p, profil)); 23513107Sbde 23619000Sbde startguprof(p); 23713107Sbde for (i = 0; i < CALIB_SCALE; i++) 23835596Sbde#if defined(__i386__) && __GNUC__ >= 2 23946548Sbde __asm("call " __XSTRING(HIDENAME(mexitcount)) "; 1:" 24035210Sbde : : : "ax", "bx", "cx", "dx", "memory"); 24135210Sbde __asm("movl $1b,%0" : "=rm" (tmp_addr)); 24213107Sbde#else 24313107Sbde#error 24413107Sbde#endif 24520396Sbde mexitcount_overhead = KCOUNT(p, PC_TO_I(p, tmp_addr)); 24613107Sbde 24713107Sbde p->state = GMON_PROF_OFF; 24819000Sbde stopguprof(p); 24919000Sbde 25088088Sjhb critical_exit(); 25113107Sbde 25219000Sbde nullfunc_loop_profiled_time = 0; 25337629Sbde for (tmp_addr = (uintfptr_t)nullfunc_loop_profiled; 25437629Sbde tmp_addr < (uintfptr_t)nullfunc_loop_profiled_end; 25520396Sbde tmp_addr += HISTFRACTION * sizeof(HISTCOUNTER)) 25620396Sbde nullfunc_loop_profiled_time += KCOUNT(p, PC_TO_I(p, tmp_addr)); 25719000Sbde#define CALIB_DOSCALE(count) (((count) + CALIB_SCALE / 3) / CALIB_SCALE) 25819000Sbde#define c2n(count, freq) ((int)((count) * 1000000000LL / freq)) 25919000Sbde printf("cputime %d, empty_loop %d, nullfunc_loop_profiled %d, mcount %d, mexitcount %d\n", 26019000Sbde CALIB_DOSCALE(c2n(cputime_overhead, p->profrate)), 26119000Sbde CALIB_DOSCALE(c2n(empty_loop_time, p->profrate)), 26219000Sbde CALIB_DOSCALE(c2n(nullfunc_loop_profiled_time, p->profrate)), 26319000Sbde CALIB_DOSCALE(c2n(mcount_overhead, p->profrate)), 26419000Sbde CALIB_DOSCALE(c2n(mexitcount_overhead, p->profrate))); 26519000Sbde cputime_overhead -= empty_loop_time; 26619000Sbde mcount_overhead -= empty_loop_time; 26719000Sbde mexitcount_overhead -= empty_loop_time; 26819000Sbde 26919000Sbde /*- 27019000Sbde * Profiling overheads are determined by the times between the 27119000Sbde * following events: 27219000Sbde * MC1: mcount() is called 27319000Sbde * MC2: cputime() (called from mcount()) latches the timer 27419000Sbde * MC3: mcount() completes 27519000Sbde * ME1: mexitcount() is called 27619000Sbde * ME2: cputime() (called from mexitcount()) latches the timer 27719000Sbde * ME3: mexitcount() completes. 27819000Sbde * The times between the events vary slightly depending on instruction 27919000Sbde * combination and cache misses, etc. Attempt to determine the 28019000Sbde * minimum times. These can be subtracted from the profiling times 28119000Sbde * without much risk of reducing the profiling times below what they 28219000Sbde * would be when profiling is not configured. Abbreviate: 28319000Sbde * ab = minimum time between MC1 and MC3 28419000Sbde * a = minumum time between MC1 and MC2 28519000Sbde * b = minimum time between MC2 and MC3 28619000Sbde * cd = minimum time between ME1 and ME3 28719000Sbde * c = minimum time between ME1 and ME2 28819000Sbde * d = minimum time between ME2 and ME3. 28919000Sbde * These satisfy the relations: 29019000Sbde * ab <= mcount_overhead (just measured) 29119000Sbde * a + b <= ab 29219000Sbde * cd <= mexitcount_overhead (just measured) 29319000Sbde * c + d <= cd 29419000Sbde * a + d <= nullfunc_loop_profiled_time (just measured) 29519000Sbde * a >= 0, b >= 0, c >= 0, d >= 0. 29619000Sbde * Assume that ab and cd are equal to the minimums. 29719000Sbde */ 29819000Sbde p->cputime_overhead = CALIB_DOSCALE(cputime_overhead); 29919000Sbde p->mcount_overhead = CALIB_DOSCALE(mcount_overhead - cputime_overhead); 30019000Sbde p->mexitcount_overhead = CALIB_DOSCALE(mexitcount_overhead 30119000Sbde - cputime_overhead); 30219000Sbde nullfunc_loop_overhead = nullfunc_loop_profiled_time - empty_loop_time; 30319000Sbde p->mexitcount_post_overhead = CALIB_DOSCALE((mcount_overhead 30419000Sbde - nullfunc_loop_overhead) 30519000Sbde / 4); 30619000Sbde p->mexitcount_pre_overhead = p->mexitcount_overhead 30719000Sbde + p->cputime_overhead 30819000Sbde - p->mexitcount_post_overhead; 30919000Sbde p->mcount_pre_overhead = CALIB_DOSCALE(nullfunc_loop_overhead) 31019000Sbde - p->mexitcount_post_overhead; 31119000Sbde p->mcount_post_overhead = p->mcount_overhead 31219000Sbde + p->cputime_overhead 31319000Sbde - p->mcount_pre_overhead; 31419000Sbde printf( 31519000Sbde"Profiling overheads: mcount: %d+%d, %d+%d; mexitcount: %d+%d, %d+%d nsec\n", 31619000Sbde c2n(p->cputime_overhead, p->profrate), 31719000Sbde c2n(p->mcount_overhead, p->profrate), 31819000Sbde c2n(p->mcount_pre_overhead, p->profrate), 31919000Sbde c2n(p->mcount_post_overhead, p->profrate), 32019000Sbde c2n(p->cputime_overhead, p->profrate), 32119000Sbde c2n(p->mexitcount_overhead, p->profrate), 32219000Sbde c2n(p->mexitcount_pre_overhead, p->profrate), 32319000Sbde c2n(p->mexitcount_post_overhead, p->profrate)); 32419000Sbde printf( 32519000Sbde"Profiling overheads: mcount: %d+%d, %d+%d; mexitcount: %d+%d, %d+%d cycles\n", 32619000Sbde p->cputime_overhead, p->mcount_overhead, 32719000Sbde p->mcount_pre_overhead, p->mcount_post_overhead, 32819000Sbde p->cputime_overhead, p->mexitcount_overhead, 32919000Sbde p->mexitcount_pre_overhead, p->mexitcount_post_overhead); 33013107Sbde#endif /* GUPROF */ 3311541Srgrimes} 3321541Srgrimes 3331541Srgrimes/* 3341541Srgrimes * Return kernel profiling information. 3351541Srgrimes */ 33612429Sphkstatic int 33762573Sphksysctl_kern_prof(SYSCTL_HANDLER_ARGS) 3381541Srgrimes{ 33912429Sphk int *name = (int *) arg1; 34012429Sphk u_int namelen = arg2; 3411541Srgrimes struct gmonparam *gp = &_gmonparam; 3421541Srgrimes int error; 34313107Sbde int state; 3441541Srgrimes 3451541Srgrimes /* all sysctl names at this level are terminal */ 3461541Srgrimes if (namelen != 1) 3471541Srgrimes return (ENOTDIR); /* overloaded */ 3481541Srgrimes 3491541Srgrimes switch (name[0]) { 3501541Srgrimes case GPROF_STATE: 35113107Sbde state = gp->state; 35213107Sbde error = sysctl_handle_int(oidp, &state, 0, req); 3531541Srgrimes if (error) 3541541Srgrimes return (error); 35513107Sbde if (!req->newptr) 35613107Sbde return (0); 35713107Sbde if (state == GMON_PROF_OFF) { 35819000Sbde gp->state = state; 3591541Srgrimes stopprofclock(&proc0); 36019000Sbde stopguprof(gp); 36113107Sbde } else if (state == GMON_PROF_ON) { 36219000Sbde gp->state = GMON_PROF_OFF; 36319000Sbde stopguprof(gp); 36413107Sbde gp->profrate = profhz; 36519000Sbde startprofclock(&proc0); 36613107Sbde gp->state = state; 36713107Sbde#ifdef GUPROF 36813107Sbde } else if (state == GMON_PROF_HIRES) { 36919000Sbde gp->state = GMON_PROF_OFF; 37013107Sbde stopprofclock(&proc0); 37119000Sbde startguprof(gp); 37213107Sbde gp->state = state; 37313107Sbde#endif 37413107Sbde } else if (state != gp->state) 37513107Sbde return (EINVAL); 3761541Srgrimes return (0); 3771541Srgrimes case GPROF_COUNT: 37812429Sphk return (sysctl_handle_opaque(oidp, 37912429Sphk gp->kcount, gp->kcountsize, req)); 3801541Srgrimes case GPROF_FROMS: 38112429Sphk return (sysctl_handle_opaque(oidp, 38212429Sphk gp->froms, gp->fromssize, req)); 3831541Srgrimes case GPROF_TOS: 38412429Sphk return (sysctl_handle_opaque(oidp, 38512429Sphk gp->tos, gp->tossize, req)); 3861541Srgrimes case GPROF_GMONPARAM: 38712429Sphk return (sysctl_handle_opaque(oidp, gp, sizeof *gp, req)); 3881541Srgrimes default: 3891541Srgrimes return (EOPNOTSUPP); 3901541Srgrimes } 3911541Srgrimes /* NOTREACHED */ 3921541Srgrimes} 39312429Sphk 39412429SphkSYSCTL_NODE(_kern, KERN_PROF, prof, CTLFLAG_RW, sysctl_kern_prof, ""); 3951541Srgrimes#endif /* GPROF */ 3961541Srgrimes 3971541Srgrimes/* 3981541Srgrimes * Profiling system call. 3991541Srgrimes * 4001541Srgrimes * The scale factor is a fixed point number with 16 bits of fraction, so that 4011541Srgrimes * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling. 4021541Srgrimes */ 40312221Sbde#ifndef _SYS_SYSPROTO_H_ 4041541Srgrimesstruct profil_args { 4051541Srgrimes caddr_t samples; 40638864Sbde size_t size; 40738864Sbde size_t offset; 4081541Srgrimes u_int scale; 4091541Srgrimes}; 41012221Sbde#endif 41182717Sdillon/* 41282717Sdillon * MPSAFE 41382717Sdillon */ 4141541Srgrimes/* ARGSUSED */ 4151549Srgrimesint 41683366Sjulianprofil(td, uap) 41783366Sjulian struct thread *td; 4181541Srgrimes register struct profil_args *uap; 4191541Srgrimes{ 4201541Srgrimes register struct uprof *upp; 4211541Srgrimes int s; 42282717Sdillon int error = 0; 4231541Srgrimes 42482717Sdillon mtx_lock(&Giant); 42582717Sdillon 42682717Sdillon if (uap->scale > (1 << 16)) { 42782717Sdillon error = EINVAL; 42882717Sdillon goto done2; 42982717Sdillon } 4301541Srgrimes if (uap->scale == 0) { 43183366Sjulian stopprofclock(td->td_proc); 43282717Sdillon goto done2; 4331541Srgrimes } 43483366Sjulian upp = &td->td_proc->p_stats->p_prof; 4351541Srgrimes 4361541Srgrimes /* Block profile interrupts while changing state. */ 4371541Srgrimes s = splstatclock(); 4381541Srgrimes upp->pr_off = uap->offset; 4391541Srgrimes upp->pr_scale = uap->scale; 4401541Srgrimes upp->pr_base = uap->samples; 4411541Srgrimes upp->pr_size = uap->size; 44283366Sjulian startprofclock(td->td_proc); 4431541Srgrimes splx(s); 4441541Srgrimes 44582717Sdillondone2: 44682717Sdillon mtx_unlock(&Giant); 44782717Sdillon return (error); 4481541Srgrimes} 4491541Srgrimes 4501541Srgrimes/* 4511541Srgrimes * Scale is a fixed-point number with the binary point 16 bits 4521541Srgrimes * into the value, and is <= 1.0. pc is at most 32 bits, so the 4531541Srgrimes * intermediate result is at most 48 bits. 4541541Srgrimes */ 4551541Srgrimes#define PC_TO_INDEX(pc, prof) \ 4561541Srgrimes ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ 4571541Srgrimes (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) 4581541Srgrimes 4591541Srgrimes/* 4601541Srgrimes * Collect user-level profiling statistics; called on a profiling tick, 4611541Srgrimes * when a process is running in user-mode. This routine may be called 4621541Srgrimes * from an interrupt context. We try to update the user profiling buffers 4631541Srgrimes * cheaply with fuswintr() and suswintr(). If that fails, we revert to 4641541Srgrimes * an AST that will vector us to trap() with a context in which copyin 4651541Srgrimes * and copyout will work. Trap will then call addupc_task(). 4661541Srgrimes * 4671541Srgrimes * Note that we may (rarely) not get around to the AST soon enough, and 4681541Srgrimes * lose profile ticks when the next tick overwrites this one, but in this 4691541Srgrimes * case the system is overloaded and the profile is probably already 4701541Srgrimes * inaccurate. 4711541Srgrimes */ 4721541Srgrimesvoid 47383366Sjulianaddupc_intr(ke, pc, ticks) 47483366Sjulian register struct kse *ke; 47572912Sjhb register uintptr_t pc; 4761541Srgrimes u_int ticks; 4771541Srgrimes{ 4781541Srgrimes register struct uprof *prof; 4791541Srgrimes register caddr_t addr; 4801541Srgrimes register u_int i; 4811541Srgrimes register int v; 4821541Srgrimes 4831541Srgrimes if (ticks == 0) 4841541Srgrimes return; 48583366Sjulian prof = &ke->ke_proc->p_stats->p_prof; 4861541Srgrimes if (pc < prof->pr_off || 4871541Srgrimes (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) 4881541Srgrimes return; /* out of range; ignore */ 4891541Srgrimes 4901541Srgrimes addr = prof->pr_base + i; 4911541Srgrimes if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) { 49281493Sjhb mtx_lock_spin(&sched_lock); 4931541Srgrimes prof->pr_addr = pc; 4941541Srgrimes prof->pr_ticks = ticks; 49583366Sjulian ke->ke_flags |= KEF_OWEUPC | KEF_ASTPENDING ; 49681493Sjhb mtx_unlock_spin(&sched_lock); 4971541Srgrimes } 4981541Srgrimes} 4991541Srgrimes 5001541Srgrimes/* 5011541Srgrimes * Much like before, but we can afford to take faults here. If the 5021541Srgrimes * update fails, we simply turn off profiling. 5031541Srgrimes */ 50413017Sbdevoid 50583366Sjulianaddupc_task(ke, pc, ticks) 50683366Sjulian register struct kse *ke; 50772912Sjhb register uintptr_t pc; 5081541Srgrimes u_int ticks; 5091541Srgrimes{ 51083366Sjulian struct proc *p = ke->ke_proc; 5111541Srgrimes register struct uprof *prof; 5121541Srgrimes register caddr_t addr; 5131541Srgrimes register u_int i; 5141541Srgrimes u_short v; 5151541Srgrimes 51688119Sjhb if (ticks == 0) 5171541Srgrimes return; 5181541Srgrimes 5191541Srgrimes prof = &p->p_stats->p_prof; 5201541Srgrimes if (pc < prof->pr_off || 5211541Srgrimes (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) 5221541Srgrimes return; 5231541Srgrimes 5241541Srgrimes addr = prof->pr_base + i; 52599012Salfred if (copyin(addr, &v, sizeof(v)) == 0) { 5261541Srgrimes v += ticks; 52799012Salfred if (copyout(&v, addr, sizeof(v)) == 0) 5281541Srgrimes return; 5291541Srgrimes } 5301541Srgrimes stopprofclock(p); 5311541Srgrimes} 532