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