xref: /csrg-svn/sys/kern/subr_prof.c (revision 68313)
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*68313Scgd  *	@(#)subr_prof.c	8.4 (Berkeley) 02/14/95
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>
15*68313Scgd 
16*68313Scgd #include <sys/mount.h>
17*68313Scgd #include <sys/syscallargs.h>
18*68313Scgd 
1954790Storek #include <machine/cpu.h>
2054790Storek 
217332Ssam #ifdef GPROF
2254790Storek #include <sys/malloc.h>
2354790Storek #include <sys/gmon.h>
247332Ssam 
257332Ssam /*
267332Ssam  * Froms is actually a bunch of unsigned shorts indexing tos
277332Ssam  */
2854790Storek struct gmonparam _gmonparam = { GMON_PROF_OFF };
2954790Storek 
3054790Storek extern char etext[];
317332Ssam 
32*68313Scgd void
kmstartup()337332Ssam kmstartup()
347332Ssam {
3554790Storek 	char *cp;
3654790Storek 	struct gmonparam *p = &_gmonparam;
3710292Smckusick 	/*
3829946Skarels 	 * Round lowpc and highpc to multiples of the density we're using
3929946Skarels 	 * so the rest of the scaling (here and in gprof) stays in ints.
4010292Smckusick 	 */
4154790Storek 	p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
4254790Storek 	p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
4354790Storek 	p->textsize = p->highpc - p->lowpc;
4454790Storek 	printf("Profiling kernel, textsize=%d [%x..%x]\n",
4554790Storek 	       p->textsize, p->lowpc, p->highpc);
4659355Smckusick 	p->kcountsize = p->textsize / HISTFRACTION;
4759555Smckusick 	p->hashfraction = HASHFRACTION;
4859355Smckusick 	p->fromssize = p->textsize / HASHFRACTION;
4954790Storek 	p->tolimit = p->textsize * ARCDENSITY / 100;
5054790Storek 	if (p->tolimit < MINARCS)
5154790Storek 		p->tolimit = MINARCS;
5254790Storek 	else if (p->tolimit > MAXARCS)
5354790Storek 		p->tolimit = MAXARCS;
5459355Smckusick 	p->tossize = p->tolimit * sizeof(struct tostruct);
5559355Smckusick 	cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize,
5659355Smckusick 	    M_GPROF, M_NOWAIT);
5754790Storek 	if (cp == 0) {
5854790Storek 		printf("No memory for profiling.\n");
597332Ssam 		return;
607332Ssam 	}
6159355Smckusick 	bzero(cp, p->kcountsize + p->tossize + p->fromssize);
6254790Storek 	p->tos = (struct tostruct *)cp;
6359355Smckusick 	cp += p->tossize;
6459355Smckusick 	p->kcount = (u_short *)cp;
6559355Smckusick 	cp += p->kcountsize;
6654790Storek 	p->froms = (u_short *)cp;
677332Ssam }
687332Ssam 
697332Ssam /*
7059355Smckusick  * Return kernel profiling information.
7159355Smckusick  */
72*68313Scgd int
sysctl_doprof(name,namelen,oldp,oldlenp,newp,newlen,p)7359355Smckusick sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen, p)
7459355Smckusick 	int *name;
7559355Smckusick 	u_int namelen;
7659355Smckusick 	void *oldp;
7759355Smckusick 	size_t *oldlenp;
7859355Smckusick 	void *newp;
7959355Smckusick 	size_t newlen;
8059355Smckusick {
8159355Smckusick 	struct gmonparam *gp = &_gmonparam;
8259870Smckusick 	int error;
8359355Smckusick 
8459355Smckusick 	/* all sysctl names at this level are terminal */
8559355Smckusick 	if (namelen != 1)
8659355Smckusick 		return (ENOTDIR);		/* overloaded */
8759355Smckusick 
8859355Smckusick 	switch (name[0]) {
8959355Smckusick 	case GPROF_STATE:
9059870Smckusick 		error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
9159870Smckusick 		if (error)
9259870Smckusick 			return (error);
9359870Smckusick 		if (gp->state == GMON_PROF_OFF)
9459870Smckusick 			stopprofclock(&proc0);
9559870Smckusick 		else
9659870Smckusick 			startprofclock(&proc0);
9759870Smckusick 		return (0);
9859355Smckusick 	case GPROF_COUNT:
9959355Smckusick 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
10059355Smckusick 		    gp->kcount, gp->kcountsize));
10159355Smckusick 	case GPROF_FROMS:
10259355Smckusick 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
10359355Smckusick 		    gp->froms, gp->fromssize));
10459355Smckusick 	case GPROF_TOS:
10559355Smckusick 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
10659355Smckusick 		    gp->tos, gp->tossize));
10759555Smckusick 	case GPROF_GMONPARAM:
10859555Smckusick 		return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
10959355Smckusick 	default:
11059355Smckusick 		return (EOPNOTSUPP);
11159355Smckusick 	}
11259355Smckusick 	/* NOTREACHED */
11359355Smckusick }
11459355Smckusick #endif /* GPROF */
11559355Smckusick 
11659355Smckusick /*
11754790Storek  * Profiling system call.
11854790Storek  *
11954790Storek  * The scale factor is a fixed point number with 16 bits of fraction, so that
12054790Storek  * 1.0 is represented as 0x10000.  A scale factor of 0 turns off profiling.
1217332Ssam  */
12254790Storek /* ARGSUSED */
123*68313Scgd int
profil(p,uap,retval)12454790Storek profil(p, uap, retval)
12554790Storek 	struct proc *p;
126*68313Scgd 	register struct profil_args /* {
127*68313Scgd 		syscallarg(caddr_t) samples;
128*68313Scgd 		syscallarg(u_int) size;
129*68313Scgd 		syscallarg(u_int) offset;
130*68313Scgd 		syscallarg(u_int) scale;
131*68313Scgd 	} */ *uap;
132*68313Scgd 	register_t *retval;
1337332Ssam {
13454790Storek 	register struct uprof *upp;
13554790Storek 	int s;
1367332Ssam 
137*68313Scgd 	if (SCARG(uap, scale) > (1 << 16))
13854790Storek 		return (EINVAL);
139*68313Scgd 	if (SCARG(uap, scale) == 0) {
14054790Storek 		stopprofclock(p);
14154790Storek 		return (0);
1427332Ssam 	}
14354790Storek 	upp = &p->p_stats->p_prof;
14464529Sbostic 
14564529Sbostic 	/* Block profile interrupts while changing state. */
14664529Sbostic 	s = splstatclock();
147*68313Scgd 	upp->pr_off = SCARG(uap, offset);
148*68313Scgd 	upp->pr_scale = SCARG(uap, scale);
149*68313Scgd 	upp->pr_base = SCARG(uap, samples);
150*68313Scgd 	upp->pr_size = SCARG(uap, size);
15154790Storek 	startprofclock(p);
15254790Storek 	splx(s);
15364529Sbostic 
15454790Storek 	return (0);
15554790Storek }
15654790Storek 
15754790Storek /*
15854790Storek  * Scale is a fixed-point number with the binary point 16 bits
15954790Storek  * into the value, and is <= 1.0.  pc is at most 32 bits, so the
16054790Storek  * intermediate result is at most 48 bits.
16154790Storek  */
16254790Storek #define	PC_TO_INDEX(pc, prof) \
16354790Storek 	((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
16454790Storek 	    (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
16554790Storek 
16654790Storek /*
16754790Storek  * Collect user-level profiling statistics; called on a profiling tick,
16854790Storek  * when a process is running in user-mode.  This routine may be called
16954790Storek  * from an interrupt context.  We try to update the user profiling buffers
17054790Storek  * cheaply with fuswintr() and suswintr().  If that fails, we revert to
17154790Storek  * an AST that will vector us to trap() with a context in which copyin
17254790Storek  * and copyout will work.  Trap will then call addupc_task().
17354790Storek  *
17454790Storek  * Note that we may (rarely) not get around to the AST soon enough, and
17554790Storek  * lose profile ticks when the next tick overwrites this one, but in this
17654790Storek  * case the system is overloaded and the profile is probably already
17754790Storek  * inaccurate.
17854790Storek  */
17954790Storek void
addupc_intr(p,pc,ticks)18054790Storek addupc_intr(p, pc, ticks)
18154790Storek 	register struct proc *p;
18254790Storek 	register u_long pc;
18354790Storek 	u_int ticks;
18454790Storek {
18554790Storek 	register struct uprof *prof;
18654790Storek 	register caddr_t addr;
18754790Storek 	register u_int i;
18854790Storek 	register int v;
18954790Storek 
19054790Storek 	if (ticks == 0)
19154790Storek 		return;
19254790Storek 	prof = &p->p_stats->p_prof;
19354790Storek 	if (pc < prof->pr_off ||
19454790Storek 	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
19554790Storek 		return;			/* out of range; ignore */
19654790Storek 
19754790Storek 	addr = prof->pr_base + i;
19854790Storek 	if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) {
19954790Storek 		prof->pr_addr = pc;
20054790Storek 		prof->pr_ticks = ticks;
20154790Storek 		need_proftick(p);
20210292Smckusick 	}
20354790Storek }
20410292Smckusick 
20554790Storek /*
20654790Storek  * Much like before, but we can afford to take faults here.  If the
20754790Storek  * update fails, we simply turn off profiling.
20854790Storek  */
20954790Storek void
addupc_task(p,pc,ticks)21054790Storek addupc_task(p, pc, ticks)
21154790Storek 	register struct proc *p;
21254790Storek 	register u_long pc;
21354790Storek 	u_int ticks;
21454790Storek {
21554790Storek 	register struct uprof *prof;
21654790Storek 	register caddr_t addr;
21754790Storek 	register u_int i;
21854790Storek 	u_short v;
21954790Storek 
22064575Sbostic 	/* Testing P_PROFIL may be unnecessary, but is certainly safe. */
22164575Sbostic 	if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
22254790Storek 		return;
22354790Storek 
22454790Storek 	prof = &p->p_stats->p_prof;
22554790Storek 	if (pc < prof->pr_off ||
22654790Storek 	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
22754790Storek 		return;
22854790Storek 
22954790Storek 	addr = prof->pr_base + i;
23054790Storek 	if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) {
23154790Storek 		v += ticks;
23254790Storek 		if (copyout((caddr_t)&v, addr, sizeof(v)) == 0)
23354790Storek 			return;
2347332Ssam 	}
23554790Storek 	stopprofclock(p);
2367332Ssam }
237