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