1 /* $OpenBSD: subr_prof.c,v 1.31 2021/09/03 16:45:45 jasper Exp $ */ 2 /* $NetBSD: subr_prof.c,v 1.12 1996/04/22 01:38:50 christos Exp $ */ 3 4 /*- 5 * Copyright (c) 1982, 1986, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * @(#)subr_prof.c 8.3 (Berkeley) 9/23/93 33 */ 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/proc.h> 38 #include <sys/resourcevar.h> 39 #include <sys/mount.h> 40 #include <sys/sysctl.h> 41 #include <sys/syscallargs.h> 42 43 44 #if defined(GPROF) || defined(DDBPROF) 45 #include <sys/malloc.h> 46 #include <sys/gmon.h> 47 48 #include <uvm/uvm_extern.h> 49 50 #include <machine/db_machdep.h> 51 #include <ddb/db_extern.h> 52 53 /* 54 * Flag to prevent CPUs from executing the mcount() monitor function 55 * until we're sure they are in a sane state. 56 */ 57 int gmoninit = 0; 58 59 extern char etext[]; 60 61 void 62 prof_init(void) 63 { 64 CPU_INFO_ITERATOR cii; 65 struct cpu_info *ci; 66 struct gmonparam *p; 67 u_long lowpc, highpc, textsize; 68 u_long kcountsize, fromssize, tossize; 69 long tolimit; 70 char *cp; 71 int size; 72 73 /* 74 * Round lowpc and highpc to multiples of the density we're using 75 * so the rest of the scaling (here and in gprof) stays in ints. 76 */ 77 lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER)); 78 highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER)); 79 textsize = highpc - lowpc; 80 #ifdef GPROF 81 printf("Profiling kernel, textsize=%ld [%lx..%lx]\n", 82 textsize, lowpc, highpc); 83 #endif 84 kcountsize = textsize / HISTFRACTION; 85 fromssize = textsize / HASHFRACTION; 86 tolimit = textsize * ARCDENSITY / 100; 87 if (tolimit < MINARCS) 88 tolimit = MINARCS; 89 else if (tolimit > MAXARCS) 90 tolimit = MAXARCS; 91 tossize = tolimit * sizeof(struct tostruct); 92 size = sizeof(*p) + kcountsize + fromssize + tossize; 93 94 /* Allocate and initialize one profiling buffer per CPU. */ 95 CPU_INFO_FOREACH(cii, ci) { 96 cp = km_alloc(round_page(size), &kv_any, &kp_zero, &kd_nowait); 97 if (cp == NULL) { 98 printf("No memory for profiling.\n"); 99 return; 100 } 101 102 p = (struct gmonparam *)cp; 103 cp += sizeof(*p); 104 p->tos = (struct tostruct *)cp; 105 cp += tossize; 106 p->kcount = (u_short *)cp; 107 cp += kcountsize; 108 p->froms = (u_short *)cp; 109 110 p->state = GMON_PROF_OFF; 111 p->lowpc = lowpc; 112 p->highpc = highpc; 113 p->textsize = textsize; 114 p->hashfraction = HASHFRACTION; 115 p->kcountsize = kcountsize; 116 p->fromssize = fromssize; 117 p->tolimit = tolimit; 118 p->tossize = tossize; 119 120 ci->ci_gmon = p; 121 } 122 } 123 124 int 125 prof_state_toggle(struct gmonparam *gp, int oldstate) 126 { 127 int error = 0; 128 129 if (gp->state == oldstate) 130 return (0); 131 132 switch (gp->state) { 133 case GMON_PROF_ON: 134 #if !defined(GPROF) 135 /* 136 * If this is not a profiling kernel, we need to patch 137 * all symbols that can be instrummented. 138 */ 139 error = db_prof_enable(); 140 #endif 141 if (error == 0) 142 startprofclock(&process0); 143 break; 144 default: 145 error = EINVAL; 146 gp->state = GMON_PROF_OFF; 147 /* FALLTHROUGH */ 148 case GMON_PROF_OFF: 149 stopprofclock(&process0); 150 #if !defined(GPROF) 151 db_prof_disable(); 152 #endif 153 break; 154 } 155 156 return (error); 157 } 158 159 /* 160 * Return kernel profiling information. 161 */ 162 int 163 sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, 164 size_t newlen) 165 { 166 CPU_INFO_ITERATOR cii; 167 struct cpu_info *ci; 168 struct gmonparam *gp = NULL; 169 int error, cpuid, op, state; 170 171 /* all sysctl names at this level are name and field */ 172 if (namelen != 2) 173 return (ENOTDIR); /* overloaded */ 174 175 op = name[0]; 176 cpuid = name[1]; 177 178 CPU_INFO_FOREACH(cii, ci) { 179 if (cpuid == CPU_INFO_UNIT(ci)) { 180 gp = ci->ci_gmon; 181 break; 182 } 183 } 184 185 if (gp == NULL) 186 return (EOPNOTSUPP); 187 188 /* Assume that if we're here it is safe to execute profiling. */ 189 gmoninit = 1; 190 191 switch (op) { 192 case GPROF_STATE: 193 state = gp->state; 194 error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state); 195 if (error) 196 return (error); 197 return (prof_state_toggle(gp, state)); 198 case GPROF_COUNT: 199 return (sysctl_struct(oldp, oldlenp, newp, newlen, 200 gp->kcount, gp->kcountsize)); 201 case GPROF_FROMS: 202 return (sysctl_struct(oldp, oldlenp, newp, newlen, 203 gp->froms, gp->fromssize)); 204 case GPROF_TOS: 205 return (sysctl_struct(oldp, oldlenp, newp, newlen, 206 gp->tos, gp->tossize)); 207 case GPROF_GMONPARAM: 208 return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp)); 209 default: 210 return (EOPNOTSUPP); 211 } 212 /* NOTREACHED */ 213 } 214 #endif /* GPROF || DDBPROF */ 215 216 /* 217 * Profiling system call. 218 * 219 * The scale factor is a fixed point number with 16 bits of fraction, so that 220 * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling. 221 */ 222 int 223 sys_profil(struct proc *p, void *v, register_t *retval) 224 { 225 struct sys_profil_args /* { 226 syscallarg(caddr_t) samples; 227 syscallarg(size_t) size; 228 syscallarg(u_long) offset; 229 syscallarg(u_int) scale; 230 } */ *uap = v; 231 struct process *pr = p->p_p; 232 struct uprof *upp; 233 int s; 234 235 if (SCARG(uap, scale) > (1 << 16)) 236 return (EINVAL); 237 if (SCARG(uap, scale) == 0) { 238 stopprofclock(pr); 239 return (0); 240 } 241 upp = &pr->ps_prof; 242 243 /* Block profile interrupts while changing state. */ 244 s = splstatclock(); 245 upp->pr_off = SCARG(uap, offset); 246 upp->pr_scale = SCARG(uap, scale); 247 upp->pr_base = (caddr_t)SCARG(uap, samples); 248 upp->pr_size = SCARG(uap, size); 249 startprofclock(pr); 250 splx(s); 251 252 return (0); 253 } 254 255 /* 256 * Scale is a fixed-point number with the binary point 16 bits 257 * into the value, and is <= 1.0. pc is at most 32 bits, so the 258 * intermediate result is at most 48 bits. 259 */ 260 #define PC_TO_INDEX(pc, prof) \ 261 ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ 262 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) 263 264 /* 265 * Collect user-level profiling statistics; called on a profiling tick, 266 * when a process is running in user-mode. This routine may be called 267 * from an interrupt context. Schedule an AST that will vector us to 268 * trap() with a context in which copyin and copyout will work. 269 * Trap will then call addupc_task(). 270 */ 271 void 272 addupc_intr(struct proc *p, u_long pc) 273 { 274 struct uprof *prof; 275 276 prof = &p->p_p->ps_prof; 277 if (pc < prof->pr_off || PC_TO_INDEX(pc, prof) >= prof->pr_size) 278 return; /* out of range; ignore */ 279 280 p->p_prof_addr = pc; 281 p->p_prof_ticks++; 282 atomic_setbits_int(&p->p_flag, P_OWEUPC); 283 need_proftick(p); 284 } 285 286 287 /* 288 * Much like before, but we can afford to take faults here. If the 289 * update fails, we simply turn off profiling. 290 */ 291 void 292 addupc_task(struct proc *p, u_long pc, u_int nticks) 293 { 294 struct process *pr = p->p_p; 295 struct uprof *prof; 296 caddr_t addr; 297 u_int i; 298 u_short v; 299 300 /* Testing PS_PROFIL may be unnecessary, but is certainly safe. */ 301 if ((pr->ps_flags & PS_PROFIL) == 0 || nticks == 0) 302 return; 303 304 prof = &pr->ps_prof; 305 if (pc < prof->pr_off || 306 (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) 307 return; 308 309 addr = prof->pr_base + i; 310 if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) { 311 v += nticks; 312 if (copyout((caddr_t)&v, addr, sizeof(v)) == 0) 313 return; 314 } 315 stopprofclock(pr); 316 } 317