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