mcount.c revision 55206
1234353Sdim/*- 2226584Sdim * Copyright (c) 1983, 1992, 1993 3353358Sdim * The Regents of the University of California. All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms, with or without 6226584Sdim * modification, are permitted provided that the following conditions 7226584Sdim * are met: 8226584Sdim * 1. Redistributions of source code must retain the above copyright 9226584Sdim * notice, this list of conditions and the following disclaimer. 10226584Sdim * 2. Redistributions in binary form must reproduce the above copyright 11226584Sdim * notice, this list of conditions and the following disclaimer in the 12226584Sdim * documentation and/or other materials provided with the distribution. 13280031Sdim * 3. All advertising materials mentioning features or use of this software 14280031Sdim * must display the following acknowledgement: 15226584Sdim * This product includes software developed by the University of 16226584Sdim * California, Berkeley and its contributors. 17226584Sdim * 4. Neither the name of the University nor the names of its contributors 18344779Sdim * may be used to endorse or promote products derived from this software 19234353Sdim * without specific prior written permission. 20226584Sdim * 21226584Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22226584Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23226584Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24226584Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25226584Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26226584Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27226584Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28226584Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29226584Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30226584Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31226584Sdim * SUCH DAMAGE. 32226584Sdim */ 33353358Sdim 34353358Sdim#if !defined(lint) && !defined(_KERNEL) && defined(LIBC_SCCS) 35226584Sdim#if 0 36226584Sdimstatic char sccsid[] = "@(#)mcount.c 8.1 (Berkeley) 6/4/93"; 37226584Sdim#endif 38226584Sdimstatic const char rcsid[] = 39226584Sdim "$FreeBSD: head/lib/libc/gmon/mcount.c 55206 1999-12-29 05:07:58Z peter $"; 40226584Sdim#endif 41226584Sdim 42327952Sdim#include <sys/param.h> 43226584Sdim#include <sys/gmon.h> 44327952Sdim#ifdef _KERNEL 45226584Sdim#include <sys/systm.h> 46234353Sdim#include <vm/vm.h> 47226584Sdim#include <vm/vm_param.h> 48226584Sdim#include <vm/pmap.h> 49226584Sdimvoid bintr __P((void)); 50226584Sdimvoid btrap __P((void)); 51226584Sdimvoid eintr __P((void)); 52353358Sdimvoid user __P((void)); 53226584Sdim#endif 54226584Sdim 55226584Sdim/* 56327952Sdim * mcount is called on entry to each function compiled with the profiling 57226584Sdim * switch set. _mcount(), which is declared in a machine-dependent way 58234353Sdim * with _MCOUNT_DECL, does the actual work and is either inlined into a 59226584Sdim * C routine or called by an assembly stub. In any case, this magic is 60226584Sdim * taken care of by the MCOUNT definition in <machine/profile.h>. 61226584Sdim * 62226584Sdim * _mcount updates data structures that represent traversals of the 63226584Sdim * program's call graph edges. frompc and selfpc are the return 64226584Sdim * address and function address that represents the given call graph edge. 65226584Sdim * 66226584Sdim * Note: the original BSD code used the same variable (frompcindex) for 67226584Sdim * both frompcindex and frompc. Any reasonable, modern compiler will 68226584Sdim * perform this optimization. 69226584Sdim */ 70226584Sdim_MCOUNT_DECL(frompc, selfpc) /* _mcount; may be static, inline, etc */ 71226584Sdim register uintfptr_t frompc, selfpc; 72226584Sdim{ 73226584Sdim#ifdef GUPROF 74327952Sdim u_int delta; 75226584Sdim#endif 76234353Sdim register fptrdiff_t frompci; 77226584Sdim register u_short *frompcindex; 78226584Sdim register struct tostruct *top, *prevtop; 79226584Sdim register struct gmonparam *p; 80226584Sdim register long toindex; 81226584Sdim#ifdef _KERNEL 82226584Sdim MCOUNT_DECL(s) 83226584Sdim#endif 84226584Sdim 85226584Sdim p = &_gmonparam; 86327952Sdim#ifndef GUPROF /* XXX */ 87226584Sdim /* 88226584Sdim * check that we are profiling 89226584Sdim * and that we aren't recursively invoked. 90226584Sdim */ 91226584Sdim if (p->state != GMON_PROF_ON) 92226584Sdim return; 93327952Sdim#endif 94226584Sdim#ifdef _KERNEL 95226584Sdim MCOUNT_ENTER(s); 96226584Sdim#else 97226584Sdim p->state = GMON_PROF_BUSY; 98226584Sdim#endif 99226584Sdim frompci = frompc - p->lowpc; 100226584Sdim 101226584Sdim#ifdef _KERNEL 102226584Sdim /* 103226584Sdim * When we are called from an exception handler, frompci may be 104226584Sdim * for a user address. Convert such frompci's to the index of 105226584Sdim * user() to merge all user counts. 106226584Sdim */ 107226584Sdim if (frompci >= p->textsize) { 108226584Sdim if (frompci + p->lowpc 109226584Sdim >= (uintfptr_t)(VM_MAXUSER_ADDRESS + UPAGES * PAGE_SIZE)) 110226584Sdim goto done; 111226584Sdim frompci = (uintfptr_t)user - p->lowpc; 112327952Sdim if (frompci >= p->textsize) 113226584Sdim goto done; 114226584Sdim } 115327952Sdim#endif 116327952Sdim 117226584Sdim#ifdef GUPROF 118226584Sdim if (p->state != GMON_PROF_HIRES) 119226584Sdim goto skip_guprof_stuff; 120327952Sdim /* 121226584Sdim * Look at the clock and add the count of clock cycles since the 122226584Sdim * clock was last looked at to a counter for frompc. This 123327952Sdim * solidifies the count for the function containing frompc and 124226584Sdim * effectively starts another clock for the current function. 125226584Sdim * The count for the new clock will be solidified when another 126226584Sdim * function call is made or the function returns. 127226584Sdim * 128226584Sdim * We use the usual sampling counters since they can be located 129327952Sdim * efficiently. 4-byte counters are usually necessary. 130226584Sdim * 131226584Sdim * There are many complications for subtracting the profiling 132226584Sdim * overheads from the counts for normal functions and adding 133226584Sdim * them to the counts for mcount(), mexitcount() and cputime(). 134226584Sdim * We attempt to handle fractional cycles, but the overheads 135261991Sdim * are usually underestimated because they are calibrated for 136226584Sdim * a simpler than usual setup. 137226584Sdim */ 138226584Sdim delta = cputime() - p->mcount_overhead; 139226584Sdim p->cputime_overhead_resid += p->cputime_overhead_frac; 140226584Sdim p->mcount_overhead_resid += p->mcount_overhead_frac; 141226584Sdim if ((int)delta < 0) 142226584Sdim *p->mcount_count += delta + p->mcount_overhead 143226584Sdim - p->cputime_overhead; 144226584Sdim else if (delta != 0) { 145226584Sdim if (p->cputime_overhead_resid >= CALIB_SCALE) { 146226584Sdim p->cputime_overhead_resid -= CALIB_SCALE; 147226584Sdim ++*p->cputime_count; 148261991Sdim --delta; 149226584Sdim } 150226584Sdim if (delta != 0) { 151226584Sdim if (p->mcount_overhead_resid >= CALIB_SCALE) { 152226584Sdim p->mcount_overhead_resid -= CALIB_SCALE; 153226584Sdim ++*p->mcount_count; 154226584Sdim --delta; 155226584Sdim } 156226584Sdim KCOUNT(p, frompci) += delta; 157226584Sdim } 158226584Sdim *p->mcount_count += p->mcount_overhead_sub; 159226584Sdim } 160226584Sdim *p->cputime_count += p->cputime_overhead; 161226584Sdimskip_guprof_stuff: 162226584Sdim#endif /* GUPROF */ 163327952Sdim 164226584Sdim#ifdef _KERNEL 165226584Sdim /* 166226584Sdim * When we are called from an exception handler, frompc is faked 167226584Sdim * to be for where the exception occurred. We've just solidified 168226584Sdim * the count for there. Now convert frompci to the index of btrap() 169226584Sdim * for trap handlers and bintr() for interrupt handlers to make 170226584Sdim * exceptions appear in the call graph as calls from btrap() and 171226584Sdim * bintr() instead of calls from all over. 172226584Sdim */ 173226584Sdim if ((uintfptr_t)selfpc >= (uintfptr_t)btrap 174226584Sdim && (uintfptr_t)selfpc < (uintfptr_t)eintr) { 175226584Sdim if ((uintfptr_t)selfpc >= (uintfptr_t)bintr) 176226584Sdim frompci = (uintfptr_t)bintr - p->lowpc; 177226584Sdim else 178226584Sdim frompci = (uintfptr_t)btrap - p->lowpc; 179226584Sdim } 180327952Sdim#endif 181226584Sdim 182226584Sdim /* 183226584Sdim * check that frompc is a reasonable pc value. 184226584Sdim * for example: signal catchers get called from the stack, 185226584Sdim * not from text space. too bad. 186226584Sdim */ 187226584Sdim if (frompci >= p->textsize) 188226584Sdim goto done; 189226584Sdim 190226584Sdim frompcindex = &p->froms[frompci / (p->hashfraction * sizeof(*p->froms))]; 191226584Sdim toindex = *frompcindex; 192226584Sdim if (toindex == 0) { 193327952Sdim /* 194226584Sdim * first time traversing this arc 195226584Sdim */ 196226584Sdim toindex = ++p->tos[0].link; 197226584Sdim if (toindex >= p->tolimit) 198226584Sdim /* halt further profiling */ 199327952Sdim goto overflow; 200226584Sdim 201226584Sdim *frompcindex = toindex; 202226584Sdim top = &p->tos[toindex]; 203226584Sdim top->selfpc = selfpc; 204226584Sdim top->count = 1; 205226584Sdim top->link = 0; 206226584Sdim goto done; 207226584Sdim } 208226584Sdim top = &p->tos[toindex]; 209226584Sdim if (top->selfpc == selfpc) { 210327952Sdim /* 211226584Sdim * arc at front of chain; usual case. 212226584Sdim */ 213226584Sdim top->count++; 214226584Sdim goto done; 215226584Sdim } 216261991Sdim /* 217226584Sdim * have to go looking down chain for it. 218226584Sdim * top points to what we are looking at, 219226584Sdim * prevtop points to previous top. 220226584Sdim * we know it is not at the head of the chain. 221327952Sdim */ 222226584Sdim for (; /* goto done */; ) { 223226584Sdim if (top->link == 0) { 224226584Sdim /* 225226584Sdim * top is end of the chain and none of the chain 226226584Sdim * had top->selfpc == selfpc. 227226584Sdim * so we allocate a new tostruct 228226584Sdim * and link it to the head of the chain. 229327952Sdim */ 230226584Sdim toindex = ++p->tos[0].link; 231226584Sdim if (toindex >= p->tolimit) 232226584Sdim goto overflow; 233226584Sdim 234226584Sdim top = &p->tos[toindex]; 235261991Sdim top->selfpc = selfpc; 236226584Sdim top->count = 1; 237226584Sdim top->link = *frompcindex; 238226584Sdim *frompcindex = toindex; 239226584Sdim goto done; 240327952Sdim } 241226584Sdim /* 242226584Sdim * otherwise, check the next arc on the chain. 243226584Sdim */ 244226584Sdim prevtop = top; 245226584Sdim top = &p->tos[top->link]; 246226584Sdim if (top->selfpc == selfpc) { 247226584Sdim /* 248327952Sdim * there it is. 249226584Sdim * increment its count 250226584Sdim * move it to the head of the chain. 251226584Sdim */ 252226584Sdim top->count++; 253226584Sdim toindex = prevtop->link; 254226584Sdim prevtop->link = top->link; 255226584Sdim top->link = *frompcindex; 256226584Sdim *frompcindex = toindex; 257226584Sdim goto done; 258226584Sdim } 259226584Sdim 260226584Sdim } 261226584Sdimdone: 262327952Sdim#ifdef _KERNEL 263226584Sdim MCOUNT_EXIT(s); 264226584Sdim#else 265226584Sdim p->state = GMON_PROF_ON; 266226584Sdim#endif 267226584Sdim return; 268226584Sdimoverflow: 269226584Sdim p->state = GMON_PROF_ERROR; 270226584Sdim#ifdef _KERNEL 271226584Sdim MCOUNT_EXIT(s); 272226584Sdim#endif 273226584Sdim return; 274226584Sdim} 275226584Sdim 276226584Sdim/* 277226584Sdim * Actual definition of mcount function. Defined in <machine/profile.h>, 278226584Sdim * which is included by <sys/gmon.h>. 279226584Sdim */ 280226584SdimMCOUNT 281226584Sdim 282226584Sdim#ifdef GUPROF 283226584Sdimvoid 284226584Sdimmexitcount(selfpc) 285226584Sdim uintfptr_t selfpc; 286226584Sdim{ 287226584Sdim struct gmonparam *p; 288226584Sdim uintfptr_t selfpcdiff; 289226584Sdim 290327952Sdim p = &_gmonparam; 291261991Sdim selfpcdiff = selfpc - (uintfptr_t)p->lowpc; 292226584Sdim if (selfpcdiff < p->textsize) { 293226584Sdim u_int delta; 294226584Sdim 295226584Sdim /* 296226584Sdim * Solidify the count for the current function. 297226584Sdim */ 298226584Sdim delta = cputime() - p->mexitcount_overhead; 299226584Sdim p->cputime_overhead_resid += p->cputime_overhead_frac; 300226584Sdim p->mexitcount_overhead_resid += p->mexitcount_overhead_frac; 301226584Sdim if ((int)delta < 0) 302226584Sdim *p->mexitcount_count += delta + p->mexitcount_overhead 303226584Sdim - p->cputime_overhead; 304226584Sdim else if (delta != 0) { 305226584Sdim if (p->cputime_overhead_resid >= CALIB_SCALE) { 306327952Sdim p->cputime_overhead_resid -= CALIB_SCALE; 307226584Sdim ++*p->cputime_count; 308226584Sdim --delta; 309226584Sdim } 310226584Sdim if (delta != 0) { 311226584Sdim if (p->mexitcount_overhead_resid 312226584Sdim >= CALIB_SCALE) { 313226584Sdim p->mexitcount_overhead_resid 314226584Sdim -= CALIB_SCALE; 315226584Sdim ++*p->mexitcount_count; 316226584Sdim --delta; 317226584Sdim } 318226584Sdim KCOUNT(p, selfpcdiff) += delta; 319226584Sdim } 320327952Sdim *p->mexitcount_count += p->mexitcount_overhead_sub; 321226584Sdim } 322226584Sdim *p->cputime_count += p->cputime_overhead; 323261991Sdim } 324226584Sdim} 325226584Sdim#endif /* GUPROF */ 326226584Sdim