mcount.c revision 13116
113116Sbde/*-
213116Sbde * Copyright (c) 1983, 1992, 1993
313116Sbde *	The Regents of the University of California.  All rights reserved.
413116Sbde *
513116Sbde * Redistribution and use in source and binary forms, with or without
613116Sbde * modification, are permitted provided that the following conditions
713116Sbde * are met:
813116Sbde * 1. Redistributions of source code must retain the above copyright
913116Sbde *    notice, this list of conditions and the following disclaimer.
1013116Sbde * 2. Redistributions in binary form must reproduce the above copyright
1113116Sbde *    notice, this list of conditions and the following disclaimer in the
1213116Sbde *    documentation and/or other materials provided with the distribution.
1313116Sbde * 3. All advertising materials mentioning features or use of this software
1413116Sbde *    must display the following acknowledgement:
1513116Sbde *	This product includes software developed by the University of
1613116Sbde *	California, Berkeley and its contributors.
1713116Sbde * 4. Neither the name of the University nor the names of its contributors
1813116Sbde *    may be used to endorse or promote products derived from this software
1913116Sbde *    without specific prior written permission.
2013116Sbde *
2113116Sbde * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2213116Sbde * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2313116Sbde * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2413116Sbde * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2513116Sbde * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2613116Sbde * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2713116Sbde * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2813116Sbde * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2913116Sbde * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3013116Sbde * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3113116Sbde * SUCH DAMAGE.
3213116Sbde */
331541Srgrimes
3413116Sbde#if !defined(lint) && !defined(KERNEL) && defined(LIBC_SCCS)
3513116Sbde#if 0
3613116Sbdestatic char sccsid[] = "@(#)mcount.c	8.1 (Berkeley) 6/4/93";
3713116Sbde#endif
3813116Sbdestatic const char rcsid[] =
3913116Sbde	"$Id$";
4013116Sbde#endif
4113116Sbde
4213116Sbde#include <sys/param.h>
4313116Sbde#include <sys/gmon.h>
4413116Sbde#ifdef KERNEL
4513116Sbde#include <sys/systm.h>
4613116Sbde#include <vm/vm.h>
4713116Sbde#include <vm/vm_param.h>
4813116Sbde#include <vm/pmap.h>
4913116Sbdevoid	bintr __P((void));
5013116Sbdevoid	btrap __P((void));
5113116Sbdevoid	eintr __P((void));
5213116Sbdevoid	user __P((void));
5313116Sbde#endif
5413116Sbde
5513116Sbde/*
5613116Sbde * mcount is called on entry to each function compiled with the profiling
5713116Sbde * switch set.  _mcount(), which is declared in a machine-dependent way
5813116Sbde * with _MCOUNT_DECL, does the actual work and is either inlined into a
5913116Sbde * C routine or called by an assembly stub.  In any case, this magic is
6013116Sbde * taken care of by the MCOUNT definition in <machine/profile.h>.
6113116Sbde *
6213116Sbde * _mcount updates data structures that represent traversals of the
6313116Sbde * program's call graph edges.  frompc and selfpc are the return
6413116Sbde * address and function address that represents the given call graph edge.
6513116Sbde *
6613116Sbde * Note: the original BSD code used the same variable (frompcindex) for
6713116Sbde * both frompcindex and frompc.  Any reasonable, modern compiler will
6813116Sbde * perform this optimization.
6913116Sbde */
7013116Sbde_MCOUNT_DECL(frompc, selfpc)	/* _mcount; may be static, inline, etc */
7113116Sbde	register fptrint_t frompc, selfpc;
7213116Sbde{
7313116Sbde#ifdef GUPROF
7413116Sbde	u_int delta;
7513116Sbde#endif
7613116Sbde	register fptrdiff_t frompci;
7713116Sbde	register u_short *frompcindex;
7813116Sbde	register struct tostruct *top, *prevtop;
7913116Sbde	register struct gmonparam *p;
8013116Sbde	register long toindex;
8113116Sbde#ifdef KERNEL
8213116Sbde	register int s;		/* XXX */
8313116Sbde	u_long save_eflags;	/* XXX */
8413116Sbde#endif
8513116Sbde
8613116Sbde	p = &_gmonparam;
8713116Sbde#ifndef GUPROF			/* XXX */
8813116Sbde	/*
8913116Sbde	 * check that we are profiling
9013116Sbde	 * and that we aren't recursively invoked.
9113116Sbde	 */
9213116Sbde	if (p->state != GMON_PROF_ON)
9313116Sbde		return;
9413116Sbde#endif
9513116Sbde#ifdef KERNEL
9613116Sbde	MCOUNT_ENTER;
9713116Sbde#else
9813116Sbde	p->state = GMON_PROF_BUSY;
9913116Sbde#endif
10013116Sbde	frompci = frompc - p->lowpc;
10113116Sbde
10213116Sbde#ifdef KERNEL
10313116Sbde	/*
10413116Sbde	 * When we are called from an exception handler, frompci may be
10513116Sbde	 * for a user address.  Convert such frompci's to the index of
10613116Sbde	 * user() to merge all user counts.
10713116Sbde	 */
10813116Sbde	if (frompci >= p->textsize) {
10913116Sbde		if (frompci + p->lowpc
11013116Sbde		    >= (fptrint_t)(VM_MAXUSER_ADDRESS + UPAGES * NBPG))
11113116Sbde			goto done;
11213116Sbde		frompci = (fptrint_t)user - p->lowpc;
11313116Sbde		if (frompci >= p->textsize)
11413116Sbde		    goto done;
11513116Sbde	}
11613116Sbde#endif /* KERNEL */
11713116Sbde
11813116Sbde#ifdef GUPROF
11913116Sbde	if (p->state != GMON_PROF_HIRES)
12013116Sbde		goto skip_guprof_stuff;
12113116Sbde	/*
12213116Sbde	 * Look at the clock and add the count of clock cycles since the
12313116Sbde	 * clock was last looked at to a counter for frompc.  This
12413116Sbde	 * solidifies the count for the function containing frompc and
12513116Sbde	 * effectively starts another clock for the current function.
12613116Sbde	 * The count for the new clock will be solidified when another
12713116Sbde	 * function call is made or the function returns.
12813116Sbde	 *
12913116Sbde	 * We use the usual sampling counters since they can be located
13013116Sbde	 * efficiently.  4-byte counters are usually necessary.
13113116Sbde	 *
13213116Sbde	 * There are many complications for subtracting the profiling
13313116Sbde	 * overheads from the counts for normal functions and adding
13413116Sbde	 * them to the counts for mcount(), mexitcount() and cputime().
13513116Sbde	 * We attempt to handle fractional cycles, but the overheads
13613116Sbde	 * are usually underestimated because they are calibrated for
13713116Sbde	 * a simpler than usual setup.
13813116Sbde	 */
13913116Sbde	delta = cputime() - p->mcount_overhead;
14013116Sbde	p->cputime_overhead_resid += p->cputime_overhead_frac;
14113116Sbde	p->mcount_overhead_resid += p->mcount_overhead_frac;
14213116Sbde	if ((int)delta < 0)
14313116Sbde		*p->mcount_count += delta + p->mcount_overhead
14413116Sbde				    - p->cputime_overhead;
14513116Sbde	else if (delta != 0) {
14613116Sbde		if (p->cputime_overhead_resid >= CALIB_SCALE) {
14713116Sbde			p->cputime_overhead_resid -= CALIB_SCALE;
14813116Sbde			++*p->cputime_count;
14913116Sbde			--delta;
15013116Sbde		}
15113116Sbde		if (delta != 0) {
15213116Sbde			if (p->mcount_overhead_resid >= CALIB_SCALE) {
15313116Sbde				p->mcount_overhead_resid -= CALIB_SCALE;
15413116Sbde				++*p->mcount_count;
15513116Sbde				--delta;
15613116Sbde			}
15713116Sbde			KCOUNT(p, frompci) += delta;
15813116Sbde		}
15913116Sbde		*p->mcount_count += p->mcount_overhead_sub;
16013116Sbde	}
16113116Sbde	*p->cputime_count += p->cputime_overhead;
16213116Sbdeskip_guprof_stuff:
16313116Sbde#endif /* GUPROF */
16413116Sbde
16513116Sbde#ifdef KERNEL
16613116Sbde	/*
16713116Sbde	 * When we are called from an exception handler, frompc is faked
16813116Sbde	 * to be for where the exception occurred.  We've just solidified
16913116Sbde	 * the count for there.  Now convert frompci to the index of btrap()
17013116Sbde	 * for trap handlers and bintr() for interrupt handlers to make
17113116Sbde	 * exceptions appear in the call graph as calls from btrap() and
17213116Sbde	 * bintr() instead of calls from all over.
17313116Sbde	 */
17413116Sbde	if ((fptrint_t)selfpc >= (fptrint_t)btrap
17513116Sbde	    && (fptrint_t)selfpc < (fptrint_t)eintr) {
17613116Sbde		if ((fptrint_t)selfpc >= (fptrint_t)bintr)
17713116Sbde			frompci = (fptrint_t)bintr - p->lowpc;
17813116Sbde		else
17913116Sbde			frompci = (fptrint_t)btrap - p->lowpc;
18013116Sbde	}
18113116Sbde#endif /* KERNEL */
18213116Sbde
18313116Sbde	/*
18413116Sbde	 * check that frompc is a reasonable pc value.
18513116Sbde	 * for example:	signal catchers get called from the stack,
18613116Sbde	 *		not from text space.  too bad.
18713116Sbde	 */
18813116Sbde	if (frompci >= p->textsize)
18913116Sbde		goto done;
19013116Sbde
19113116Sbde	frompcindex = &p->froms[frompci / (p->hashfraction * sizeof(*p->froms))];
19213116Sbde	toindex = *frompcindex;
19313116Sbde	if (toindex == 0) {
19413116Sbde		/*
19513116Sbde		 *	first time traversing this arc
19613116Sbde		 */
19713116Sbde		toindex = ++p->tos[0].link;
19813116Sbde		if (toindex >= p->tolimit)
19913116Sbde			/* halt further profiling */
20013116Sbde			goto overflow;
20113116Sbde
20213116Sbde		*frompcindex = toindex;
20313116Sbde		top = &p->tos[toindex];
20413116Sbde		top->selfpc = selfpc;
20513116Sbde		top->count = 1;
20613116Sbde		top->link = 0;
20713116Sbde		goto done;
20813116Sbde	}
20913116Sbde	top = &p->tos[toindex];
21013116Sbde	if (top->selfpc == selfpc) {
21113116Sbde		/*
21213116Sbde		 * arc at front of chain; usual case.
21313116Sbde		 */
21413116Sbde		top->count++;
21513116Sbde		goto done;
21613116Sbde	}
21713116Sbde	/*
21813116Sbde	 * have to go looking down chain for it.
21913116Sbde	 * top points to what we are looking at,
22013116Sbde	 * prevtop points to previous top.
22113116Sbde	 * we know it is not at the head of the chain.
22213116Sbde	 */
22313116Sbde	for (; /* goto done */; ) {
22413116Sbde		if (top->link == 0) {
22513116Sbde			/*
22613116Sbde			 * top is end of the chain and none of the chain
22713116Sbde			 * had top->selfpc == selfpc.
22813116Sbde			 * so we allocate a new tostruct
22913116Sbde			 * and link it to the head of the chain.
23013116Sbde			 */
23113116Sbde			toindex = ++p->tos[0].link;
23213116Sbde			if (toindex >= p->tolimit)
23313116Sbde				goto overflow;
23413116Sbde
23513116Sbde			top = &p->tos[toindex];
23613116Sbde			top->selfpc = selfpc;
23713116Sbde			top->count = 1;
23813116Sbde			top->link = *frompcindex;
23913116Sbde			*frompcindex = toindex;
24013116Sbde			goto done;
24113116Sbde		}
24213116Sbde		/*
24313116Sbde		 * otherwise, check the next arc on the chain.
24413116Sbde		 */
24513116Sbde		prevtop = top;
24613116Sbde		top = &p->tos[top->link];
24713116Sbde		if (top->selfpc == selfpc) {
24813116Sbde			/*
24913116Sbde			 * there it is.
25013116Sbde			 * increment its count
25113116Sbde			 * move it to the head of the chain.
25213116Sbde			 */
25313116Sbde			top->count++;
25413116Sbde			toindex = prevtop->link;
25513116Sbde			prevtop->link = top->link;
25613116Sbde			top->link = *frompcindex;
25713116Sbde			*frompcindex = toindex;
25813116Sbde			goto done;
25913116Sbde		}
26013116Sbde
26113116Sbde	}
26213116Sbdedone:
26313116Sbde#ifdef KERNEL
26413116Sbde	MCOUNT_EXIT;
26513116Sbde#else
26613116Sbde	p->state = GMON_PROF_ON;
26713116Sbde#endif
26813116Sbde	return;
26913116Sbdeoverflow:
27013116Sbde	p->state = GMON_PROF_ERROR;
27113116Sbde#ifdef KERNEL
27213116Sbde	MCOUNT_EXIT;
27313116Sbde#endif
27413116Sbde	return;
27513116Sbde}
27613116Sbde
27713116Sbde/*
27813116Sbde * Actual definition of mcount function.  Defined in <machine/profile.h>,
27913116Sbde * which is included by <sys/gmon.h>.
28013116Sbde */
28113116SbdeMCOUNT
28213116Sbde
28313116Sbde#ifdef GUPROF
28413116Sbdevoid
28513116Sbdemexitcount(selfpc)
28613116Sbde	fptrint_t selfpc;
28713116Sbde{
28813116Sbde	struct gmonparam *p;
28913116Sbde	fptrint_t selfpcdiff;
29013116Sbde
29113116Sbde	p = &_gmonparam;
29213116Sbde	selfpcdiff = selfpc - (fptrint_t)p->lowpc;
29313116Sbde	if (selfpcdiff < p->textsize) {
29413116Sbde		u_int delta;
29513116Sbde
29613116Sbde		/*
29713116Sbde		 * Solidify the count for the current function.
29813116Sbde		 */
29913116Sbde		delta = cputime() - p->mexitcount_overhead;
30013116Sbde		p->cputime_overhead_resid += p->cputime_overhead_frac;
30113116Sbde		p->mexitcount_overhead_resid += p->mexitcount_overhead_frac;
30213116Sbde		if ((int)delta < 0)
30313116Sbde			*p->mexitcount_count += delta + p->mexitcount_overhead
30413116Sbde						- p->cputime_overhead;
30513116Sbde		else if (delta != 0) {
30613116Sbde			if (p->cputime_overhead_resid >= CALIB_SCALE) {
30713116Sbde				p->cputime_overhead_resid -= CALIB_SCALE;
30813116Sbde				++*p->cputime_count;
30913116Sbde				--delta;
31013116Sbde			}
31113116Sbde			if (delta != 0) {
31213116Sbde				if (p->mexitcount_overhead_resid
31313116Sbde				    >= CALIB_SCALE) {
31413116Sbde					p->mexitcount_overhead_resid
31513116Sbde					    -= CALIB_SCALE;
31613116Sbde					++*p->mexitcount_count;
31713116Sbde					--delta;
31813116Sbde				}
31913116Sbde				KCOUNT(p, selfpcdiff) += delta;
32013116Sbde			}
32113116Sbde			*p->mexitcount_count += p->mexitcount_overhead_sub;
32213116Sbde		}
32313116Sbde		*p->cputime_count += p->cputime_overhead;
32413116Sbde	}
32513116Sbde}
32613116Sbde#endif /* GUPROF */
327