1/* 2 * Copyright (c) 2000-2006 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 <kern/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 char *cp; 110 struct segment_command *sgp; /* 32 bit mach object file segment */ 111 struct gmonparam *p = &_gmonparam; 112 113 sgp = getsegbyname("__TEXT"); 114 p->lowpc = (u_long)sgp->vmaddr; 115 p->highpc = (u_long)(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(struct tostruct); 135 /* Why not use MALLOC with M_GPROF ? */ 136 cp = (char *)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 = (struct tostruct *)cp; 143 cp += p->tossize; 144 p->kcount = (u_short *)cp; 145 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 * Return kernel profiling information. 156 */ 157int 158sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, 159 user_addr_t newp, size_t newlen) 160{ 161 struct gmonparam *gp = &_gmonparam; 162 int error; 163 164 /* all sysctl names at this level are terminal */ 165 if (namelen != 1) 166 return (ENOTDIR); /* overloaded */ 167 168 switch (name[0]) { 169 case GPROF_STATE: 170 error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state); 171 if (error) 172 return (error); 173 if (gp->state == GMON_PROF_OFF) 174 stopprofclock(kernproc); 175 else 176 startprofclock(kernproc); 177 return (0); 178 case GPROF_COUNT: 179 return (sysctl_struct(oldp, oldlenp, newp, newlen, 180 gp->kcount, gp->kcountsize)); 181 case GPROF_FROMS: 182 return (sysctl_struct(oldp, oldlenp, newp, newlen, 183 gp->froms, gp->fromssize)); 184 case GPROF_TOS: 185 return (sysctl_struct(oldp, oldlenp, newp, newlen, 186 gp->tos, gp->tossize)); 187 case GPROF_GMONPARAM: 188 return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp)); 189 default: 190 return (ENOTSUP); 191 } 192 /* NOTREACHED */ 193} 194 195 196/* 197 * mcount() called with interrupts disabled. 198 */ 199void 200mcount( 201 u_long frompc, 202 u_long selfpc 203) 204{ 205 unsigned short *frompcindex; 206 struct tostruct *top, *prevtop; 207 struct gmonparam *p = &_gmonparam; 208 long toindex; 209 210 /* 211 * check that we are profiling 212 * and that we aren't recursively invoked. 213 */ 214 if (p->state != GMON_PROF_ON) 215 return; 216 217 lck_spin_lock(mcount_lock); 218 219 /* 220 * check that frompcindex is a reasonable pc value. 221 * for example: signal catchers get called from the stack, 222 * not from text space. too bad. 223 */ 224 frompc -= p->lowpc; 225 if (frompc > p->textsize) 226 goto done; 227 228 frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))]; 229 toindex = *frompcindex; 230 if (toindex == 0) { 231 /* 232 * first time traversing this arc 233 */ 234 toindex = ++p->tos[0].link; 235 if (toindex >= p->tolimit) { 236 /* halt further profiling */ 237 goto overflow; 238 } 239 *frompcindex = toindex; 240 top = &p->tos[toindex]; 241 top->selfpc = selfpc; 242 top->count = 1; 243 top->link = 0; 244 goto done; 245 } 246 top = &p->tos[toindex]; 247 if (top->selfpc == selfpc) { 248 /* 249 * arc at front of chain; usual case. 250 */ 251 top->count++; 252 goto done; 253 } 254 /* 255 * have to go looking down chain for it. 256 * top points to what we are looking at, 257 * prevtop points to previous top. 258 * we know it is not at the head of the chain. 259 */ 260 for (; /* goto done */; ) { 261 if (top->link == 0) { 262 /* 263 * top is end of the chain and none of the chain 264 * had top->selfpc == selfpc. 265 * so we allocate a new tostruct 266 * and link it to the head of the chain. 267 */ 268 toindex = ++p->tos[0].link; 269 if (toindex >= p->tolimit) { 270 goto overflow; 271 } 272 top = &p->tos[toindex]; 273 top->selfpc = selfpc; 274 top->count = 1; 275 top->link = *frompcindex; 276 *frompcindex = toindex; 277 goto done; 278 } 279 /* 280 * otherwise, check the next arc on the chain. 281 */ 282 prevtop = top; 283 top = &p->tos[top->link]; 284 if (top->selfpc == selfpc) { 285 /* 286 * there it is. 287 * increment its count 288 * move it to the head of the chain. 289 */ 290 top->count++; 291 toindex = prevtop->link; 292 prevtop->link = top->link; 293 top->link = *frompcindex; 294 *frompcindex = toindex; 295 goto done; 296 } 297 298 } 299done: 300 lck_spin_unlock(mcount_lock); 301 return; 302 303overflow: 304 p->state = GMON_PROF_ERROR; 305 lck_spin_unlock(mcount_lock); 306 printf("mcount: tos overflow\n"); 307 return; 308} 309 310#endif /* GPROF */ 311 312#define PROFILE_LOCK(x) 313#define PROFILE_UNLOCK(x) 314 315static int profil_funneled(struct proc *p, struct profil_args *uap, register_t *retval); 316static int add_profil_funneled(struct proc *p, struct add_profil_args *uap, register_t *retval); 317 318 319int 320profil(struct proc *p, struct profil_args *uap, register_t *retval) 321{ 322 boolean_t funnel_state; 323 int error; 324 325 funnel_state = thread_funnel_set(kernel_flock, TRUE); 326 error = profil_funneled(p, uap, retval); 327 thread_funnel_set(kernel_flock, funnel_state); 328 return(error); 329} 330 331static int 332profil_funneled(struct proc *p, struct profil_args *uap, __unused register_t *retval) 333{ 334 struct uprof *upp = &p->p_stats->p_prof; 335 int s; 336 337 if (uap->pcscale > (1 << 16)) 338 return (EINVAL); 339 340 if (uap->pcscale == 0) { 341 stopprofclock(p); 342 return (0); 343 } 344 /* 345 * Block profile interrupts while changing state. 346 */ 347 s = ml_set_interrupts_enabled(FALSE); 348 349 if (proc_is64bit(p)) { 350 struct user_uprof *user_upp = &p->p_stats->user_p_prof; 351 struct user_uprof *upc, *nupc; 352 353 PROFILE_LOCK(&user_upp->pr_lock); 354 355 user_upp->pr_base = uap->bufbase; 356 user_upp->pr_size = uap->bufsize; 357 user_upp->pr_off = uap->pcoffset; 358 user_upp->pr_scale = uap->pcscale; 359 upp->pr_base = NULL; 360 upp->pr_size = 0; 361 upp->pr_scale = 0; 362 363 /* 364 * remove buffers previously allocated with add_profil() 365 * don't do the kfree's while interrupts disabled 366 */ 367 upc = user_upp->pr_next; 368 user_upp->pr_next = 0; 369 370 PROFILE_UNLOCK(&user_upp->pr_lock); 371 372 startprofclock(p); 373 ml_set_interrupts_enabled(s); 374 375 while (upc) { 376 nupc = upc->pr_next; 377 kfree(upc, sizeof (*upc)); 378 upc = nupc; 379 } 380 381 } else { 382 struct uprof *upc, *nupc; 383 384 PROFILE_LOCK(&upp->pr_lock); 385 386 upp->pr_base = CAST_DOWN(caddr_t, uap->bufbase); 387 upp->pr_size = uap->bufsize; 388 upp->pr_off = uap->pcoffset; 389 upp->pr_scale = uap->pcscale; 390 391 /* 392 * remove buffers previously allocated with add_profil() 393 * don't do the kfree's while interrupts disabled 394 */ 395 upc = upp->pr_next; 396 upp->pr_next = 0; 397 398 PROFILE_UNLOCK(&upp->pr_lock); 399 400 startprofclock(p); 401 ml_set_interrupts_enabled(s); 402 403 while (upc) { 404 nupc = upc->pr_next; 405 kfree(upc, sizeof (struct uprof)); 406 upc = nupc; 407 } 408 } 409 return(0); 410} 411 412int 413add_profil(struct proc *p, struct add_profil_args *uap, register_t *retval) 414{ 415 boolean_t funnel_state; 416 int error; 417 418 funnel_state = thread_funnel_set(kernel_flock, TRUE); 419 error = add_profil_funneled(p, uap, retval); 420 thread_funnel_set(kernel_flock, funnel_state); 421 return(error); 422} 423 424 425static int 426add_profil_funneled(struct proc *p, struct add_profil_args *uap, __unused register_t *retval) 427{ 428 struct uprof *upp = &p->p_stats->p_prof, *upc; 429 struct user_uprof *user_upp = NULL, *user_upc; 430 int s; 431 boolean_t is64bit = proc_is64bit(p); 432 433 434 upc = NULL; 435 user_upc = NULL; 436 437 if (is64bit) { 438 user_upp = &p->p_stats->user_p_prof; 439 440 if (user_upp->pr_scale == 0) 441 return (0); 442 } 443 else { 444 if (upp->pr_scale == 0) 445 return (0); 446 } 447 if (is64bit) { 448 user_upc = (struct user_uprof *) kalloc(sizeof (struct user_uprof)); 449 user_upc->pr_base = uap->bufbase; 450 user_upc->pr_size = uap->bufsize; 451 user_upc->pr_off = uap->pcoffset; 452 user_upc->pr_scale = uap->pcscale; 453 } else { 454 upc = (struct uprof *) kalloc(sizeof (struct uprof)); 455 upc->pr_base = CAST_DOWN(caddr_t, uap->bufbase); 456 upc->pr_size = uap->bufsize; 457 upc->pr_off = uap->pcoffset; 458 upc->pr_scale = uap->pcscale; 459 } 460 s = ml_set_interrupts_enabled(FALSE); 461 462 if (is64bit) { 463 PROFILE_LOCK(&user_upp->pr_lock); 464 if (user_upp->pr_scale) { 465 user_upc->pr_next = user_upp->pr_next; 466 user_upp->pr_next = user_upc; 467 user_upc = NULL; 468 } 469 PROFILE_UNLOCK(&user_upp->pr_lock); 470 } else { 471 PROFILE_LOCK(&upp->pr_lock); 472 if (upp->pr_scale) { 473 upc->pr_next = upp->pr_next; 474 upp->pr_next = upc; 475 upc = NULL; 476 } 477 PROFILE_UNLOCK(&upp->pr_lock); 478 } 479 ml_set_interrupts_enabled(s); 480 481 if (upc) 482 kfree(upc, sizeof(struct uprof)); 483 if (user_upc) 484 kfree(user_upc, sizeof(struct user_uprof)); 485 486 return(0); 487} 488 489/* 490 * Scale is a fixed-point number with the binary point 16 bits 491 * into the value, and is <= 1.0. pc is at most 32 bits, so the 492 * intermediate result is at most 48 bits. 493 */ 494#define PC_TO_INDEX(pc, prof) \ 495 ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ 496 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) 497 498/* 499 * Collect user-level profiling statistics; called on a profiling tick, 500 * when a process is running in user-mode. We use 501 * an AST that will vector us to trap() with a context in which copyin 502 * and copyout will work. Trap will then call addupc_task(). 503 * 504 * Note that we may (rarely) not get around to the AST soon enough, and 505 * lose profile ticks when the next tick overwrites this one, but in this 506 * case the system is overloaded and the profile is probably already 507 * inaccurate. 508 * 509 * We can afford to take faults here. If the 510 * update fails, we simply turn off profiling. 511 */ 512void 513addupc_task(struct proc *p, user_addr_t pc, u_int ticks) 514{ 515 u_int off; 516 u_short count; 517 518 /* Testing P_PROFIL may be unnecessary, but is certainly safe. */ 519 if ((p->p_flag & P_PROFIL) == 0 || ticks == 0) 520 return; 521 522 if (proc_is64bit(p)) { 523 struct user_uprof *prof; 524 user_addr_t cell; 525 526 for (prof = &p->p_stats->user_p_prof; prof; prof = prof->pr_next) { 527 off = PC_TO_INDEX(pc, prof); 528 cell = (prof->pr_base + off); 529 if (cell >= prof->pr_base && 530 cell < (prof->pr_size + prof->pr_base)) { 531 if (copyin(cell, (caddr_t) &count, sizeof(count)) == 0) { 532 count += ticks; 533 if(copyout((caddr_t) &count, cell, sizeof(count)) == 0) 534 return; 535 } 536 p->p_stats->user_p_prof.pr_scale = 0; 537 stopprofclock(p); 538 break; 539 } 540 } 541 } 542 else { 543 struct uprof *prof; 544 short *cell; 545 546 for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) { 547 off = PC_TO_INDEX(CAST_DOWN(uint, pc),prof); 548 cell = (short *)(prof->pr_base + off); 549 if (cell >= (short *)prof->pr_base && 550 cell < (short*)(prof->pr_size + (int) prof->pr_base)) { 551 if (copyin(CAST_USER_ADDR_T(cell), (caddr_t) &count, sizeof(count)) == 0) { 552 count += ticks; 553 if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0) 554 return; 555 } 556 p->p_stats->p_prof.pr_scale = 0; 557 stopprofclock(p); 558 break; 559 } 560 } 561 } 562} 563