1/* 2 * Copyright (c) 2000-2008 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ 29/*- 30 * Copyright (c) 1982, 1986, 1993 31 * The Regents of the University of California. All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. All advertising materials mentioning features or use of this software 42 * must display the following acknowledgement: 43 * This product includes software developed by the University of 44 * California, Berkeley and its contributors. 45 * 4. Neither the name of the University nor the names of its contributors 46 * may be used to endorse or promote products derived from this software 47 * without specific prior written permission. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 * 61 * @(#)subr_prof.c 8.3 (Berkeley) 9/23/93 62 */ 63 64#ifdef GPROF 65#include <libkern/kernel_mach_header.h> 66#endif 67 68#include <sys/param.h> 69#include <sys/systm.h> 70#include <sys/kernel.h> 71#include <sys/proc_internal.h> 72#include <sys/user.h> 73#include <machine/spl.h> 74#include <machine/machine_routines.h> 75 76#include <sys/mount_internal.h> 77#include <sys/sysproto.h> 78 79#include <mach/mach_types.h> 80#include <kern/kern_types.h> 81#include <kern/cpu_number.h> 82#include <kern/kalloc.h> 83 84#ifdef GPROF 85#include <sys/malloc.h> 86#include <sys/gmon.h> 87 88extern int sysctl_doprof(int *, u_int, user_addr_t, size_t *, 89 user_addr_t, size_t newlen); 90extern int sysctl_struct(user_addr_t, size_t *, 91 user_addr_t, size_t, void *, int); 92 93lck_spin_t * mcount_lock; 94lck_grp_t * mcount_lock_grp; 95lck_attr_t * mcount_lock_attr; 96 97/* 98 * Froms is actually a bunch of unsigned shorts indexing tos 99 */ 100struct gmonparam _gmonparam = { .state = GMON_PROF_OFF }; 101 102/* 103 * This code uses 32 bit mach object segment information from the currently 104 * running kernel. 105 */ 106void 107kmstartup(void) 108{ 109 tostruct_t *cp; 110 kernel_segment_command_t *sgp; /* 32 bit mach object file segment */ 111 struct gmonparam *p = &_gmonparam; 112 113 sgp = getsegbyname("__TEXT"); 114 p->lowpc = (u_int32_t)sgp->vmaddr; 115 p->highpc = (u_int32_t)(sgp->vmaddr + sgp->vmsize); 116 117 /* 118 * Round lowpc and highpc to multiples of the density we're using 119 * so the rest of the scaling (here and in gprof) stays in ints. 120 */ 121 p->lowpc = ROUNDDOWN(p->lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); 122 p->highpc = ROUNDUP(p->highpc, HISTFRACTION * sizeof(HISTCOUNTER)); 123 p->textsize = p->highpc - p->lowpc; 124 printf("Profiling kernel, textsize=%lu [0x%016lx..0x%016lx]\n", 125 p->textsize, p->lowpc, p->highpc); 126 p->kcountsize = p->textsize / HISTFRACTION; 127 p->hashfraction = HASHFRACTION; 128 p->fromssize = p->textsize / HASHFRACTION; 129 p->tolimit = p->textsize * ARCDENSITY / 100; 130 if (p->tolimit < MINARCS) 131 p->tolimit = MINARCS; 132 else if (p->tolimit > MAXARCS) 133 p->tolimit = MAXARCS; 134 p->tossize = p->tolimit * sizeof(tostruct_t); 135 /* Why not use MALLOC with M_GPROF ? */ 136 cp = (tostruct_t *)kalloc(p->kcountsize + p->fromssize + p->tossize); 137 if (cp == 0) { 138 printf("No memory for profiling.\n"); 139 return; 140 } 141 bzero(cp, p->kcountsize + p->tossize + p->fromssize); 142 p->tos = cp; 143 cp = (tostruct_t *)((vm_offset_t)cp + p->tossize); 144 p->kcount = (u_short *)cp; 145 cp = (tostruct_t *)((vm_offset_t)cp + p->kcountsize); 146 p->froms = (u_short *)cp; 147 148 mcount_lock_grp = lck_grp_alloc_init("MCOUNT", LCK_GRP_ATTR_NULL); 149 mcount_lock_attr = lck_attr_alloc_init(); 150 mcount_lock = lck_spin_alloc_init(mcount_lock_grp, mcount_lock_attr); 151 152} 153 154/* 155 * XXX These should be broken out into per-argument OID values, 156 * XXX since there are no sub-OID parameter values, but unfortunately 157 * XXX there is barely enough time for an initial conversion. 158 * 159 * Note: These items appear to be read/write. 160 */ 161STATIC int 162sysctl_doprofhandle SYSCTL_HANDLER_ARGS 163{ 164sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, 165 user_addr_t newp, size_t newlen) 166{ 167 __unused int cmd = oidp->oid_arg2; /* subcommand*/ 168 int *name = arg1; /* oid element argument vector */ 169 int namelen = arg2; /* number of oid element arguments */ 170 user_addr_t oldp = req->oldptr; /* user buffer copy out address */ 171 size_t *oldlenp = req->oldlen; /* user buffer copy out size */ 172 user_addr_t newp = req->newptr; /* user buffer copy in address */ 173 size_t newlen = req->newlen; /* user buffer copy in size */ 174 175 struct gmonparam *gp = &_gmonparam; 176 int error = 0; 177 178 /* all sysctl names at this level are terminal */ 179 if (namelen != 1) 180 return (ENOTDIR); /* overloaded */ 181 182 switch (name[0]) { 183 case GPROF_STATE: 184 error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state); 185 if (error) 186 break; 187 if (gp->state == GMON_PROF_OFF) 188 stopprofclock(kernproc); 189 else 190 startprofclock(kernproc); 191 break; 192 case GPROF_COUNT: 193 error = sysctl_struct(oldp, oldlenp, newp, newlen, 194 gp->kcount, gp->kcountsize); 195 break; 196 case GPROF_FROMS: 197 error = sysctl_struct(oldp, oldlenp, newp, newlen, 198 gp->froms, gp->fromssize); 199 break; 200 case GPROF_TOS: 201 error = sysctl_struct(oldp, oldlenp, newp, newlen, 202 gp->tos, gp->tossize); 203 break; 204 case GPROF_GMONPARAM: 205 error = sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp); 206 break; 207 default: 208 error = ENOTSUP; 209 break; 210 } 211 212 /* adjust index so we return the right required/consumed amount */ 213 if (!error) 214 req->oldidx += req->oldlen; 215 216 return(error); 217} 218SYSCTL_PROC(_kern, KERN_PROF, prof, STLFLAG_NODE|CTLFLAG_RW | CTLFLAG_LOCKED, 219 0, /* Pointer argument (arg1) */ 220 0, /* Integer argument (arg2) */ 221 sysctl_doprofhandle, /* Handler function */ 222 NULL, /* No explicit data */ 223 ""); 224 225 226/* 227 * mcount() called with interrupts disabled. 228 */ 229void 230mcount( 231 uintptr_t frompc, 232 uintptr_t selfpc 233) 234{ 235 unsigned short *frompcindex; 236 tostruct_t *top, *prevtop; 237 struct gmonparam *p = &_gmonparam; 238 long toindex; 239 240 /* 241 * check that we are profiling 242 * and that we aren't recursively invoked. 243 */ 244 if (p->state != GMON_PROF_ON) 245 return; 246 247 lck_spin_lock(mcount_lock); 248 249 /* 250 * check that frompcindex is a reasonable pc value. 251 * for example: signal catchers get called from the stack, 252 * not from text space. too bad. 253 */ 254 frompc -= p->lowpc; 255 if (frompc > p->textsize) 256 goto done; 257 258 frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))]; 259 toindex = *frompcindex; 260 if (toindex == 0) { 261 /* 262 * first time traversing this arc 263 */ 264 toindex = ++p->tos[0].link; 265 if (toindex >= p->tolimit) { 266 /* halt further profiling */ 267 goto overflow; 268 } 269 *frompcindex = toindex; 270 top = &p->tos[toindex]; 271 top->selfpc = selfpc; 272 top->count = 1; 273 top->link = 0; 274 goto done; 275 } 276 top = &p->tos[toindex]; 277 if (top->selfpc == selfpc) { 278 /* 279 * arc at front of chain; usual case. 280 */ 281 top->count++; 282 goto done; 283 } 284 /* 285 * have to go looking down chain for it. 286 * top points to what we are looking at, 287 * prevtop points to previous top. 288 * we know it is not at the head of the chain. 289 */ 290 for (; /* goto done */; ) { 291 if (top->link == 0) { 292 /* 293 * top is end of the chain and none of the chain 294 * had top->selfpc == selfpc. 295 * so we allocate a new tostruct 296 * and link it to the head of the chain. 297 */ 298 toindex = ++p->tos[0].link; 299 if (toindex >= p->tolimit) { 300 goto overflow; 301 } 302 top = &p->tos[toindex]; 303 top->selfpc = selfpc; 304 top->count = 1; 305 top->link = *frompcindex; 306 *frompcindex = toindex; 307 goto done; 308 } 309 /* 310 * otherwise, check the next arc on the chain. 311 */ 312 prevtop = top; 313 top = &p->tos[top->link]; 314 if (top->selfpc == selfpc) { 315 /* 316 * there it is. 317 * increment its count 318 * move it to the head of the chain. 319 */ 320 top->count++; 321 toindex = prevtop->link; 322 prevtop->link = top->link; 323 top->link = *frompcindex; 324 *frompcindex = toindex; 325 goto done; 326 } 327 328 } 329done: 330 lck_spin_unlock(mcount_lock); 331 return; 332 333overflow: 334 p->state = GMON_PROF_ERROR; 335 lck_spin_unlock(mcount_lock); 336 printf("mcount: tos overflow\n"); 337 return; 338} 339 340#endif /* GPROF */ 341 342#define PROFILE_LOCK(x) 343#define PROFILE_UNLOCK(x) 344 345 346/* 347 * Scale is a fixed-point number with the binary point 16 bits 348 * into the value, and is <= 1.0. pc is at most 32 bits, so the 349 * intermediate result is at most 48 bits. 350 */ 351//K64todo - this doesn't fit into 64 bit any more, it needs 64+16 352#define PC_TO_INDEX(pc, prof) \ 353 ((user_addr_t)(((u_quad_t)((pc) - (prof)->pr_off) * \ 354 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) 355 356/* 357 * Collect user-level profiling statistics; called on a profiling tick, 358 * when a process is running in user-mode. We use 359 * an AST that will vector us to trap() with a context in which copyin 360 * and copyout will work. Trap will then call addupc_task(). 361 * 362 * Note that we may (rarely) not get around to the AST soon enough, and 363 * lose profile ticks when the next tick overwrites this one, but in this 364 * case the system is overloaded and the profile is probably already 365 * inaccurate. 366 * 367 * We can afford to take faults here. If the 368 * update fails, we simply turn off profiling. 369 */ 370void 371addupc_task(struct proc *p, user_addr_t pc, u_int ticks) 372{ 373 user_addr_t off; 374 u_short count; 375 376 /* Testing P_PROFIL may be unnecessary, but is certainly safe. */ 377 if ((p->p_flag & P_PROFIL) == 0 || ticks == 0) 378 return; 379 380 if (proc_is64bit(p)) { 381 struct user_uprof *prof; 382 user_addr_t cell; 383 384 for (prof = &p->p_stats->user_p_prof; prof; prof = prof->pr_next) { 385 off = PC_TO_INDEX(pc, prof); 386 cell = (prof->pr_base + off); 387 if (cell >= prof->pr_base && 388 cell < (prof->pr_size + prof->pr_base)) { 389 if (copyin(cell, (caddr_t) &count, sizeof(count)) == 0) { 390 count += ticks; 391 if(copyout((caddr_t) &count, cell, sizeof(count)) == 0) 392 return; 393 } 394 p->p_stats->user_p_prof.pr_scale = 0; 395 stopprofclock(p); 396 break; 397 } 398 } 399 } 400 else { 401 struct uprof *prof; 402 short *cell; 403 404 for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) { 405 off = PC_TO_INDEX(pc,prof); 406 cell = (short *)(prof->pr_base + off); 407 if (cell >= (short *)prof->pr_base && 408 cell < (short*)(prof->pr_size + prof->pr_base)) { 409 if (copyin(CAST_USER_ADDR_T(cell), (caddr_t) &count, sizeof(count)) == 0) { 410 count += ticks; 411 if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0) 412 return; 413 } 414 p->p_stats->p_prof.pr_scale = 0; 415 stopprofclock(p); 416 break; 417 } 418 } 419 } 420} 421