1df8bae1dSRodney W. Grimes /*- 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 6df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 7df8bae1dSRodney W. Grimes * are met: 8df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 9df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 10df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 11df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 12df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 13df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 14df8bae1dSRodney W. Grimes * must display the following acknowledgement: 15df8bae1dSRodney W. Grimes * This product includes software developed by the University of 16df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 17df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 18df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 19df8bae1dSRodney W. Grimes * without specific prior written permission. 20df8bae1dSRodney W. Grimes * 21df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31df8bae1dSRodney W. Grimes * SUCH DAMAGE. 32df8bae1dSRodney W. Grimes * 33df8bae1dSRodney W. Grimes * @(#)subr_prof.c 8.3 (Berkeley) 9/23/93 34df8bae1dSRodney W. Grimes */ 35df8bae1dSRodney W. Grimes 36df8bae1dSRodney W. Grimes #include <sys/param.h> 37df8bae1dSRodney W. Grimes #include <sys/systm.h> 38df8bae1dSRodney W. Grimes #include <sys/kernel.h> 39df8bae1dSRodney W. Grimes #include <sys/proc.h> 40df8bae1dSRodney W. Grimes #include <sys/user.h> 41df8bae1dSRodney W. Grimes #include <machine/cpu.h> 42df8bae1dSRodney W. Grimes 43df8bae1dSRodney W. Grimes #ifdef GPROF 44df8bae1dSRodney W. Grimes #include <sys/malloc.h> 45df8bae1dSRodney W. Grimes #include <sys/gmon.h> 46df8bae1dSRodney W. Grimes 47df8bae1dSRodney W. Grimes /* 48df8bae1dSRodney W. Grimes * Froms is actually a bunch of unsigned shorts indexing tos 49df8bae1dSRodney W. Grimes */ 50df8bae1dSRodney W. Grimes struct gmonparam _gmonparam = { GMON_PROF_OFF }; 51df8bae1dSRodney W. Grimes 52df8bae1dSRodney W. Grimes extern char etext[]; 53df8bae1dSRodney W. Grimes 54df8bae1dSRodney W. Grimes kmstartup() 55df8bae1dSRodney W. Grimes { 56df8bae1dSRodney W. Grimes char *cp; 57df8bae1dSRodney W. Grimes struct gmonparam *p = &_gmonparam; 58df8bae1dSRodney W. Grimes /* 59df8bae1dSRodney W. Grimes * Round lowpc and highpc to multiples of the density we're using 60df8bae1dSRodney W. Grimes * so the rest of the scaling (here and in gprof) stays in ints. 61df8bae1dSRodney W. Grimes */ 62df8bae1dSRodney W. Grimes p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER)); 63df8bae1dSRodney W. Grimes p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER)); 64df8bae1dSRodney W. Grimes p->textsize = p->highpc - p->lowpc; 65df8bae1dSRodney W. Grimes printf("Profiling kernel, textsize=%d [%x..%x]\n", 66df8bae1dSRodney W. Grimes p->textsize, p->lowpc, p->highpc); 67df8bae1dSRodney W. Grimes p->kcountsize = p->textsize / HISTFRACTION; 68df8bae1dSRodney W. Grimes p->hashfraction = HASHFRACTION; 69df8bae1dSRodney W. Grimes p->fromssize = p->textsize / HASHFRACTION; 70df8bae1dSRodney W. Grimes p->tolimit = p->textsize * ARCDENSITY / 100; 71df8bae1dSRodney W. Grimes if (p->tolimit < MINARCS) 72df8bae1dSRodney W. Grimes p->tolimit = MINARCS; 73df8bae1dSRodney W. Grimes else if (p->tolimit > MAXARCS) 74df8bae1dSRodney W. Grimes p->tolimit = MAXARCS; 75df8bae1dSRodney W. Grimes p->tossize = p->tolimit * sizeof(struct tostruct); 76df8bae1dSRodney W. Grimes cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize, 77df8bae1dSRodney W. Grimes M_GPROF, M_NOWAIT); 78df8bae1dSRodney W. Grimes if (cp == 0) { 79df8bae1dSRodney W. Grimes printf("No memory for profiling.\n"); 80df8bae1dSRodney W. Grimes return; 81df8bae1dSRodney W. Grimes } 82df8bae1dSRodney W. Grimes bzero(cp, p->kcountsize + p->tossize + p->fromssize); 83df8bae1dSRodney W. Grimes p->tos = (struct tostruct *)cp; 84df8bae1dSRodney W. Grimes cp += p->tossize; 85df8bae1dSRodney W. Grimes p->kcount = (u_short *)cp; 86df8bae1dSRodney W. Grimes cp += p->kcountsize; 87df8bae1dSRodney W. Grimes p->froms = (u_short *)cp; 88df8bae1dSRodney W. Grimes } 89df8bae1dSRodney W. Grimes 90df8bae1dSRodney W. Grimes /* 91df8bae1dSRodney W. Grimes * Return kernel profiling information. 92df8bae1dSRodney W. Grimes */ 93df8bae1dSRodney W. Grimes sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen, p) 94df8bae1dSRodney W. Grimes int *name; 95df8bae1dSRodney W. Grimes u_int namelen; 96df8bae1dSRodney W. Grimes void *oldp; 97df8bae1dSRodney W. Grimes size_t *oldlenp; 98df8bae1dSRodney W. Grimes void *newp; 99df8bae1dSRodney W. Grimes size_t newlen; 100df8bae1dSRodney W. Grimes { 101df8bae1dSRodney W. Grimes struct gmonparam *gp = &_gmonparam; 102df8bae1dSRodney W. Grimes int error; 103df8bae1dSRodney W. Grimes 104df8bae1dSRodney W. Grimes /* all sysctl names at this level are terminal */ 105df8bae1dSRodney W. Grimes if (namelen != 1) 106df8bae1dSRodney W. Grimes return (ENOTDIR); /* overloaded */ 107df8bae1dSRodney W. Grimes 108df8bae1dSRodney W. Grimes switch (name[0]) { 109df8bae1dSRodney W. Grimes case GPROF_STATE: 110df8bae1dSRodney W. Grimes error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state); 111df8bae1dSRodney W. Grimes if (error) 112df8bae1dSRodney W. Grimes return (error); 113df8bae1dSRodney W. Grimes if (gp->state == GMON_PROF_OFF) 114df8bae1dSRodney W. Grimes stopprofclock(&proc0); 115df8bae1dSRodney W. Grimes else 116df8bae1dSRodney W. Grimes startprofclock(&proc0); 117df8bae1dSRodney W. Grimes return (0); 118df8bae1dSRodney W. Grimes case GPROF_COUNT: 119df8bae1dSRodney W. Grimes return (sysctl_struct(oldp, oldlenp, newp, newlen, 120df8bae1dSRodney W. Grimes gp->kcount, gp->kcountsize)); 121df8bae1dSRodney W. Grimes case GPROF_FROMS: 122df8bae1dSRodney W. Grimes return (sysctl_struct(oldp, oldlenp, newp, newlen, 123df8bae1dSRodney W. Grimes gp->froms, gp->fromssize)); 124df8bae1dSRodney W. Grimes case GPROF_TOS: 125df8bae1dSRodney W. Grimes return (sysctl_struct(oldp, oldlenp, newp, newlen, 126df8bae1dSRodney W. Grimes gp->tos, gp->tossize)); 127df8bae1dSRodney W. Grimes case GPROF_GMONPARAM: 128df8bae1dSRodney W. Grimes return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp)); 129df8bae1dSRodney W. Grimes default: 130df8bae1dSRodney W. Grimes return (EOPNOTSUPP); 131df8bae1dSRodney W. Grimes } 132df8bae1dSRodney W. Grimes /* NOTREACHED */ 133df8bae1dSRodney W. Grimes } 134df8bae1dSRodney W. Grimes #endif /* GPROF */ 135df8bae1dSRodney W. Grimes 136df8bae1dSRodney W. Grimes /* 137df8bae1dSRodney W. Grimes * Profiling system call. 138df8bae1dSRodney W. Grimes * 139df8bae1dSRodney W. Grimes * The scale factor is a fixed point number with 16 bits of fraction, so that 140df8bae1dSRodney W. Grimes * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling. 141df8bae1dSRodney W. Grimes */ 142df8bae1dSRodney W. Grimes struct profil_args { 143df8bae1dSRodney W. Grimes caddr_t samples; 144df8bae1dSRodney W. Grimes u_int size; 145df8bae1dSRodney W. Grimes u_int offset; 146df8bae1dSRodney W. Grimes u_int scale; 147df8bae1dSRodney W. Grimes }; 148df8bae1dSRodney W. Grimes /* ARGSUSED */ 149df8bae1dSRodney W. Grimes profil(p, uap, retval) 150df8bae1dSRodney W. Grimes struct proc *p; 151df8bae1dSRodney W. Grimes register struct profil_args *uap; 152df8bae1dSRodney W. Grimes int *retval; 153df8bae1dSRodney W. Grimes { 154df8bae1dSRodney W. Grimes register struct uprof *upp; 155df8bae1dSRodney W. Grimes int s; 156df8bae1dSRodney W. Grimes 157df8bae1dSRodney W. Grimes if (uap->scale > (1 << 16)) 158df8bae1dSRodney W. Grimes return (EINVAL); 159df8bae1dSRodney W. Grimes if (uap->scale == 0) { 160df8bae1dSRodney W. Grimes stopprofclock(p); 161df8bae1dSRodney W. Grimes return (0); 162df8bae1dSRodney W. Grimes } 163df8bae1dSRodney W. Grimes upp = &p->p_stats->p_prof; 164df8bae1dSRodney W. Grimes 165df8bae1dSRodney W. Grimes /* Block profile interrupts while changing state. */ 166df8bae1dSRodney W. Grimes s = splstatclock(); 167df8bae1dSRodney W. Grimes upp->pr_off = uap->offset; 168df8bae1dSRodney W. Grimes upp->pr_scale = uap->scale; 169df8bae1dSRodney W. Grimes upp->pr_base = uap->samples; 170df8bae1dSRodney W. Grimes upp->pr_size = uap->size; 171df8bae1dSRodney W. Grimes startprofclock(p); 172df8bae1dSRodney W. Grimes splx(s); 173df8bae1dSRodney W. Grimes 174df8bae1dSRodney W. Grimes return (0); 175df8bae1dSRodney W. Grimes } 176df8bae1dSRodney W. Grimes 177df8bae1dSRodney W. Grimes /* 178df8bae1dSRodney W. Grimes * Scale is a fixed-point number with the binary point 16 bits 179df8bae1dSRodney W. Grimes * into the value, and is <= 1.0. pc is at most 32 bits, so the 180df8bae1dSRodney W. Grimes * intermediate result is at most 48 bits. 181df8bae1dSRodney W. Grimes */ 182df8bae1dSRodney W. Grimes #define PC_TO_INDEX(pc, prof) \ 183df8bae1dSRodney W. Grimes ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ 184df8bae1dSRodney W. Grimes (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) 185df8bae1dSRodney W. Grimes 186df8bae1dSRodney W. Grimes /* 187df8bae1dSRodney W. Grimes * Collect user-level profiling statistics; called on a profiling tick, 188df8bae1dSRodney W. Grimes * when a process is running in user-mode. This routine may be called 189df8bae1dSRodney W. Grimes * from an interrupt context. We try to update the user profiling buffers 190df8bae1dSRodney W. Grimes * cheaply with fuswintr() and suswintr(). If that fails, we revert to 191df8bae1dSRodney W. Grimes * an AST that will vector us to trap() with a context in which copyin 192df8bae1dSRodney W. Grimes * and copyout will work. Trap will then call addupc_task(). 193df8bae1dSRodney W. Grimes * 194df8bae1dSRodney W. Grimes * Note that we may (rarely) not get around to the AST soon enough, and 195df8bae1dSRodney W. Grimes * lose profile ticks when the next tick overwrites this one, but in this 196df8bae1dSRodney W. Grimes * case the system is overloaded and the profile is probably already 197df8bae1dSRodney W. Grimes * inaccurate. 198df8bae1dSRodney W. Grimes */ 199df8bae1dSRodney W. Grimes void 200df8bae1dSRodney W. Grimes addupc_intr(p, pc, ticks) 201df8bae1dSRodney W. Grimes register struct proc *p; 202df8bae1dSRodney W. Grimes register u_long pc; 203df8bae1dSRodney W. Grimes u_int ticks; 204df8bae1dSRodney W. Grimes { 205df8bae1dSRodney W. Grimes register struct uprof *prof; 206df8bae1dSRodney W. Grimes register caddr_t addr; 207df8bae1dSRodney W. Grimes register u_int i; 208df8bae1dSRodney W. Grimes register int v; 209df8bae1dSRodney W. Grimes 210df8bae1dSRodney W. Grimes if (ticks == 0) 211df8bae1dSRodney W. Grimes return; 212df8bae1dSRodney W. Grimes prof = &p->p_stats->p_prof; 213df8bae1dSRodney W. Grimes if (pc < prof->pr_off || 214df8bae1dSRodney W. Grimes (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) 215df8bae1dSRodney W. Grimes return; /* out of range; ignore */ 216df8bae1dSRodney W. Grimes 217df8bae1dSRodney W. Grimes addr = prof->pr_base + i; 218df8bae1dSRodney W. Grimes if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) { 219df8bae1dSRodney W. Grimes prof->pr_addr = pc; 220df8bae1dSRodney W. Grimes prof->pr_ticks = ticks; 221df8bae1dSRodney W. Grimes need_proftick(p); 222df8bae1dSRodney W. Grimes } 223df8bae1dSRodney W. Grimes } 224df8bae1dSRodney W. Grimes 225df8bae1dSRodney W. Grimes /* 226df8bae1dSRodney W. Grimes * Much like before, but we can afford to take faults here. If the 227df8bae1dSRodney W. Grimes * update fails, we simply turn off profiling. 228df8bae1dSRodney W. Grimes */ 229df8bae1dSRodney W. Grimes void 230df8bae1dSRodney W. Grimes addupc_task(p, pc, ticks) 231df8bae1dSRodney W. Grimes register struct proc *p; 232df8bae1dSRodney W. Grimes register u_long pc; 233df8bae1dSRodney W. Grimes u_int ticks; 234df8bae1dSRodney W. Grimes { 235df8bae1dSRodney W. Grimes register struct uprof *prof; 236df8bae1dSRodney W. Grimes register caddr_t addr; 237df8bae1dSRodney W. Grimes register u_int i; 238df8bae1dSRodney W. Grimes u_short v; 239df8bae1dSRodney W. Grimes 240df8bae1dSRodney W. Grimes /* Testing P_PROFIL may be unnecessary, but is certainly safe. */ 241df8bae1dSRodney W. Grimes if ((p->p_flag & P_PROFIL) == 0 || ticks == 0) 242df8bae1dSRodney W. Grimes return; 243df8bae1dSRodney W. Grimes 244df8bae1dSRodney W. Grimes prof = &p->p_stats->p_prof; 245df8bae1dSRodney W. Grimes if (pc < prof->pr_off || 246df8bae1dSRodney W. Grimes (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) 247df8bae1dSRodney W. Grimes return; 248df8bae1dSRodney W. Grimes 249df8bae1dSRodney W. Grimes addr = prof->pr_base + i; 250df8bae1dSRodney W. Grimes if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) { 251df8bae1dSRodney W. Grimes v += ticks; 252df8bae1dSRodney W. Grimes if (copyout((caddr_t)&v, addr, sizeof(v)) == 0) 253df8bae1dSRodney W. Grimes return; 254df8bae1dSRodney W. Grimes } 255df8bae1dSRodney W. Grimes stopprofclock(p); 256df8bae1dSRodney W. Grimes } 257