/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1983, 1992, 19934* The Regents of the University of California. All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14* 3. Neither the name of the University nor the names of its contributors15* may be used to endorse or promote products derived from this software16* without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND19* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE20* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE21* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE22* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL23* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS24* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)25* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT26* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY27* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF28* SUCH DAMAGE.29*/3031#include <sys/param.h>32#include <sys/gmon.h>33#ifdef _KERNEL34#include <sys/systm.h>35#include <vm/vm.h>36#include <vm/vm_param.h>37#include <vm/pmap.h>38void bintr(void);39void btrap(void);40void eintr(void);41void user(void);42#endif43#include <machine/atomic.h>4445/*46* mcount is called on entry to each function compiled with the profiling47* switch set. _mcount(), which is declared in a machine-dependent way48* with _MCOUNT_DECL, does the actual work and is either inlined into a49* C routine or called by an assembly stub. In any case, this magic is50* taken care of by the MCOUNT definition in <machine/profile.h>.51*52* _mcount updates data structures that represent traversals of the53* program's call graph edges. frompc and selfpc are the return54* address and function address that represents the given call graph edge.55*56* Note: the original BSD code used the same variable (frompcindex) for57* both frompcindex and frompc. Any reasonable, modern compiler will58* perform this optimization.59*/60/* _mcount; may be static, inline, etc */61_MCOUNT_DECL(uintfptr_t frompc, uintfptr_t selfpc)62{63#ifdef GUPROF64u_int delta;65#endif66fptrdiff_t frompci;67u_short *frompcindex;68struct tostruct *top, *prevtop;69struct gmonparam *p;70long toindex;71#ifdef _KERNEL72MCOUNT_DECL(s)73#endif7475p = &_gmonparam;76#ifndef GUPROF /* XXX */77/*78* check that we are profiling79* and that we aren't recursively invoked.80*/81if (p->state != GMON_PROF_ON)82return;83#endif84#ifdef _KERNEL85MCOUNT_ENTER(s);86#else87if (!atomic_cmpset_acq_int(&p->state, GMON_PROF_ON, GMON_PROF_BUSY))88return;89#endif90frompci = frompc - p->lowpc;9192#ifdef _KERNEL93/*94* When we are called from an exception handler, frompci may be95* for a user address. Convert such frompci's to the index of96* user() to merge all user counts.97*/98if (frompci >= p->textsize) {99if (frompci + p->lowpc100>= (uintfptr_t)(VM_MAXUSER_ADDRESS + UPAGES * PAGE_SIZE))101goto done;102frompci = (uintfptr_t)user - p->lowpc;103if (frompci >= p->textsize)104goto done;105}106#endif107108#ifdef GUPROF109if (p->state != GMON_PROF_HIRES)110goto skip_guprof_stuff;111/*112* Look at the clock and add the count of clock cycles since the113* clock was last looked at to a counter for frompc. This114* solidifies the count for the function containing frompc and115* effectively starts another clock for the current function.116* The count for the new clock will be solidified when another117* function call is made or the function returns.118*119* We use the usual sampling counters since they can be located120* efficiently. 4-byte counters are usually necessary.121*122* There are many complications for subtracting the profiling123* overheads from the counts for normal functions and adding124* them to the counts for mcount(), mexitcount() and cputime().125* We attempt to handle fractional cycles, but the overheads126* are usually underestimated because they are calibrated for127* a simpler than usual setup.128*/129delta = cputime() - p->mcount_overhead;130p->cputime_overhead_resid += p->cputime_overhead_frac;131p->mcount_overhead_resid += p->mcount_overhead_frac;132if ((int)delta < 0)133*p->mcount_count += delta + p->mcount_overhead134- p->cputime_overhead;135else if (delta != 0) {136if (p->cputime_overhead_resid >= CALIB_SCALE) {137p->cputime_overhead_resid -= CALIB_SCALE;138++*p->cputime_count;139--delta;140}141if (delta != 0) {142if (p->mcount_overhead_resid >= CALIB_SCALE) {143p->mcount_overhead_resid -= CALIB_SCALE;144++*p->mcount_count;145--delta;146}147KCOUNT(p, frompci) += delta;148}149*p->mcount_count += p->mcount_overhead_sub;150}151*p->cputime_count += p->cputime_overhead;152skip_guprof_stuff:153#endif /* GUPROF */154155#ifdef _KERNEL156/*157* When we are called from an exception handler, frompc is faked158* to be for where the exception occurred. We've just solidified159* the count for there. Now convert frompci to the index of btrap()160* for trap handlers and bintr() for interrupt handlers to make161* exceptions appear in the call graph as calls from btrap() and162* bintr() instead of calls from all over.163*/164if ((uintfptr_t)selfpc >= (uintfptr_t)btrap165&& (uintfptr_t)selfpc < (uintfptr_t)eintr) {166if ((uintfptr_t)selfpc >= (uintfptr_t)bintr)167frompci = (uintfptr_t)bintr - p->lowpc;168else169frompci = (uintfptr_t)btrap - p->lowpc;170}171#endif172173/*174* check that frompc is a reasonable pc value.175* for example: signal catchers get called from the stack,176* not from text space. too bad.177*/178if (frompci >= p->textsize)179goto done;180181frompcindex = &p->froms[frompci / (p->hashfraction * sizeof(*p->froms))];182toindex = *frompcindex;183if (toindex == 0) {184/*185* first time traversing this arc186*/187toindex = ++p->tos[0].link;188if (toindex >= p->tolimit)189/* halt further profiling */190goto overflow;191192*frompcindex = toindex;193top = &p->tos[toindex];194top->selfpc = selfpc;195top->count = 1;196top->link = 0;197goto done;198}199top = &p->tos[toindex];200if (top->selfpc == selfpc) {201/*202* arc at front of chain; usual case.203*/204top->count++;205goto done;206}207/*208* have to go looking down chain for it.209* top points to what we are looking at,210* prevtop points to previous top.211* we know it is not at the head of the chain.212*/213for (; /* goto done */; ) {214if (top->link == 0) {215/*216* top is end of the chain and none of the chain217* had top->selfpc == selfpc.218* so we allocate a new tostruct219* and link it to the head of the chain.220*/221toindex = ++p->tos[0].link;222if (toindex >= p->tolimit)223goto overflow;224225top = &p->tos[toindex];226top->selfpc = selfpc;227top->count = 1;228top->link = *frompcindex;229*frompcindex = toindex;230goto done;231}232/*233* otherwise, check the next arc on the chain.234*/235prevtop = top;236top = &p->tos[top->link];237if (top->selfpc == selfpc) {238/*239* there it is.240* increment its count241* move it to the head of the chain.242*/243top->count++;244toindex = prevtop->link;245prevtop->link = top->link;246top->link = *frompcindex;247*frompcindex = toindex;248goto done;249}250251}252done:253#ifdef _KERNEL254MCOUNT_EXIT(s);255#else256atomic_store_rel_int(&p->state, GMON_PROF_ON);257#endif258return;259overflow:260atomic_store_rel_int(&p->state, GMON_PROF_ERROR);261#ifdef _KERNEL262MCOUNT_EXIT(s);263#endif264return;265}266267/*268* Actual definition of mcount function. Defined in <machine/profile.h>,269* which is included by <sys/gmon.h>.270*/271MCOUNT272273#ifdef GUPROF274void275mexitcount(uintfptr_t selfpc)276{277struct gmonparam *p;278uintfptr_t selfpcdiff;279280p = &_gmonparam;281selfpcdiff = selfpc - (uintfptr_t)p->lowpc;282if (selfpcdiff < p->textsize) {283u_int delta;284285/*286* Solidify the count for the current function.287*/288delta = cputime() - p->mexitcount_overhead;289p->cputime_overhead_resid += p->cputime_overhead_frac;290p->mexitcount_overhead_resid += p->mexitcount_overhead_frac;291if ((int)delta < 0)292*p->mexitcount_count += delta + p->mexitcount_overhead293- p->cputime_overhead;294else if (delta != 0) {295if (p->cputime_overhead_resid >= CALIB_SCALE) {296p->cputime_overhead_resid -= CALIB_SCALE;297++*p->cputime_count;298--delta;299}300if (delta != 0) {301if (p->mexitcount_overhead_resid302>= CALIB_SCALE) {303p->mexitcount_overhead_resid304-= CALIB_SCALE;305++*p->mexitcount_count;306--delta;307}308KCOUNT(p, selfpcdiff) += delta;309}310*p->mexitcount_count += p->mexitcount_overhead_sub;311}312*p->cputime_count += p->cputime_overhead;313}314}315#endif /* GUPROF */316317318