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 * 4. Neither the name of the University nor the names of its contributors
141541Srgrimes *    may be used to endorse or promote products derived from this software
151541Srgrimes *    without specific prior written permission.
161541Srgrimes *
171541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271541Srgrimes * SUCH DAMAGE.
281541Srgrimes *
291541Srgrimes *	@(#)subr_prof.c	8.3 (Berkeley) 9/23/93
301541Srgrimes */
311541Srgrimes
32116182Sobrien#include <sys/cdefs.h>
33116182Sobrien__FBSDID("$FreeBSD$");
34116182Sobrien
351541Srgrimes#include <sys/param.h>
361541Srgrimes#include <sys/systm.h>
3712221Sbde#include <sys/sysproto.h>
3852147Sbde#include <sys/kernel.h>
3976166Smarkm#include <sys/lock.h>
4076166Smarkm#include <sys/mutex.h>
411541Srgrimes#include <sys/proc.h>
4212657Sbde#include <sys/resourcevar.h>
4352147Sbde#include <sys/sysctl.h>
447090Sbde
451541Srgrimes#include <machine/cpu.h>
461541Srgrimes
471541Srgrimes#ifdef GPROF
481541Srgrimes#include <sys/malloc.h>
491541Srgrimes#include <sys/gmon.h>
5046548Sbde#undef MCOUNT
511541Srgrimes
5230354Sphkstatic MALLOC_DEFINE(M_GPROF, "gprof", "kernel profiling buffer");
5330309Sphk
5492723Salfredstatic void kmstartup(void *);
55177253SrwatsonSYSINIT(kmem, SI_SUB_KPROF, SI_ORDER_FIRST, kmstartup, NULL);
5610358Sjulian
571541Srgrimesstruct gmonparam _gmonparam = { GMON_PROF_OFF };
581541Srgrimes
5919000Sbde#ifdef GUPROF
6019000Sbdevoid
6119000Sbdenullfunc_loop_profiled()
6219000Sbde{
6319000Sbde	int i;
6419000Sbde
6519000Sbde	for (i = 0; i < CALIB_SCALE; i++)
6619000Sbde		nullfunc_profiled();
6719000Sbde}
6819000Sbde
6920396Sbde#define	nullfunc_loop_profiled_end	nullfunc_profiled	/* XXX */
7020396Sbde
7119000Sbdevoid
7219000Sbdenullfunc_profiled()
7319000Sbde{
7419000Sbde}
7519000Sbde#endif /* GUPROF */
7619000Sbde
7785733Sgreen/*
7885733Sgreen * Update the histograms to support extending the text region arbitrarily.
7985733Sgreen * This is done slightly naively (no sparse regions), so will waste slight
8085733Sgreen * amounts of memory, but will overall work nicely enough to allow profiling
8185733Sgreen * of KLDs.
8285733Sgreen */
8385733Sgreenvoid
8485733Sgreenkmupetext(uintfptr_t nhighpc)
8585733Sgreen{
8685733Sgreen	struct gmonparam np;	/* slightly large */
8785733Sgreen	struct gmonparam *p = &_gmonparam;
8885733Sgreen	char *cp;
8985733Sgreen
9085733Sgreen	GIANT_REQUIRED;
9185733Sgreen	bcopy(p, &np, sizeof(*p));
9285733Sgreen	np.highpc = ROUNDUP(nhighpc, HISTFRACTION * sizeof(HISTCOUNTER));
9385733Sgreen	if (np.highpc <= p->highpc)
9485733Sgreen		return;
9585733Sgreen	np.textsize = np.highpc - p->lowpc;
9685733Sgreen	np.kcountsize = np.textsize / HISTFRACTION;
9785733Sgreen	np.hashfraction = HASHFRACTION;
9885733Sgreen	np.fromssize = np.textsize / HASHFRACTION;
9985733Sgreen	np.tolimit = np.textsize * ARCDENSITY / 100;
10085733Sgreen	if (np.tolimit < MINARCS)
10185733Sgreen		np.tolimit = MINARCS;
10285733Sgreen	else if (np.tolimit > MAXARCS)
10385733Sgreen		np.tolimit = MAXARCS;
10485733Sgreen	np.tossize = np.tolimit * sizeof(struct tostruct);
10585733Sgreen	cp = malloc(np.kcountsize + np.fromssize + np.tossize,
106111119Simp	    M_GPROF, M_WAITOK);
10785733Sgreen	/*
10885733Sgreen	 * Check for something else extending highpc while we slept.
10985733Sgreen	 */
11085733Sgreen	if (np.highpc <= p->highpc) {
11185733Sgreen		free(cp, M_GPROF);
11285733Sgreen		return;
11385733Sgreen	}
11485733Sgreen	np.tos = (struct tostruct *)cp;
11585733Sgreen	cp += np.tossize;
11685733Sgreen	np.kcount = (HISTCOUNTER *)cp;
11785733Sgreen	cp += np.kcountsize;
11885733Sgreen	np.froms = (u_short *)cp;
11985733Sgreen#ifdef GUPROF
12085733Sgreen	/* Reinitialize pointers to overhead counters. */
12185733Sgreen	np.cputime_count = &KCOUNT(&np, PC_TO_I(&np, cputime));
12285733Sgreen	np.mcount_count = &KCOUNT(&np, PC_TO_I(&np, mcount));
12385733Sgreen	np.mexitcount_count = &KCOUNT(&np, PC_TO_I(&np, mexitcount));
12485733Sgreen#endif
12588088Sjhb	critical_enter();
12685733Sgreen	bcopy(p->tos, np.tos, p->tossize);
12785733Sgreen	bzero((char *)np.tos + p->tossize, np.tossize - p->tossize);
12885733Sgreen	bcopy(p->kcount, np.kcount, p->kcountsize);
12985733Sgreen	bzero((char *)np.kcount + p->kcountsize, np.kcountsize -
13085733Sgreen	    p->kcountsize);
13185733Sgreen	bcopy(p->froms, np.froms, p->fromssize);
13285733Sgreen	bzero((char *)np.froms + p->fromssize, np.fromssize - p->fromssize);
13385733Sgreen	cp = (char *)p->tos;
13485733Sgreen	bcopy(&np, p, sizeof(*p));
13588088Sjhb	critical_exit();
13685733Sgreen	free(cp, M_GPROF);
13785733Sgreen}
13885733Sgreen
13910407Sbdestatic void
14012569Sbdekmstartup(dummy)
14112569Sbde	void *dummy;
1421541Srgrimes{
1431541Srgrimes	char *cp;
1441541Srgrimes	struct gmonparam *p = &_gmonparam;
14513107Sbde#ifdef GUPROF
14619000Sbde	int cputime_overhead;
14719000Sbde	int empty_loop_time;
14819000Sbde	int i;
14919000Sbde	int mcount_overhead;
15019000Sbde	int mexitcount_overhead;
15119000Sbde	int nullfunc_loop_overhead;
15219000Sbde	int nullfunc_loop_profiled_time;
15337629Sbde	uintfptr_t tmp_addr;
15413107Sbde#endif
15513107Sbde
1561541Srgrimes	/*
1571541Srgrimes	 * Round lowpc and highpc to multiples of the density we're using
1581541Srgrimes	 * so the rest of the scaling (here and in gprof) stays in ints.
1591541Srgrimes	 */
1606009Sbde	p->lowpc = ROUNDDOWN((u_long)btext, HISTFRACTION * sizeof(HISTCOUNTER));
1611541Srgrimes	p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
1621541Srgrimes	p->textsize = p->highpc - p->lowpc;
163129502Sbde	printf("Profiling kernel, textsize=%lu [%jx..%jx]\n",
164129502Sbde	    p->textsize, (uintmax_t)p->lowpc, (uintmax_t)p->highpc);
1651541Srgrimes	p->kcountsize = p->textsize / HISTFRACTION;
1661541Srgrimes	p->hashfraction = HASHFRACTION;
1671541Srgrimes	p->fromssize = p->textsize / HASHFRACTION;
1681541Srgrimes	p->tolimit = p->textsize * ARCDENSITY / 100;
1691541Srgrimes	if (p->tolimit < MINARCS)
1701541Srgrimes		p->tolimit = MINARCS;
1711541Srgrimes	else if (p->tolimit > MAXARCS)
1721541Srgrimes		p->tolimit = MAXARCS;
1731541Srgrimes	p->tossize = p->tolimit * sizeof(struct tostruct);
1741541Srgrimes	cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize,
175111119Simp	    M_GPROF, M_WAITOK | M_ZERO);
1761541Srgrimes	p->tos = (struct tostruct *)cp;
1771541Srgrimes	cp += p->tossize;
17813107Sbde	p->kcount = (HISTCOUNTER *)cp;
1791541Srgrimes	cp += p->kcountsize;
1801541Srgrimes	p->froms = (u_short *)cp;
181129501Sbde	p->histcounter_type = FUNCTION_ALIGNMENT / HISTFRACTION * NBBY;
18213107Sbde
18313107Sbde#ifdef GUPROF
184129501Sbde	/* Signed counters. */
185129501Sbde	p->histcounter_type = -p->histcounter_type;
186129501Sbde
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++)
226225617Skmacy		MCOUNT_OVERHEAD(sys_profil);
227225617Skmacy	mcount_overhead = KCOUNT(p, PC_TO_I(p, sys_profil));
22813107Sbde
22919000Sbde	startguprof(p);
23013107Sbde	for (i = 0; i < CALIB_SCALE; i++)
231129498Sbde		MEXITCOUNT_OVERHEAD();
232129498Sbde	MEXITCOUNT_OVERHEAD_GETLABEL(tmp_addr);
23320396Sbde	mexitcount_overhead = KCOUNT(p, PC_TO_I(p, tmp_addr));
23413107Sbde
23513107Sbde	p->state = GMON_PROF_OFF;
23619000Sbde	stopguprof(p);
23719000Sbde
23888088Sjhb	critical_exit();
23913107Sbde
24019000Sbde	nullfunc_loop_profiled_time = 0;
24137629Sbde	for (tmp_addr = (uintfptr_t)nullfunc_loop_profiled;
24237629Sbde	     tmp_addr < (uintfptr_t)nullfunc_loop_profiled_end;
24320396Sbde	     tmp_addr += HISTFRACTION * sizeof(HISTCOUNTER))
24420396Sbde		nullfunc_loop_profiled_time += KCOUNT(p, PC_TO_I(p, tmp_addr));
24519000Sbde#define CALIB_DOSCALE(count)	(((count) + CALIB_SCALE / 3) / CALIB_SCALE)
24619000Sbde#define	c2n(count, freq)	((int)((count) * 1000000000LL / freq))
24719000Sbde	printf("cputime %d, empty_loop %d, nullfunc_loop_profiled %d, mcount %d, mexitcount %d\n",
24819000Sbde	       CALIB_DOSCALE(c2n(cputime_overhead, p->profrate)),
24919000Sbde	       CALIB_DOSCALE(c2n(empty_loop_time, p->profrate)),
25019000Sbde	       CALIB_DOSCALE(c2n(nullfunc_loop_profiled_time, p->profrate)),
25119000Sbde	       CALIB_DOSCALE(c2n(mcount_overhead, p->profrate)),
25219000Sbde	       CALIB_DOSCALE(c2n(mexitcount_overhead, p->profrate)));
25319000Sbde	cputime_overhead -= empty_loop_time;
25419000Sbde	mcount_overhead -= empty_loop_time;
25519000Sbde	mexitcount_overhead -= empty_loop_time;
25619000Sbde
257210226Strasz	/*-
25819000Sbde	 * Profiling overheads are determined by the times between the
25919000Sbde	 * following events:
26019000Sbde	 *	MC1: mcount() is called
26119000Sbde	 *	MC2: cputime() (called from mcount()) latches the timer
26219000Sbde	 *	MC3: mcount() completes
26319000Sbde	 *	ME1: mexitcount() is called
26419000Sbde	 *	ME2: cputime() (called from mexitcount()) latches the timer
26519000Sbde	 *	ME3: mexitcount() completes.
26619000Sbde	 * The times between the events vary slightly depending on instruction
26719000Sbde	 * combination and cache misses, etc.  Attempt to determine the
26819000Sbde	 * minimum times.  These can be subtracted from the profiling times
26919000Sbde	 * without much risk of reducing the profiling times below what they
27019000Sbde	 * would be when profiling is not configured.  Abbreviate:
27119000Sbde	 *	ab = minimum time between MC1 and MC3
272298819Spfg	 *	a  = minimum time between MC1 and MC2
27319000Sbde	 *	b  = minimum time between MC2 and MC3
27419000Sbde	 *	cd = minimum time between ME1 and ME3
27519000Sbde	 *	c  = minimum time between ME1 and ME2
27619000Sbde	 *	d  = minimum time between ME2 and ME3.
27719000Sbde	 * These satisfy the relations:
27819000Sbde	 *	ab            <= mcount_overhead		(just measured)
27919000Sbde	 *	a + b         <= ab
28019000Sbde	 *	        cd    <= mexitcount_overhead		(just measured)
28119000Sbde	 *	        c + d <= cd
28219000Sbde	 *	a         + d <= nullfunc_loop_profiled_time	(just measured)
28319000Sbde	 *	a >= 0, b >= 0, c >= 0, d >= 0.
28419000Sbde	 * Assume that ab and cd are equal to the minimums.
28519000Sbde	 */
28619000Sbde	p->cputime_overhead = CALIB_DOSCALE(cputime_overhead);
28719000Sbde	p->mcount_overhead = CALIB_DOSCALE(mcount_overhead - cputime_overhead);
28819000Sbde	p->mexitcount_overhead = CALIB_DOSCALE(mexitcount_overhead
28919000Sbde					       - cputime_overhead);
29019000Sbde	nullfunc_loop_overhead = nullfunc_loop_profiled_time - empty_loop_time;
29119000Sbde	p->mexitcount_post_overhead = CALIB_DOSCALE((mcount_overhead
29219000Sbde						     - nullfunc_loop_overhead)
29319000Sbde						    / 4);
29419000Sbde	p->mexitcount_pre_overhead = p->mexitcount_overhead
29519000Sbde				     + p->cputime_overhead
29619000Sbde				     - p->mexitcount_post_overhead;
29719000Sbde	p->mcount_pre_overhead = CALIB_DOSCALE(nullfunc_loop_overhead)
29819000Sbde				 - p->mexitcount_post_overhead;
29919000Sbde	p->mcount_post_overhead = p->mcount_overhead
30019000Sbde				  + p->cputime_overhead
30119000Sbde				  - p->mcount_pre_overhead;
30219000Sbde	printf(
30319000Sbde"Profiling overheads: mcount: %d+%d, %d+%d; mexitcount: %d+%d, %d+%d nsec\n",
30419000Sbde	       c2n(p->cputime_overhead, p->profrate),
30519000Sbde	       c2n(p->mcount_overhead, p->profrate),
30619000Sbde	       c2n(p->mcount_pre_overhead, p->profrate),
30719000Sbde	       c2n(p->mcount_post_overhead, p->profrate),
30819000Sbde	       c2n(p->cputime_overhead, p->profrate),
30919000Sbde	       c2n(p->mexitcount_overhead, p->profrate),
31019000Sbde	       c2n(p->mexitcount_pre_overhead, p->profrate),
31119000Sbde	       c2n(p->mexitcount_post_overhead, p->profrate));
31219000Sbde	printf(
31319000Sbde"Profiling overheads: mcount: %d+%d, %d+%d; mexitcount: %d+%d, %d+%d cycles\n",
31419000Sbde	       p->cputime_overhead, p->mcount_overhead,
31519000Sbde	       p->mcount_pre_overhead, p->mcount_post_overhead,
31619000Sbde	       p->cputime_overhead, p->mexitcount_overhead,
31719000Sbde	       p->mexitcount_pre_overhead, p->mexitcount_post_overhead);
31813107Sbde#endif /* GUPROF */
3191541Srgrimes}
3201541Srgrimes
3211541Srgrimes/*
3221541Srgrimes * Return kernel profiling information.
3231541Srgrimes */
32412429Sphkstatic int
32562573Sphksysctl_kern_prof(SYSCTL_HANDLER_ARGS)
3261541Srgrimes{
32712429Sphk	int *name = (int *) arg1;
32812429Sphk	u_int namelen = arg2;
3291541Srgrimes	struct gmonparam *gp = &_gmonparam;
3301541Srgrimes	int error;
33113107Sbde	int state;
3321541Srgrimes
3331541Srgrimes	/* all sysctl names at this level are terminal */
3341541Srgrimes	if (namelen != 1)
3351541Srgrimes		return (ENOTDIR);		/* overloaded */
3361541Srgrimes
3371541Srgrimes	switch (name[0]) {
3381541Srgrimes	case GPROF_STATE:
33913107Sbde		state = gp->state;
34013107Sbde		error = sysctl_handle_int(oidp, &state, 0, req);
3411541Srgrimes		if (error)
3421541Srgrimes			return (error);
34313107Sbde		if (!req->newptr)
34413107Sbde			return (0);
34513107Sbde		if (state == GMON_PROF_OFF) {
34619000Sbde			gp->state = state;
347110530Sjulian			PROC_LOCK(&proc0);
3481541Srgrimes			stopprofclock(&proc0);
349110530Sjulian			PROC_UNLOCK(&proc0);
35019000Sbde			stopguprof(gp);
35113107Sbde		} else if (state == GMON_PROF_ON) {
35219000Sbde			gp->state = GMON_PROF_OFF;
35319000Sbde			stopguprof(gp);
35413107Sbde			gp->profrate = profhz;
355113874Sjhb			PROC_LOCK(&proc0);
35619000Sbde			startprofclock(&proc0);
357113874Sjhb			PROC_UNLOCK(&proc0);
35813107Sbde			gp->state = state;
35913107Sbde#ifdef GUPROF
36013107Sbde		} else if (state == GMON_PROF_HIRES) {
36119000Sbde			gp->state = GMON_PROF_OFF;
362110530Sjulian			PROC_LOCK(&proc0);
36313107Sbde			stopprofclock(&proc0);
364110530Sjulian			PROC_UNLOCK(&proc0);
36519000Sbde			startguprof(gp);
36613107Sbde			gp->state = state;
36713107Sbde#endif
36813107Sbde		} else if (state != gp->state)
36913107Sbde			return (EINVAL);
3701541Srgrimes		return (0);
3711541Srgrimes	case GPROF_COUNT:
37212429Sphk		return (sysctl_handle_opaque(oidp,
37312429Sphk			gp->kcount, gp->kcountsize, req));
3741541Srgrimes	case GPROF_FROMS:
37512429Sphk		return (sysctl_handle_opaque(oidp,
37612429Sphk			gp->froms, gp->fromssize, req));
3771541Srgrimes	case GPROF_TOS:
37812429Sphk		return (sysctl_handle_opaque(oidp,
37912429Sphk			gp->tos, gp->tossize, req));
3801541Srgrimes	case GPROF_GMONPARAM:
38112429Sphk		return (sysctl_handle_opaque(oidp, gp, sizeof *gp, req));
3821541Srgrimes	default:
3831541Srgrimes		return (EOPNOTSUPP);
3841541Srgrimes	}
3851541Srgrimes	/* NOTREACHED */
3861541Srgrimes}
38712429Sphk
388227309Sedstatic SYSCTL_NODE(_kern, KERN_PROF, prof, CTLFLAG_RW, sysctl_kern_prof, "");
3891541Srgrimes#endif /* GPROF */
3901541Srgrimes
3911541Srgrimes/*
3921541Srgrimes * Profiling system call.
3931541Srgrimes *
3941541Srgrimes * The scale factor is a fixed point number with 16 bits of fraction, so that
3951541Srgrimes * 1.0 is represented as 0x10000.  A scale factor of 0 turns off profiling.
3961541Srgrimes */
39712221Sbde#ifndef _SYS_SYSPROTO_H_
3981541Srgrimesstruct profil_args {
3991541Srgrimes	caddr_t	samples;
40038864Sbde	size_t	size;
40138864Sbde	size_t	offset;
4021541Srgrimes	u_int	scale;
4031541Srgrimes};
40412221Sbde#endif
4051541Srgrimes/* ARGSUSED */
4061549Srgrimesint
407225617Skmacysys_profil(struct thread *td, struct profil_args *uap)
4081541Srgrimes{
409110530Sjulian	struct uprof *upp;
410113874Sjhb	struct proc *p;
4111541Srgrimes
412113874Sjhb	if (uap->scale > (1 << 16))
413113874Sjhb		return (EINVAL);
41482717Sdillon
415113874Sjhb	p = td->td_proc;
4161541Srgrimes	if (uap->scale == 0) {
417131437Sjhb		PROC_LOCK(p);
418131437Sjhb		stopprofclock(p);
419131437Sjhb		PROC_UNLOCK(p);
420113874Sjhb		return (0);
4211541Srgrimes	}
422131437Sjhb	PROC_LOCK(p);
42383366Sjulian	upp = &td->td_proc->p_stats->p_prof;
424275121Skib	PROC_PROFLOCK(p);
4251541Srgrimes	upp->pr_off = uap->offset;
4261541Srgrimes	upp->pr_scale = uap->scale;
4271541Srgrimes	upp->pr_base = uap->samples;
4281541Srgrimes	upp->pr_size = uap->size;
429275121Skib	PROC_PROFUNLOCK(p);
430113874Sjhb	startprofclock(p);
431113874Sjhb	PROC_UNLOCK(p);
4321541Srgrimes
433113874Sjhb	return (0);
4341541Srgrimes}
4351541Srgrimes
4361541Srgrimes/*
4371541Srgrimes * Scale is a fixed-point number with the binary point 16 bits
4381541Srgrimes * into the value, and is <= 1.0.  pc is at most 32 bits, so the
4391541Srgrimes * intermediate result is at most 48 bits.
4401541Srgrimes */
4411541Srgrimes#define	PC_TO_INDEX(pc, prof) \
4421541Srgrimes	((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
4431541Srgrimes	    (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
4441541Srgrimes
4451541Srgrimes/*
4461541Srgrimes * Collect user-level profiling statistics; called on a profiling tick,
4471541Srgrimes * when a process is running in user-mode.  This routine may be called
4481541Srgrimes * from an interrupt context.  We try to update the user profiling buffers
4491541Srgrimes * cheaply with fuswintr() and suswintr().  If that fails, we revert to
4501541Srgrimes * an AST that will vector us to trap() with a context in which copyin
4511541Srgrimes * and copyout will work.  Trap will then call addupc_task().
4521541Srgrimes *
4531541Srgrimes * Note that we may (rarely) not get around to the AST soon enough, and
4541541Srgrimes * lose profile ticks when the next tick overwrites this one, but in this
4551541Srgrimes * case the system is overloaded and the profile is probably already
4561541Srgrimes * inaccurate.
4571541Srgrimes */
4581541Srgrimesvoid
459153489Sjhbaddupc_intr(struct thread *td, uintfptr_t pc, u_int ticks)
4601541Srgrimes{
461110530Sjulian	struct uprof *prof;
462110530Sjulian	caddr_t addr;
463110530Sjulian	u_int i;
464110530Sjulian	int v;
4651541Srgrimes
4661541Srgrimes	if (ticks == 0)
4671541Srgrimes		return;
468111032Sjulian	prof = &td->td_proc->p_stats->p_prof;
469275121Skib	PROC_PROFLOCK(td->td_proc);
4701541Srgrimes	if (pc < prof->pr_off ||
471131437Sjhb	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) {
472275121Skib		PROC_PROFUNLOCK(td->td_proc);
4731541Srgrimes		return;			/* out of range; ignore */
474131437Sjhb	}
4751541Srgrimes
4761541Srgrimes	addr = prof->pr_base + i;
477275121Skib	PROC_PROFUNLOCK(td->td_proc);
4781541Srgrimes	if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) {
479132266Sjhb		td->td_profil_addr = pc;
480132266Sjhb		td->td_profil_ticks = ticks;
481132266Sjhb		td->td_pflags |= TDP_OWEUPC;
482170307Sjeff		thread_lock(td);
483132266Sjhb		td->td_flags |= TDF_ASTPENDING;
484170307Sjeff		thread_unlock(td);
4851541Srgrimes	}
4861541Srgrimes}
4871541Srgrimes
4881541Srgrimes/*
4891541Srgrimes * Much like before, but we can afford to take faults here.  If the
4901541Srgrimes * update fails, we simply turn off profiling.
4911541Srgrimes */
49213017Sbdevoid
493153489Sjhbaddupc_task(struct thread *td, uintfptr_t pc, u_int ticks)
4941541Srgrimes{
495111032Sjulian	struct proc *p = td->td_proc;
496110530Sjulian	struct uprof *prof;
497110530Sjulian	caddr_t addr;
498110530Sjulian	u_int i;
4991541Srgrimes	u_short v;
500110530Sjulian	int stop = 0;
5011541Srgrimes
50288119Sjhb	if (ticks == 0)
5031541Srgrimes		return;
5041541Srgrimes
505110530Sjulian	PROC_LOCK(p);
506113874Sjhb	if (!(p->p_flag & P_PROFIL)) {
507110530Sjulian		PROC_UNLOCK(p);
508110530Sjulian		return;
509110530Sjulian	}
510110530Sjulian	p->p_profthreads++;
5111541Srgrimes	prof = &p->p_stats->p_prof;
512275121Skib	PROC_PROFLOCK(p);
5131541Srgrimes	if (pc < prof->pr_off ||
514110530Sjulian	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) {
515275121Skib		PROC_PROFUNLOCK(p);
516110530Sjulian		goto out;
517110530Sjulian	}
5181541Srgrimes
5191541Srgrimes	addr = prof->pr_base + i;
520275121Skib	PROC_PROFUNLOCK(p);
521131437Sjhb	PROC_UNLOCK(p);
52299012Salfred	if (copyin(addr, &v, sizeof(v)) == 0) {
5231541Srgrimes		v += ticks;
524131437Sjhb		if (copyout(&v, addr, sizeof(v)) == 0) {
525131437Sjhb			PROC_LOCK(p);
526110530Sjulian			goto out;
527131437Sjhb		}
5281541Srgrimes	}
529110530Sjulian	stop = 1;
530131437Sjhb	PROC_LOCK(p);
531110530Sjulian
532110530Sjulianout:
533110530Sjulian	if (--p->p_profthreads == 0) {
534113874Sjhb		if (p->p_flag & P_STOPPROF) {
535110530Sjulian			wakeup(&p->p_profthreads);
536274343Skib			p->p_flag &= ~P_STOPPROF;
537110530Sjulian			stop = 0;
538110530Sjulian		}
539110530Sjulian	}
540110530Sjulian	if (stop)
541110530Sjulian		stopprofclock(p);
542110530Sjulian	PROC_UNLOCK(p);
5431541Srgrimes}
544