subr_prof.c revision 30309
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
3430309Sphk * $Id: subr_prof.c,v 1.20 1997/02/22 09:39:17 peter 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
5130309SphkMALLOC_DEFINE(M_GPROF, "gprof", "kernel profiling buffer");
5230309Sphk
5310653Sdgstatic void kmstartup __P((void *));
5410358SjulianSYSINIT(kmem, SI_SUB_KPROF, SI_ORDER_FIRST, kmstartup, NULL)
5510358Sjulian
561541Srgrimesstruct gmonparam _gmonparam = { GMON_PROF_OFF };
571541Srgrimes
586009Sbdeextern char btext[];
591541Srgrimesextern char etext[];
601541Srgrimes
6119000Sbde#ifdef GUPROF
6219000Sbdevoid
6319000Sbdenullfunc_loop_profiled()
6419000Sbde{
6519000Sbde	int i;
6619000Sbde
6719000Sbde	for (i = 0; i < CALIB_SCALE; i++)
6819000Sbde		nullfunc_profiled();
6919000Sbde}
7019000Sbde
7120396Sbde#define	nullfunc_loop_profiled_end	nullfunc_profiled	/* XXX */
7220396Sbde
7319000Sbdevoid
7419000Sbdenullfunc_profiled()
7519000Sbde{
7619000Sbde}
7719000Sbde#endif /* GUPROF */
7819000Sbde
7910407Sbdestatic void
8012569Sbdekmstartup(dummy)
8112569Sbde	void *dummy;
821541Srgrimes{
831541Srgrimes	char *cp;
841541Srgrimes	struct gmonparam *p = &_gmonparam;
8513107Sbde#ifdef GUPROF
8619000Sbde	int cputime_overhead;
8719000Sbde	int empty_loop_time;
8819000Sbde	int i;
8919000Sbde	int mcount_overhead;
9019000Sbde	int mexitcount_overhead;
9119000Sbde	int nullfunc_loop_overhead;
9219000Sbde	int nullfunc_loop_profiled_time;
9320396Sbde	fptrint_t tmp_addr;
9413107Sbde#endif
9513107Sbde
961541Srgrimes	/*
971541Srgrimes	 * Round lowpc and highpc to multiples of the density we're using
981541Srgrimes	 * so the rest of the scaling (here and in gprof) stays in ints.
991541Srgrimes	 */
1006009Sbde	p->lowpc = ROUNDDOWN((u_long)btext, HISTFRACTION * sizeof(HISTCOUNTER));
1011541Srgrimes	p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
1021541Srgrimes	p->textsize = p->highpc - p->lowpc;
10319000Sbde	printf("Profiling kernel, textsize=%lu [%x..%x]\n",
1041541Srgrimes	       p->textsize, p->lowpc, p->highpc);
1051541Srgrimes	p->kcountsize = p->textsize / HISTFRACTION;
1061541Srgrimes	p->hashfraction = HASHFRACTION;
1071541Srgrimes	p->fromssize = p->textsize / HASHFRACTION;
1081541Srgrimes	p->tolimit = p->textsize * ARCDENSITY / 100;
1091541Srgrimes	if (p->tolimit < MINARCS)
1101541Srgrimes		p->tolimit = MINARCS;
1111541Srgrimes	else if (p->tolimit > MAXARCS)
1121541Srgrimes		p->tolimit = MAXARCS;
1131541Srgrimes	p->tossize = p->tolimit * sizeof(struct tostruct);
1141541Srgrimes	cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize,
1151541Srgrimes	    M_GPROF, M_NOWAIT);
1161541Srgrimes	if (cp == 0) {
1171541Srgrimes		printf("No memory for profiling.\n");
1181541Srgrimes		return;
1191541Srgrimes	}
1201541Srgrimes	bzero(cp, p->kcountsize + p->tossize + p->fromssize);
1211541Srgrimes	p->tos = (struct tostruct *)cp;
1221541Srgrimes	cp += p->tossize;
12313107Sbde	p->kcount = (HISTCOUNTER *)cp;
1241541Srgrimes	cp += p->kcountsize;
1251541Srgrimes	p->froms = (u_short *)cp;
12613107Sbde
12713107Sbde#ifdef GUPROF
12819000Sbde	/* Initialize pointers to overhead counters. */
12913107Sbde	p->cputime_count = &KCOUNT(p, PC_TO_I(p, cputime));
13013107Sbde	p->mcount_count = &KCOUNT(p, PC_TO_I(p, mcount));
13113107Sbde	p->mexitcount_count = &KCOUNT(p, PC_TO_I(p, mexitcount));
13213107Sbde
13313107Sbde	/*
13419000Sbde	 * Disable interrupts to avoid interference while we calibrate
13519000Sbde	 * things.
13613107Sbde	 */
13713107Sbde	disable_intr();
13813107Sbde
13919000Sbde	/*
14019000Sbde	 * Determine overheads.
14119000Sbde	 * XXX this needs to be repeated for each useful timer/counter.
14219000Sbde	 */
14319000Sbde	cputime_overhead = 0;
14419000Sbde	startguprof(p);
14513107Sbde	for (i = 0; i < CALIB_SCALE; i++)
14619000Sbde		cputime_overhead += cputime();
14713107Sbde
14819000Sbde	empty_loop();
14919000Sbde	startguprof(p);
15019000Sbde	empty_loop();
15119000Sbde	empty_loop_time = cputime();
15219000Sbde
15319000Sbde	nullfunc_loop_profiled();
15419000Sbde
15519000Sbde	/*
15619000Sbde	 * Start profiling.  There won't be any normal function calls since
15719000Sbde	 * interrupts are disabled, but we will call the profiling routines
15819000Sbde	 * directly to determine their overheads.
15919000Sbde	 */
16019000Sbde	p->state = GMON_PROF_HIRES;
16119000Sbde
16219000Sbde	startguprof(p);
16319000Sbde	nullfunc_loop_profiled();
16419000Sbde
16519000Sbde	startguprof(p);
16613107Sbde	for (i = 0; i < CALIB_SCALE; i++)
16713107Sbde#if defined(i386) && __GNUC__ >= 2
16813107Sbde		asm("pushl %0; call __mcount; popl %%ecx"
16913107Sbde		    :
17019000Sbde		    : "i" (profil)
17113107Sbde		    : "ax", "bx", "cx", "dx", "memory");
17213107Sbde#else
17313107Sbde#error
17413107Sbde#endif
17519000Sbde	mcount_overhead = KCOUNT(p, PC_TO_I(p, profil));
17613107Sbde
17719000Sbde	startguprof(p);
17813107Sbde	for (i = 0; i < CALIB_SCALE; i++)
17913107Sbde#if defined(i386) && __GNUC__ >= 2
18013107Sbde		    asm("call mexitcount; 1:"
18113107Sbde			: : : "ax", "bx", "cx", "dx", "memory");
18220396Sbde	asm("movl $1b,%0" : "=rm" (tmp_addr));
18313107Sbde#else
18413107Sbde#error
18513107Sbde#endif
18620396Sbde	mexitcount_overhead = KCOUNT(p, PC_TO_I(p, tmp_addr));
18713107Sbde
18813107Sbde	p->state = GMON_PROF_OFF;
18919000Sbde	stopguprof(p);
19019000Sbde
19113107Sbde	enable_intr();
19213107Sbde
19319000Sbde	nullfunc_loop_profiled_time = 0;
19420396Sbde	for (tmp_addr = (fptrint_t)nullfunc_loop_profiled;
19520396Sbde	     tmp_addr < (fptrint_t)nullfunc_loop_profiled_end;
19620396Sbde	     tmp_addr += HISTFRACTION * sizeof(HISTCOUNTER))
19720396Sbde		nullfunc_loop_profiled_time += KCOUNT(p, PC_TO_I(p, tmp_addr));
19819000Sbde#define CALIB_DOSCALE(count)	(((count) + CALIB_SCALE / 3) / CALIB_SCALE)
19919000Sbde#define	c2n(count, freq)	((int)((count) * 1000000000LL / freq))
20019000Sbde	printf("cputime %d, empty_loop %d, nullfunc_loop_profiled %d, mcount %d, mexitcount %d\n",
20119000Sbde	       CALIB_DOSCALE(c2n(cputime_overhead, p->profrate)),
20219000Sbde	       CALIB_DOSCALE(c2n(empty_loop_time, p->profrate)),
20319000Sbde	       CALIB_DOSCALE(c2n(nullfunc_loop_profiled_time, p->profrate)),
20419000Sbde	       CALIB_DOSCALE(c2n(mcount_overhead, p->profrate)),
20519000Sbde	       CALIB_DOSCALE(c2n(mexitcount_overhead, p->profrate)));
20619000Sbde	cputime_overhead -= empty_loop_time;
20719000Sbde	mcount_overhead -= empty_loop_time;
20819000Sbde	mexitcount_overhead -= empty_loop_time;
20919000Sbde
21019000Sbde	/*-
21119000Sbde	 * Profiling overheads are determined by the times between the
21219000Sbde	 * following events:
21319000Sbde	 *	MC1: mcount() is called
21419000Sbde	 *	MC2: cputime() (called from mcount()) latches the timer
21519000Sbde	 *	MC3: mcount() completes
21619000Sbde	 *	ME1: mexitcount() is called
21719000Sbde	 *	ME2: cputime() (called from mexitcount()) latches the timer
21819000Sbde	 *	ME3: mexitcount() completes.
21919000Sbde	 * The times between the events vary slightly depending on instruction
22019000Sbde	 * combination and cache misses, etc.  Attempt to determine the
22119000Sbde	 * minimum times.  These can be subtracted from the profiling times
22219000Sbde	 * without much risk of reducing the profiling times below what they
22319000Sbde	 * would be when profiling is not configured.  Abbreviate:
22419000Sbde	 *	ab = minimum time between MC1 and MC3
22519000Sbde	 *	a  = minumum time between MC1 and MC2
22619000Sbde	 *	b  = minimum time between MC2 and MC3
22719000Sbde	 *	cd = minimum time between ME1 and ME3
22819000Sbde	 *	c  = minimum time between ME1 and ME2
22919000Sbde	 *	d  = minimum time between ME2 and ME3.
23019000Sbde	 * These satisfy the relations:
23119000Sbde	 *	ab            <= mcount_overhead		(just measured)
23219000Sbde	 *	a + b         <= ab
23319000Sbde	 *	        cd    <= mexitcount_overhead		(just measured)
23419000Sbde	 *	        c + d <= cd
23519000Sbde	 *	a         + d <= nullfunc_loop_profiled_time	(just measured)
23619000Sbde	 *	a >= 0, b >= 0, c >= 0, d >= 0.
23719000Sbde	 * Assume that ab and cd are equal to the minimums.
23819000Sbde	 */
23919000Sbde	p->cputime_overhead = CALIB_DOSCALE(cputime_overhead);
24019000Sbde	p->mcount_overhead = CALIB_DOSCALE(mcount_overhead - cputime_overhead);
24119000Sbde	p->mexitcount_overhead = CALIB_DOSCALE(mexitcount_overhead
24219000Sbde					       - cputime_overhead);
24319000Sbde	nullfunc_loop_overhead = nullfunc_loop_profiled_time - empty_loop_time;
24419000Sbde	p->mexitcount_post_overhead = CALIB_DOSCALE((mcount_overhead
24519000Sbde						     - nullfunc_loop_overhead)
24619000Sbde						    / 4);
24719000Sbde	p->mexitcount_pre_overhead = p->mexitcount_overhead
24819000Sbde				     + p->cputime_overhead
24919000Sbde				     - p->mexitcount_post_overhead;
25019000Sbde	p->mcount_pre_overhead = CALIB_DOSCALE(nullfunc_loop_overhead)
25119000Sbde				 - p->mexitcount_post_overhead;
25219000Sbde	p->mcount_post_overhead = p->mcount_overhead
25319000Sbde				  + p->cputime_overhead
25419000Sbde				  - p->mcount_pre_overhead;
25519000Sbde	printf(
25619000Sbde"Profiling overheads: mcount: %d+%d, %d+%d; mexitcount: %d+%d, %d+%d nsec\n",
25719000Sbde	       c2n(p->cputime_overhead, p->profrate),
25819000Sbde	       c2n(p->mcount_overhead, p->profrate),
25919000Sbde	       c2n(p->mcount_pre_overhead, p->profrate),
26019000Sbde	       c2n(p->mcount_post_overhead, p->profrate),
26119000Sbde	       c2n(p->cputime_overhead, p->profrate),
26219000Sbde	       c2n(p->mexitcount_overhead, p->profrate),
26319000Sbde	       c2n(p->mexitcount_pre_overhead, p->profrate),
26419000Sbde	       c2n(p->mexitcount_post_overhead, p->profrate));
26519000Sbde	printf(
26619000Sbde"Profiling overheads: mcount: %d+%d, %d+%d; mexitcount: %d+%d, %d+%d cycles\n",
26719000Sbde	       p->cputime_overhead, p->mcount_overhead,
26819000Sbde	       p->mcount_pre_overhead, p->mcount_post_overhead,
26919000Sbde	       p->cputime_overhead, p->mexitcount_overhead,
27019000Sbde	       p->mexitcount_pre_overhead, p->mexitcount_post_overhead);
27113107Sbde#endif /* GUPROF */
2721541Srgrimes}
2731541Srgrimes
2741541Srgrimes/*
2751541Srgrimes * Return kernel profiling information.
2761541Srgrimes */
27712429Sphkstatic int
27812429Sphksysctl_kern_prof SYSCTL_HANDLER_ARGS
2791541Srgrimes{
28012429Sphk	int *name = (int *) arg1;
28112429Sphk	u_int namelen = arg2;
2821541Srgrimes	struct gmonparam *gp = &_gmonparam;
2831541Srgrimes	int error;
28413107Sbde	int state;
2851541Srgrimes
2861541Srgrimes	/* all sysctl names at this level are terminal */
2871541Srgrimes	if (namelen != 1)
2881541Srgrimes		return (ENOTDIR);		/* overloaded */
2891541Srgrimes
2901541Srgrimes	switch (name[0]) {
2911541Srgrimes	case GPROF_STATE:
29213107Sbde		state = gp->state;
29313107Sbde		error = sysctl_handle_int(oidp, &state, 0, req);
2941541Srgrimes		if (error)
2951541Srgrimes			return (error);
29613107Sbde		if (!req->newptr)
29713107Sbde			return (0);
29813107Sbde		if (state == GMON_PROF_OFF) {
29919000Sbde			gp->state = state;
3001541Srgrimes			stopprofclock(&proc0);
30119000Sbde			stopguprof(gp);
30213107Sbde		} else if (state == GMON_PROF_ON) {
30319000Sbde			gp->state = GMON_PROF_OFF;
30419000Sbde			stopguprof(gp);
30513107Sbde			gp->profrate = profhz;
30619000Sbde			startprofclock(&proc0);
30713107Sbde			gp->state = state;
30813107Sbde#ifdef GUPROF
30913107Sbde		} else if (state == GMON_PROF_HIRES) {
31019000Sbde			gp->state = GMON_PROF_OFF;
31113107Sbde			stopprofclock(&proc0);
31219000Sbde			startguprof(gp);
31313107Sbde			gp->state = state;
31413107Sbde#endif
31513107Sbde		} else if (state != gp->state)
31613107Sbde			return (EINVAL);
3171541Srgrimes		return (0);
3181541Srgrimes	case GPROF_COUNT:
31912429Sphk		return (sysctl_handle_opaque(oidp,
32012429Sphk			gp->kcount, gp->kcountsize, req));
3211541Srgrimes	case GPROF_FROMS:
32212429Sphk		return (sysctl_handle_opaque(oidp,
32312429Sphk			gp->froms, gp->fromssize, req));
3241541Srgrimes	case GPROF_TOS:
32512429Sphk		return (sysctl_handle_opaque(oidp,
32612429Sphk			gp->tos, gp->tossize, req));
3271541Srgrimes	case GPROF_GMONPARAM:
32812429Sphk		return (sysctl_handle_opaque(oidp, gp, sizeof *gp, req));
3291541Srgrimes	default:
3301541Srgrimes		return (EOPNOTSUPP);
3311541Srgrimes	}
3321541Srgrimes	/* NOTREACHED */
3331541Srgrimes}
33412429Sphk
33512429SphkSYSCTL_NODE(_kern, KERN_PROF, prof, CTLFLAG_RW, sysctl_kern_prof, "");
3361541Srgrimes#endif /* GPROF */
3371541Srgrimes
3381541Srgrimes/*
3391541Srgrimes * Profiling system call.
3401541Srgrimes *
3411541Srgrimes * The scale factor is a fixed point number with 16 bits of fraction, so that
3421541Srgrimes * 1.0 is represented as 0x10000.  A scale factor of 0 turns off profiling.
3431541Srgrimes */
34412221Sbde#ifndef _SYS_SYSPROTO_H_
3451541Srgrimesstruct profil_args {
3461541Srgrimes	caddr_t	samples;
3471541Srgrimes	u_int	size;
3481541Srgrimes	u_int	offset;
3491541Srgrimes	u_int	scale;
3501541Srgrimes};
35112221Sbde#endif
3521541Srgrimes/* ARGSUSED */
3531549Srgrimesint
3541541Srgrimesprofil(p, uap, retval)
3551541Srgrimes	struct proc *p;
3561541Srgrimes	register struct profil_args *uap;
3571541Srgrimes	int *retval;
3581541Srgrimes{
3591541Srgrimes	register struct uprof *upp;
3601541Srgrimes	int s;
3611541Srgrimes
3621541Srgrimes	if (uap->scale > (1 << 16))
3631541Srgrimes		return (EINVAL);
3641541Srgrimes	if (uap->scale == 0) {
3651541Srgrimes		stopprofclock(p);
3661541Srgrimes		return (0);
3671541Srgrimes	}
3681541Srgrimes	upp = &p->p_stats->p_prof;
3691541Srgrimes
3701541Srgrimes	/* Block profile interrupts while changing state. */
3711541Srgrimes	s = splstatclock();
3721541Srgrimes	upp->pr_off = uap->offset;
3731541Srgrimes	upp->pr_scale = uap->scale;
3741541Srgrimes	upp->pr_base = uap->samples;
3751541Srgrimes	upp->pr_size = uap->size;
3761541Srgrimes	startprofclock(p);
3771541Srgrimes	splx(s);
3781541Srgrimes
3791541Srgrimes	return (0);
3801541Srgrimes}
3811541Srgrimes
3821541Srgrimes/*
3831541Srgrimes * Scale is a fixed-point number with the binary point 16 bits
3841541Srgrimes * into the value, and is <= 1.0.  pc is at most 32 bits, so the
3851541Srgrimes * intermediate result is at most 48 bits.
3861541Srgrimes */
3871541Srgrimes#define	PC_TO_INDEX(pc, prof) \
3881541Srgrimes	((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
3891541Srgrimes	    (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
3901541Srgrimes
3911541Srgrimes/*
3921541Srgrimes * Collect user-level profiling statistics; called on a profiling tick,
3931541Srgrimes * when a process is running in user-mode.  This routine may be called
3941541Srgrimes * from an interrupt context.  We try to update the user profiling buffers
3951541Srgrimes * cheaply with fuswintr() and suswintr().  If that fails, we revert to
3961541Srgrimes * an AST that will vector us to trap() with a context in which copyin
3971541Srgrimes * and copyout will work.  Trap will then call addupc_task().
3981541Srgrimes *
3991541Srgrimes * Note that we may (rarely) not get around to the AST soon enough, and
4001541Srgrimes * lose profile ticks when the next tick overwrites this one, but in this
4011541Srgrimes * case the system is overloaded and the profile is probably already
4021541Srgrimes * inaccurate.
4031541Srgrimes */
4041541Srgrimesvoid
4051541Srgrimesaddupc_intr(p, pc, ticks)
4061541Srgrimes	register struct proc *p;
4071541Srgrimes	register u_long pc;
4081541Srgrimes	u_int ticks;
4091541Srgrimes{
4101541Srgrimes	register struct uprof *prof;
4111541Srgrimes	register caddr_t addr;
4121541Srgrimes	register u_int i;
4131541Srgrimes	register int v;
4141541Srgrimes
4151541Srgrimes	if (ticks == 0)
4161541Srgrimes		return;
4171541Srgrimes	prof = &p->p_stats->p_prof;
4181541Srgrimes	if (pc < prof->pr_off ||
4191541Srgrimes	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
4201541Srgrimes		return;			/* out of range; ignore */
4211541Srgrimes
4221541Srgrimes	addr = prof->pr_base + i;
4231541Srgrimes	if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) {
4241541Srgrimes		prof->pr_addr = pc;
4251541Srgrimes		prof->pr_ticks = ticks;
4261541Srgrimes		need_proftick(p);
4271541Srgrimes	}
4281541Srgrimes}
4291541Srgrimes
4301541Srgrimes/*
4311541Srgrimes * Much like before, but we can afford to take faults here.  If the
4321541Srgrimes * update fails, we simply turn off profiling.
4331541Srgrimes */
43413017Sbdevoid
4351541Srgrimesaddupc_task(p, pc, ticks)
4361541Srgrimes	register struct proc *p;
4371541Srgrimes	register u_long pc;
4381541Srgrimes	u_int ticks;
4391541Srgrimes{
4401541Srgrimes	register struct uprof *prof;
4411541Srgrimes	register caddr_t addr;
4421541Srgrimes	register u_int i;
4431541Srgrimes	u_short v;
4441541Srgrimes
4451541Srgrimes	/* Testing P_PROFIL may be unnecessary, but is certainly safe. */
4461541Srgrimes	if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
4471541Srgrimes		return;
4481541Srgrimes
4491541Srgrimes	prof = &p->p_stats->p_prof;
4501541Srgrimes	if (pc < prof->pr_off ||
4511541Srgrimes	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
4521541Srgrimes		return;
4531541Srgrimes
4541541Srgrimes	addr = prof->pr_base + i;
4551541Srgrimes	if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) {
4561541Srgrimes		v += ticks;
4571541Srgrimes		if (copyout((caddr_t)&v, addr, sizeof(v)) == 0)
4581541Srgrimes			return;
4591541Srgrimes	}
4601541Srgrimes	stopprofclock(p);
4611541Srgrimes}
462