mcount.c revision 13107
11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1983, 1992, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * Redistribution and use in source and binary forms, with or without
61573Srgrimes * modification, are permitted provided that the following conditions
71573Srgrimes * are met:
81573Srgrimes * 1. Redistributions of source code must retain the above copyright
91573Srgrimes *    notice, this list of conditions and the following disclaimer.
101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111573Srgrimes *    notice, this list of conditions and the following disclaimer in the
121573Srgrimes *    documentation and/or other materials provided with the distribution.
131573Srgrimes * 3. All advertising materials mentioning features or use of this software
141573Srgrimes *    must display the following acknowledgement:
151573Srgrimes *	This product includes software developed by the University of
161573Srgrimes *	California, Berkeley and its contributors.
171573Srgrimes * 4. Neither the name of the University nor the names of its contributors
181573Srgrimes *    may be used to endorse or promote products derived from this software
191573Srgrimes *    without specific prior written permission.
201573Srgrimes *
211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311573Srgrimes * SUCH DAMAGE.
321573Srgrimes */
331573Srgrimes
341573Srgrimes#if !defined(lint) && !defined(KERNEL) && defined(LIBC_SCCS)
351573Srgrimesstatic char sccsid[] = "@(#)mcount.c	8.1 (Berkeley) 6/4/93";
361573Srgrimes#endif
371573Srgrimes
381573Srgrimes#include <sys/param.h>
391573Srgrimes#include <sys/gmon.h>
402800Spaul#ifdef KERNEL
4113107Sbde#include <sys/systm.h>
4213107Sbde#include <vm/vm.h>
4313107Sbde#include <vm/vm_param.h>
4413107Sbde#include <vm/pmap.h>
4513107Sbdevoid	bintr __P((void));
4613107Sbdevoid	btrap __P((void));
4713107Sbdevoid	eintr __P((void));
4813107Sbdevoid	user __P((void));
492800Spaul#endif
501573Srgrimes
511573Srgrimes/*
521573Srgrimes * mcount is called on entry to each function compiled with the profiling
531573Srgrimes * switch set.  _mcount(), which is declared in a machine-dependent way
541573Srgrimes * with _MCOUNT_DECL, does the actual work and is either inlined into a
551573Srgrimes * C routine or called by an assembly stub.  In any case, this magic is
561573Srgrimes * taken care of by the MCOUNT definition in <machine/profile.h>.
571573Srgrimes *
581573Srgrimes * _mcount updates data structures that represent traversals of the
591573Srgrimes * program's call graph edges.  frompc and selfpc are the return
601573Srgrimes * address and function address that represents the given call graph edge.
618870Srgrimes *
621573Srgrimes * Note: the original BSD code used the same variable (frompcindex) for
631573Srgrimes * both frompcindex and frompc.  Any reasonable, modern compiler will
641573Srgrimes * perform this optimization.
651573Srgrimes */
661573Srgrimes_MCOUNT_DECL(frompc, selfpc)	/* _mcount; may be static, inline, etc */
6713107Sbde	register fptrint_t frompc, selfpc;
681573Srgrimes{
6913107Sbde#ifdef GUPROF
7013107Sbde	u_int delta;
7113107Sbde#endif
7213107Sbde	register fptrdiff_t frompci;
731573Srgrimes	register u_short *frompcindex;
741573Srgrimes	register struct tostruct *top, *prevtop;
751573Srgrimes	register struct gmonparam *p;
761573Srgrimes	register long toindex;
771573Srgrimes#ifdef KERNEL
7813107Sbde	register int s;		/* XXX */
7913107Sbde	u_long save_eflags;	/* XXX */
801573Srgrimes#endif
811573Srgrimes
821573Srgrimes	p = &_gmonparam;
8313107Sbde#ifndef GUPROF			/* XXX */
841573Srgrimes	/*
851573Srgrimes	 * check that we are profiling
861573Srgrimes	 * and that we aren't recursively invoked.
871573Srgrimes	 */
881573Srgrimes	if (p->state != GMON_PROF_ON)
891573Srgrimes		return;
9013107Sbde#endif
911573Srgrimes#ifdef KERNEL
921573Srgrimes	MCOUNT_ENTER;
931573Srgrimes#else
941573Srgrimes	p->state = GMON_PROF_BUSY;
951573Srgrimes#endif
9613107Sbde	frompci = frompc - p->lowpc;
9713107Sbde
9813107Sbde#ifdef KERNEL
991573Srgrimes	/*
10013107Sbde	 * When we are called from an exception handler, frompci may be
10113107Sbde	 * for a user address.  Convert such frompci's to the index of
10213107Sbde	 * user() to merge all user counts.
10313107Sbde	 */
10413107Sbde	if (frompci >= p->textsize) {
10513107Sbde		if (frompci + p->lowpc
10613107Sbde		    >= (fptrint_t)(VM_MAXUSER_ADDRESS + UPAGES * NBPG))
10713107Sbde			goto done;
10813107Sbde		frompci = (fptrint_t)user - p->lowpc;
10913107Sbde		if (frompci >= p->textsize)
11013107Sbde		    goto done;
11113107Sbde	}
11213107Sbde#endif /* KERNEL */
11313107Sbde
11413107Sbde#ifdef GUPROF
11513107Sbde	if (p->state != GMON_PROF_HIRES)
11613107Sbde		goto skip_guprof_stuff;
11713107Sbde	/*
11813107Sbde	 * Look at the clock and add the count of clock cycles since the
11913107Sbde	 * clock was last looked at to a counter for frompc.  This
12013107Sbde	 * solidifies the count for the function containing frompc and
12113107Sbde	 * effectively starts another clock for the current function.
12213107Sbde	 * The count for the new clock will be solidified when another
12313107Sbde	 * function call is made or the function returns.
12413107Sbde	 *
12513107Sbde	 * We use the usual sampling counters since they can be located
12613107Sbde	 * efficiently.  4-byte counters are usually necessary.
12713107Sbde	 *
12813107Sbde	 * There are many complications for subtracting the profiling
12913107Sbde	 * overheads from the counts for normal functions and adding
13013107Sbde	 * them to the counts for mcount(), mexitcount() and cputime().
13113107Sbde	 * We attempt to handle fractional cycles, but the overheads
13213107Sbde	 * are usually underestimated because they are calibrated for
13313107Sbde	 * a simpler than usual setup.
13413107Sbde	 */
13513107Sbde	delta = cputime() - p->mcount_overhead;
13613107Sbde	p->cputime_overhead_resid += p->cputime_overhead_frac;
13713107Sbde	p->mcount_overhead_resid += p->mcount_overhead_frac;
13813107Sbde	if ((int)delta < 0)
13913107Sbde		*p->mcount_count += delta + p->mcount_overhead
14013107Sbde				    - p->cputime_overhead;
14113107Sbde	else if (delta != 0) {
14213107Sbde		if (p->cputime_overhead_resid >= CALIB_SCALE) {
14313107Sbde			p->cputime_overhead_resid -= CALIB_SCALE;
14413107Sbde			++*p->cputime_count;
14513107Sbde			--delta;
14613107Sbde		}
14713107Sbde		if (delta != 0) {
14813107Sbde			if (p->mcount_overhead_resid >= CALIB_SCALE) {
14913107Sbde				p->mcount_overhead_resid -= CALIB_SCALE;
15013107Sbde				++*p->mcount_count;
15113107Sbde				--delta;
15213107Sbde			}
15313107Sbde			KCOUNT(p, frompci) += delta;
15413107Sbde		}
15513107Sbde		*p->mcount_count += p->mcount_overhead_sub;
15613107Sbde	}
15713107Sbde	*p->cputime_count += p->cputime_overhead;
15813107Sbdeskip_guprof_stuff:
15913107Sbde#endif /* GUPROF */
16013107Sbde
16113107Sbde#ifdef KERNEL
16213107Sbde	/*
16313107Sbde	 * When we are called from an exception handler, frompc is faked
16413107Sbde	 * to be for where the exception occurred.  We've just solidified
16513107Sbde	 * the count for there.  Now convert frompci to the index of btrap()
16613107Sbde	 * for trap handlers and bintr() for interrupt handlers to make
16713107Sbde	 * exceptions appear in the call graph as calls from btrap() and
16813107Sbde	 * bintr() instead of calls from all over.
16913107Sbde	 */
17013107Sbde	if ((fptrint_t)selfpc >= (fptrint_t)btrap
17113107Sbde	    && (fptrint_t)selfpc < (fptrint_t)eintr) {
17213107Sbde		if ((fptrint_t)selfpc >= (fptrint_t)bintr)
17313107Sbde			frompci = (fptrint_t)bintr - p->lowpc;
17413107Sbde		else
17513107Sbde			frompci = (fptrint_t)btrap - p->lowpc;
17613107Sbde	}
17713107Sbde#endif /* KERNEL */
17813107Sbde
17913107Sbde	/*
18013107Sbde	 * check that frompc is a reasonable pc value.
1811573Srgrimes	 * for example:	signal catchers get called from the stack,
1821573Srgrimes	 *		not from text space.  too bad.
1831573Srgrimes	 */
18413107Sbde	if (frompci >= p->textsize)
1851573Srgrimes		goto done;
1861573Srgrimes
18713107Sbde	frompcindex = &p->froms[frompci / (p->hashfraction * sizeof(*p->froms))];
1881573Srgrimes	toindex = *frompcindex;
1891573Srgrimes	if (toindex == 0) {
1901573Srgrimes		/*
1911573Srgrimes		 *	first time traversing this arc
1921573Srgrimes		 */
1931573Srgrimes		toindex = ++p->tos[0].link;
1941573Srgrimes		if (toindex >= p->tolimit)
1951573Srgrimes			/* halt further profiling */
1961573Srgrimes			goto overflow;
1971573Srgrimes
1981573Srgrimes		*frompcindex = toindex;
1991573Srgrimes		top = &p->tos[toindex];
2001573Srgrimes		top->selfpc = selfpc;
2011573Srgrimes		top->count = 1;
2021573Srgrimes		top->link = 0;
2031573Srgrimes		goto done;
2041573Srgrimes	}
2051573Srgrimes	top = &p->tos[toindex];
2061573Srgrimes	if (top->selfpc == selfpc) {
2071573Srgrimes		/*
2081573Srgrimes		 * arc at front of chain; usual case.
2091573Srgrimes		 */
2101573Srgrimes		top->count++;
2111573Srgrimes		goto done;
2121573Srgrimes	}
2131573Srgrimes	/*
2141573Srgrimes	 * have to go looking down chain for it.
2151573Srgrimes	 * top points to what we are looking at,
2161573Srgrimes	 * prevtop points to previous top.
2171573Srgrimes	 * we know it is not at the head of the chain.
2181573Srgrimes	 */
2191573Srgrimes	for (; /* goto done */; ) {
2201573Srgrimes		if (top->link == 0) {
2211573Srgrimes			/*
2221573Srgrimes			 * top is end of the chain and none of the chain
2231573Srgrimes			 * had top->selfpc == selfpc.
2241573Srgrimes			 * so we allocate a new tostruct
2251573Srgrimes			 * and link it to the head of the chain.
2261573Srgrimes			 */
2271573Srgrimes			toindex = ++p->tos[0].link;
2281573Srgrimes			if (toindex >= p->tolimit)
2291573Srgrimes				goto overflow;
2301573Srgrimes
2311573Srgrimes			top = &p->tos[toindex];
2321573Srgrimes			top->selfpc = selfpc;
2331573Srgrimes			top->count = 1;
2341573Srgrimes			top->link = *frompcindex;
2351573Srgrimes			*frompcindex = toindex;
2361573Srgrimes			goto done;
2371573Srgrimes		}
2381573Srgrimes		/*
2391573Srgrimes		 * otherwise, check the next arc on the chain.
2401573Srgrimes		 */
2411573Srgrimes		prevtop = top;
2421573Srgrimes		top = &p->tos[top->link];
2431573Srgrimes		if (top->selfpc == selfpc) {
2441573Srgrimes			/*
2451573Srgrimes			 * there it is.
2461573Srgrimes			 * increment its count
2471573Srgrimes			 * move it to the head of the chain.
2481573Srgrimes			 */
2491573Srgrimes			top->count++;
2501573Srgrimes			toindex = prevtop->link;
2511573Srgrimes			prevtop->link = top->link;
2521573Srgrimes			top->link = *frompcindex;
2531573Srgrimes			*frompcindex = toindex;
2541573Srgrimes			goto done;
2551573Srgrimes		}
2568870Srgrimes
2571573Srgrimes	}
2581573Srgrimesdone:
2591573Srgrimes#ifdef KERNEL
2601573Srgrimes	MCOUNT_EXIT;
2611573Srgrimes#else
2621573Srgrimes	p->state = GMON_PROF_ON;
2631573Srgrimes#endif
2641573Srgrimes	return;
2651573Srgrimesoverflow:
2661573Srgrimes	p->state = GMON_PROF_ERROR;
2671573Srgrimes#ifdef KERNEL
2681573Srgrimes	MCOUNT_EXIT;
2691573Srgrimes#endif
2701573Srgrimes	return;
2711573Srgrimes}
2721573Srgrimes
2731573Srgrimes/*
2741573Srgrimes * Actual definition of mcount function.  Defined in <machine/profile.h>,
2751573Srgrimes * which is included by <sys/gmon.h>.
2761573Srgrimes */
2771573SrgrimesMCOUNT
27813107Sbde
27913107Sbde#ifdef GUPROF
28013107Sbdevoid
28113107Sbdemexitcount(selfpc)
28213107Sbde	fptrint_t selfpc;
28313107Sbde{
28413107Sbde	struct gmonparam *p;
28513107Sbde	fptrint_t selfpcdiff;
28613107Sbde
28713107Sbde	p = &_gmonparam;
28813107Sbde	selfpcdiff = selfpc - (fptrint_t)p->lowpc;
28913107Sbde	if (selfpcdiff < p->textsize) {
29013107Sbde		u_int delta;
29113107Sbde
29213107Sbde		/*
29313107Sbde		 * Solidify the count for the current function.
29413107Sbde		 */
29513107Sbde		delta = cputime() - p->mexitcount_overhead;
29613107Sbde		p->cputime_overhead_resid += p->cputime_overhead_frac;
29713107Sbde		p->mexitcount_overhead_resid += p->mexitcount_overhead_frac;
29813107Sbde		if ((int)delta < 0)
29913107Sbde			*p->mexitcount_count += delta + p->mexitcount_overhead
30013107Sbde						- p->cputime_overhead;
30113107Sbde		else if (delta != 0) {
30213107Sbde			if (p->cputime_overhead_resid >= CALIB_SCALE) {
30313107Sbde				p->cputime_overhead_resid -= CALIB_SCALE;
30413107Sbde				++*p->cputime_count;
30513107Sbde				--delta;
30613107Sbde			}
30713107Sbde			if (delta != 0) {
30813107Sbde				if (p->mexitcount_overhead_resid
30913107Sbde				    >= CALIB_SCALE) {
31013107Sbde					p->mexitcount_overhead_resid
31113107Sbde					    -= CALIB_SCALE;
31213107Sbde					++*p->mexitcount_count;
31313107Sbde					--delta;
31413107Sbde				}
31513107Sbde				KCOUNT(p, selfpcdiff) += delta;
31613107Sbde			}
31713107Sbde			*p->mexitcount_count += p->mexitcount_overhead_sub;
31813107Sbde		}
31913107Sbde		*p->cputime_count += p->cputime_overhead;
32013107Sbde	}
32113107Sbde}
32213107Sbde#endif /* GUPROF */
323