xref: /csrg-svn/sys/kern/subr_prof.c (revision 64529)
154790Storek /*-
263176Sbostic  * Copyright (c) 1982, 1986, 1993
363176Sbostic  *	The Regents of the University of California.  All rights reserved.
423380Smckusick  *
541966Smckusick  * %sccs.include.redist.c%
641966Smckusick  *
7*64529Sbostic  *	@(#)subr_prof.c	8.2 (Berkeley) 09/21/93
823380Smckusick  */
97332Ssam 
1054790Storek #include <sys/param.h>
1154790Storek #include <sys/systm.h>
1254790Storek #include <sys/kernel.h>
1354790Storek #include <sys/proc.h>
1454790Storek #include <sys/user.h>
1554790Storek #include <machine/cpu.h>
1654790Storek 
177332Ssam #ifdef GPROF
1854790Storek #include <sys/malloc.h>
1954790Storek #include <sys/gmon.h>
207332Ssam 
217332Ssam /*
227332Ssam  * Froms is actually a bunch of unsigned shorts indexing tos
237332Ssam  */
2454790Storek struct gmonparam _gmonparam = { GMON_PROF_OFF };
2554790Storek 
2654790Storek extern char etext[];
277332Ssam 
287332Ssam kmstartup()
297332Ssam {
3054790Storek 	char *cp;
3154790Storek 	struct gmonparam *p = &_gmonparam;
3210292Smckusick 	/*
3329946Skarels 	 * Round lowpc and highpc to multiples of the density we're using
3429946Skarels 	 * so the rest of the scaling (here and in gprof) stays in ints.
3510292Smckusick 	 */
3654790Storek 	p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
3754790Storek 	p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
3854790Storek 	p->textsize = p->highpc - p->lowpc;
3954790Storek 	printf("Profiling kernel, textsize=%d [%x..%x]\n",
4054790Storek 	       p->textsize, p->lowpc, p->highpc);
4159355Smckusick 	p->kcountsize = p->textsize / HISTFRACTION;
4259555Smckusick 	p->hashfraction = HASHFRACTION;
4359355Smckusick 	p->fromssize = p->textsize / HASHFRACTION;
4454790Storek 	p->tolimit = p->textsize * ARCDENSITY / 100;
4554790Storek 	if (p->tolimit < MINARCS)
4654790Storek 		p->tolimit = MINARCS;
4754790Storek 	else if (p->tolimit > MAXARCS)
4854790Storek 		p->tolimit = MAXARCS;
4959355Smckusick 	p->tossize = p->tolimit * sizeof(struct tostruct);
5059355Smckusick 	cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize,
5159355Smckusick 	    M_GPROF, M_NOWAIT);
5254790Storek 	if (cp == 0) {
5354790Storek 		printf("No memory for profiling.\n");
547332Ssam 		return;
557332Ssam 	}
5659355Smckusick 	bzero(cp, p->kcountsize + p->tossize + p->fromssize);
5754790Storek 	p->tos = (struct tostruct *)cp;
5859355Smckusick 	cp += p->tossize;
5959355Smckusick 	p->kcount = (u_short *)cp;
6059355Smckusick 	cp += p->kcountsize;
6154790Storek 	p->froms = (u_short *)cp;
627332Ssam }
637332Ssam 
647332Ssam /*
6559355Smckusick  * Return kernel profiling information.
6659355Smckusick  */
6759355Smckusick sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen, p)
6859355Smckusick 	int *name;
6959355Smckusick 	u_int namelen;
7059355Smckusick 	void *oldp;
7159355Smckusick 	size_t *oldlenp;
7259355Smckusick 	void *newp;
7359355Smckusick 	size_t newlen;
7459355Smckusick {
7559355Smckusick 	struct gmonparam *gp = &_gmonparam;
7659870Smckusick 	int error;
7759355Smckusick 
7859355Smckusick 	/* all sysctl names at this level are terminal */
7959355Smckusick 	if (namelen != 1)
8059355Smckusick 		return (ENOTDIR);		/* overloaded */
8159355Smckusick 
8259355Smckusick 	switch (name[0]) {
8359355Smckusick 	case GPROF_STATE:
8459870Smckusick 		error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
8559870Smckusick 		if (error)
8659870Smckusick 			return (error);
8759870Smckusick 		if (gp->state == GMON_PROF_OFF)
8859870Smckusick 			stopprofclock(&proc0);
8959870Smckusick 		else
9059870Smckusick 			startprofclock(&proc0);
9159870Smckusick 		return (0);
9259355Smckusick 	case GPROF_COUNT:
9359355Smckusick 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
9459355Smckusick 		    gp->kcount, gp->kcountsize));
9559355Smckusick 	case GPROF_FROMS:
9659355Smckusick 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
9759355Smckusick 		    gp->froms, gp->fromssize));
9859355Smckusick 	case GPROF_TOS:
9959355Smckusick 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
10059355Smckusick 		    gp->tos, gp->tossize));
10159555Smckusick 	case GPROF_GMONPARAM:
10259555Smckusick 		return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
10359355Smckusick 	default:
10459355Smckusick 		return (EOPNOTSUPP);
10559355Smckusick 	}
10659355Smckusick 	/* NOTREACHED */
10759355Smckusick }
10859355Smckusick #endif /* GPROF */
10959355Smckusick 
11059355Smckusick /*
11154790Storek  * Profiling system call.
11254790Storek  *
11354790Storek  * The scale factor is a fixed point number with 16 bits of fraction, so that
11454790Storek  * 1.0 is represented as 0x10000.  A scale factor of 0 turns off profiling.
1157332Ssam  */
11654929Storek struct profil_args {
117*64529Sbostic 	caddr_t	samples;
118*64529Sbostic 	u_int	size;
11954929Storek 	u_int	offset;
12054929Storek 	u_int	scale;
12154929Storek };
12254790Storek /* ARGSUSED */
12354790Storek profil(p, uap, retval)
12454790Storek 	struct proc *p;
12554929Storek 	register struct profil_args *uap;
12654790Storek 	int *retval;
1277332Ssam {
12854790Storek 	register struct uprof *upp;
12954790Storek 	int s;
1307332Ssam 
13154790Storek 	if (uap->scale > (1 << 16))
13254790Storek 		return (EINVAL);
13354790Storek 	if (uap->scale == 0) {
13454790Storek 		stopprofclock(p);
13554790Storek 		return (0);
1367332Ssam 	}
13754790Storek 	upp = &p->p_stats->p_prof;
138*64529Sbostic 
139*64529Sbostic 	/* Block profile interrupts while changing state. */
140*64529Sbostic 	s = splstatclock();
14154790Storek 	upp->pr_off = uap->offset;
14254790Storek 	upp->pr_scale = uap->scale;
143*64529Sbostic 	upp->pr_base = uap->samples;
144*64529Sbostic 	upp->pr_size = uap->size;
14554790Storek 	startprofclock(p);
14654790Storek 	splx(s);
147*64529Sbostic 
14854790Storek 	return (0);
14954790Storek }
15054790Storek 
15154790Storek /*
15254790Storek  * Scale is a fixed-point number with the binary point 16 bits
15354790Storek  * into the value, and is <= 1.0.  pc is at most 32 bits, so the
15454790Storek  * intermediate result is at most 48 bits.
15554790Storek  */
15654790Storek #define	PC_TO_INDEX(pc, prof) \
15754790Storek 	((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
15854790Storek 	    (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
15954790Storek 
16054790Storek /*
16154790Storek  * Collect user-level profiling statistics; called on a profiling tick,
16254790Storek  * when a process is running in user-mode.  This routine may be called
16354790Storek  * from an interrupt context.  We try to update the user profiling buffers
16454790Storek  * cheaply with fuswintr() and suswintr().  If that fails, we revert to
16554790Storek  * an AST that will vector us to trap() with a context in which copyin
16654790Storek  * and copyout will work.  Trap will then call addupc_task().
16754790Storek  *
16854790Storek  * Note that we may (rarely) not get around to the AST soon enough, and
16954790Storek  * lose profile ticks when the next tick overwrites this one, but in this
17054790Storek  * case the system is overloaded and the profile is probably already
17154790Storek  * inaccurate.
17254790Storek  */
17354790Storek void
17454790Storek addupc_intr(p, pc, ticks)
17554790Storek 	register struct proc *p;
17654790Storek 	register u_long pc;
17754790Storek 	u_int ticks;
17854790Storek {
17954790Storek 	register struct uprof *prof;
18054790Storek 	register caddr_t addr;
18154790Storek 	register u_int i;
18254790Storek 	register int v;
18354790Storek 
18454790Storek 	if (ticks == 0)
18554790Storek 		return;
18654790Storek 	prof = &p->p_stats->p_prof;
18754790Storek 	if (pc < prof->pr_off ||
18854790Storek 	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
18954790Storek 		return;			/* out of range; ignore */
19054790Storek 
19154790Storek 	addr = prof->pr_base + i;
19254790Storek 	if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) {
19354790Storek 		prof->pr_addr = pc;
19454790Storek 		prof->pr_ticks = ticks;
19554790Storek 		need_proftick(p);
19610292Smckusick 	}
19754790Storek }
19810292Smckusick 
19954790Storek /*
20054790Storek  * Much like before, but we can afford to take faults here.  If the
20154790Storek  * update fails, we simply turn off profiling.
20254790Storek  */
20354790Storek void
20454790Storek addupc_task(p, pc, ticks)
20554790Storek 	register struct proc *p;
20654790Storek 	register u_long pc;
20754790Storek 	u_int ticks;
20854790Storek {
20954790Storek 	register struct uprof *prof;
21054790Storek 	register caddr_t addr;
21154790Storek 	register u_int i;
21254790Storek 	u_short v;
21354790Storek 
21454790Storek 	/* testing SPROFIL may be unnecessary, but is certainly safe */
21554790Storek 	if ((p->p_flag & SPROFIL) == 0 || ticks == 0)
21654790Storek 		return;
21754790Storek 
21854790Storek 	prof = &p->p_stats->p_prof;
21954790Storek 	if (pc < prof->pr_off ||
22054790Storek 	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
22154790Storek 		return;
22254790Storek 
22354790Storek 	addr = prof->pr_base + i;
22454790Storek 	if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) {
22554790Storek 		v += ticks;
22654790Storek 		if (copyout((caddr_t)&v, addr, sizeof(v)) == 0)
22754790Storek 			return;
2287332Ssam 	}
22954790Storek 	stopprofclock(p);
2307332Ssam }
231