]>
git.saurik.com Git - apple/xnu.git/blob - bsd/kern/subr_prof.c
   2  * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * The contents of this file constitute Original Code as defined in and 
   7  * are subject to the Apple Public Source License Version 1.1 (the 
   8  * "License").  You may not use this file except in compliance with the 
   9  * License.  Please obtain a copy of the License at 
  10  * http://www.apple.com/publicsource and read it before using this file. 
  12  * This Original Code and all software distributed under the License are 
  13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the 
  17  * License for the specific language governing rights and limitations 
  20  * @APPLE_LICENSE_HEADER_END@ 
  22 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ 
  24  * Copyright (c) 1982, 1986, 1993 
  25  *      The Regents of the University of California.  All rights reserved. 
  27  * Redistribution and use in source and binary forms, with or without 
  28  * modification, are permitted provided that the following conditions 
  30  * 1. Redistributions of source code must retain the above copyright 
  31  *    notice, this list of conditions and the following disclaimer. 
  32  * 2. Redistributions in binary form must reproduce the above copyright 
  33  *    notice, this list of conditions and the following disclaimer in the 
  34  *    documentation and/or other materials provided with the distribution. 
  35  * 3. All advertising materials mentioning features or use of this software 
  36  *    must display the following acknowledgement: 
  37  *      This product includes software developed by the University of 
  38  *      California, Berkeley and its contributors. 
  39  * 4. Neither the name of the University nor the names of its contributors 
  40  *    may be used to endorse or promote products derived from this software 
  41  *    without specific prior written permission. 
  43  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 
  44  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
  45  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
  46  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 
  47  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
  48  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
  49  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
  50  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
  51  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
  52  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
  55  *      @(#)subr_prof.c 8.3 (Berkeley) 9/23/93 
  58 #include <sys/param.h> 
  59 #include <sys/systm.h> 
  60 #include <sys/kernel.h> 
  61 #include <sys/proc_internal.h> 
  63 #include <machine/spl.h> 
  64 #include <machine/machine_routines.h> 
  66 #include <sys/mount_internal.h> 
  67 #include <sys/sysproto.h> 
  69 #include <mach/mach_types.h> 
  70 #include <kern/kern_types.h> 
  71 #include <kern/cpu_number.h> 
  72 #include <kern/kalloc.h> 
  74 extern boolean_t 
ml_set_interrupts_enabled(boolean_t enable
); 
  77 #include <sys/malloc.h> 
  79 #include <kern/mach_header.h> 
  80 #include <machine/profile.h> 
  82 lck_spin_t 
* mcount_lock
; 
  83 lck_grp_t 
* mcount_lock_grp
; 
  84 lck_attr_t 
* mcount_lock_attr
; 
  87  * Froms is actually a bunch of unsigned shorts indexing tos 
  89 struct gmonparam _gmonparam 
= { GMON_PROF_OFF 
}; 
  92  * This code uses 32 bit mach object segment information from the currently 
  99         u_long  fromssize
, tossize
; 
 100         struct segment_command  
*sgp
;   /* 32 bit mach object file segment */ 
 101         struct gmonparam 
*p 
= &_gmonparam
; 
 103         sgp 
= getsegbyname("__TEXT"); 
 104         p
->lowpc 
= (u_long
)sgp
->vmaddr
; 
 105         p
->highpc 
= (u_long
)(sgp
->vmaddr 
+ sgp
->vmsize
); 
 108          * Round lowpc and highpc to multiples of the density we're using 
 109          * so the rest of the scaling (here and in gprof) stays in ints. 
 111         p
->lowpc 
= ROUNDDOWN(p
->lowpc
, HISTFRACTION 
* sizeof(HISTCOUNTER
)); 
 112         p
->highpc 
= ROUNDUP(p
->highpc
, HISTFRACTION 
* sizeof(HISTCOUNTER
)); 
 113         p
->textsize 
= p
->highpc 
- p
->lowpc
; 
 114         printf("Profiling kernel, textsize=%d [0x%08x..0x%08x]\n", 
 115                p
->textsize
, p
->lowpc
, p
->highpc
); 
 116         p
->kcountsize 
= p
->textsize 
/ HISTFRACTION
; 
 117         p
->hashfraction 
= HASHFRACTION
; 
 118         p
->fromssize 
= p
->textsize 
/ HASHFRACTION
; 
 119         p
->tolimit 
= p
->textsize 
* ARCDENSITY 
/ 100; 
 120         if (p
->tolimit 
< MINARCS
) 
 121                 p
->tolimit 
= MINARCS
; 
 122         else if (p
->tolimit 
> MAXARCS
) 
 123                 p
->tolimit 
= MAXARCS
; 
 124         p
->tossize 
= p
->tolimit 
* sizeof(struct tostruct
); 
 125         /* Why not use MALLOC with M_GPROF ? */ 
 126         cp 
= (char *)kalloc(p
->kcountsize 
+ p
->fromssize 
+ p
->tossize
); 
 128                 printf("No memory for profiling.\n"); 
 131         bzero(cp
, p
->kcountsize 
+ p
->tossize 
+ p
->fromssize
); 
 132         p
->tos 
= (struct tostruct 
*)cp
; 
 134         p
->kcount 
= (u_short 
*)cp
; 
 136         p
->froms 
= (u_short 
*)cp
; 
 138         mcount_lock_grp 
= lck_grp_alloc_init("MCOUNT", LCK_GRP_ATTR_NULL
); 
 139         mcount_lock_attr 
= lck_attr_alloc_init(); 
 140         //lck_attr_setdebug(mcount_lock_attr); 
 141         mcount_lock 
= lck_spin_alloc_init(mcount_lock_grp
, mcount_lock_attr
); 
 146  * Return kernel profiling information. 
 149 sysctl_doprof(int *name
, u_int namelen
, user_addr_t oldp
, size_t *oldlenp
,  
 150               user_addr_t newp
, size_t newlen
) 
 152         struct gmonparam 
*gp 
= &_gmonparam
; 
 155         /* all sysctl names at this level are terminal */ 
 157                 return (ENOTDIR
);               /* overloaded */ 
 161                 error 
= sysctl_int(oldp
, oldlenp
, newp
, newlen
, &gp
->state
); 
 164                 if (gp
->state 
== GMON_PROF_OFF
) 
 165                         stopprofclock(kernproc
); 
 167                         startprofclock(kernproc
); 
 170                 return (sysctl_struct(oldp
, oldlenp
, newp
, newlen
,  
 171                                       gp
->kcount
, gp
->kcountsize
)); 
 173                 return (sysctl_struct(oldp
, oldlenp
, newp
, newlen
, 
 174                                       gp
->froms
, gp
->fromssize
)); 
 176                 return (sysctl_struct(oldp
, oldlenp
, newp
, newlen
, 
 177                                       gp
->tos
, gp
->tossize
)); 
 178         case GPROF_GMONPARAM
: 
 179                 return (sysctl_rdstruct(oldp
, oldlenp
, newp
, gp
, sizeof *gp
)); 
 188  * mcount() called with interrupts disabled. 
 192     register u_long frompc
, 
 193     register u_long selfpc
 
 196     unsigned short *frompcindex
; 
 197         register struct tostruct 
*top
, *prevtop
; 
 198         struct gmonparam 
*p 
= &_gmonparam
; 
 199         register long toindex
; 
 202      * check that we are profiling 
 203      * and that we aren't recursively invoked. 
 205     if (p
->state 
!= GMON_PROF_ON
) 
 208         lck_spin_lock(mcount_lock
); 
 211          *      check that frompcindex is a reasonable pc value. 
 212          *      for example:    signal catchers get called from the stack, 
 213          *                      not from text space.  too bad. 
 216         if (frompc 
> p
->textsize
) 
 219         frompcindex 
= &p
->froms
[frompc 
/ (p
->hashfraction 
* sizeof(*p
->froms
))]; 
 220         toindex 
= *frompcindex
; 
 223                  *      first time traversing this arc 
 225                 toindex 
= ++p
->tos
[0].link
; 
 226                 if (toindex 
>= p
->tolimit
) { 
 227             /* halt further profiling */ 
 230                 *frompcindex 
= toindex
; 
 231                 top 
= &p
->tos
[toindex
]; 
 232                 top
->selfpc 
= selfpc
; 
 237         top 
= &p
->tos
[toindex
]; 
 238         if (top
->selfpc 
== selfpc
) { 
 240                  *      arc at front of chain; usual case. 
 246          *      have to go looking down chain for it. 
 247          *      top points to what we are looking at, 
 248          *      prevtop points to previous top. 
 249          *      we know it is not at the head of the chain. 
 251         for (; /* goto done */; ) { 
 252                 if (top
->link 
== 0) { 
 254                          *      top is end of the chain and none of the chain 
 255                          *      had top->selfpc == selfpc. 
 256                          *      so we allocate a new tostruct 
 257                          *      and link it to the head of the chain. 
 259                         toindex 
= ++p
->tos
[0].link
; 
 260                         if (toindex 
>= p
->tolimit
) { 
 263                         top 
= &p
->tos
[toindex
]; 
 264                         top
->selfpc 
= selfpc
; 
 266                         top
->link 
= *frompcindex
; 
 267                         *frompcindex 
= toindex
; 
 271                  *      otherwise, check the next arc on the chain. 
 274                 top 
= &p
->tos
[top
->link
]; 
 275                 if (top
->selfpc 
== selfpc
) { 
 278                          *      increment its count 
 279                          *      move it to the head of the chain. 
 282                         toindex 
= prevtop
->link
; 
 283                         prevtop
->link 
= top
->link
; 
 284                         top
->link 
= *frompcindex
; 
 285                         *frompcindex 
= toindex
; 
 291         lck_spin_unlock(mcount_lock
); 
 295     p
->state 
= GMON_PROF_ERROR
; 
 296         lck_spin_unlock(mcount_lock
); 
 297         printf("mcount: tos overflow\n"); 
 303 #define PROFILE_LOCK(x) 
 304 #define PROFILE_UNLOCK(x) 
 307 profil(struct proc 
*p
, register struct profil_args 
*uap
, __unused register_t 
*retval
) 
 309     struct uprof 
*upp 
= &p
->p_stats
->p_prof
; 
 312         if (uap
->pcscale 
> (1 << 16)) 
 314         if (uap
->pcscale 
== 0) { 
 319         /* Block profile interrupts while changing state. */ 
 320     s 
= ml_set_interrupts_enabled(FALSE
);        
 322         if (proc_is64bit(p
)) { 
 323         struct user_uprof 
*user_upp 
= &p
->p_stats
->user_p_prof
; 
 324         struct user_uprof 
*upc
, *nupc
; 
 326             PROFILE_LOCK(&user_upp
->pr_lock
); 
 327         user_upp
->pr_base 
= uap
->bufbase
; 
 328         user_upp
->pr_size 
= uap
->bufsize
; 
 329         user_upp
->pr_off 
= uap
->pcoffset
; 
 330             user_upp
->pr_scale 
= uap
->pcscale
; 
 335         /* remove buffers previously allocated with add_profil() */ 
 336         for (upc 
= user_upp
->pr_next
; upc
; upc 
= nupc
) { 
 338             kfree(upc
, sizeof (*upc
)); 
 340         user_upp
->pr_next 
= 0; 
 341             PROFILE_UNLOCK(&user_upp
->pr_lock
); 
 344         struct uprof 
*upc
, *nupc
; 
 346             PROFILE_LOCK(&upp
->pr_lock
); 
 347         upp
->pr_base 
= CAST_DOWN(caddr_t
, uap
->bufbase
); 
 348         upp
->pr_size 
= uap
->bufsize
; 
 349         upp
->pr_off 
= uap
->pcoffset
; 
 350             upp
->pr_scale 
= uap
->pcscale
; 
 352         /* remove buffers previously allocated with add_profil() */ 
 353         for (upc 
= upp
->pr_next
; upc
; upc 
= nupc
) { 
 355             kfree(upc
, sizeof (struct uprof
)); 
 358             PROFILE_UNLOCK(&upp
->pr_lock
); 
 362         ml_set_interrupts_enabled(s
); 
 367 add_profil(struct proc 
*p
, register struct add_profil_args 
*uap
, __unused register_t 
*retval
) 
 369         struct uprof 
*upp 
= &p
->p_stats
->p_prof
, *upc
; 
 370         struct user_uprof 
*user_upp 
= NULL
, *user_upc
; 
 372         boolean_t is64bit 
= proc_is64bit(p
); 
 375        user_upp 
= &p
->p_stats
->user_p_prof
; 
 376        if (user_upp
->pr_scale 
== 0) 
 380         if (upp
->pr_scale 
== 0) 
 384     s 
= ml_set_interrupts_enabled(FALSE
);        
 387         user_upc 
= (struct user_uprof 
*) kalloc(sizeof (struct user_uprof
)); 
 388         user_upc
->pr_base 
= uap
->bufbase
; 
 389         user_upc
->pr_size 
= uap
->bufsize
; 
 390         user_upc
->pr_off 
= uap
->pcoffset
; 
 391         user_upc
->pr_scale 
= uap
->pcscale
; 
 392         PROFILE_LOCK(&user_upp
->pr_lock
); 
 393         user_upc
->pr_next 
= user_upp
->pr_next
; 
 394         user_upp
->pr_next 
= user_upc
; 
 395         PROFILE_UNLOCK(&user_upp
->pr_lock
); 
 398         upc 
= (struct uprof 
*) kalloc(sizeof (struct uprof
)); 
 399         upc
->pr_base 
= CAST_DOWN(caddr_t
, uap
->bufbase
); 
 400         upc
->pr_size 
= uap
->bufsize
; 
 401         upc
->pr_off 
= uap
->pcoffset
; 
 402         upc
->pr_scale 
= uap
->pcscale
; 
 403         PROFILE_LOCK(&upp
->pr_lock
); 
 404         upc
->pr_next 
= upp
->pr_next
; 
 406         PROFILE_UNLOCK(&upp
->pr_lock
); 
 409         ml_set_interrupts_enabled(s
);            
 414  * Scale is a fixed-point number with the binary point 16 bits 
 415  * into the value, and is <= 1.0.  pc is at most 32 bits, so the 
 416  * intermediate result is at most 48 bits. 
 418 #define PC_TO_INDEX(pc, prof) \ 
 419         ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ 
 420                         (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) 
 423  * Collect user-level profiling statistics; called on a profiling tick, 
 424  * when a process is running in user-mode. We use 
 425  * an AST that will vector us to trap() with a context in which copyin 
 426  * and copyout will work.  Trap will then call addupc_task(). 
 428  * Note that we may (rarely) not get around to the AST soon enough, and 
 429  * lose profile ticks when the next tick overwrites this one, but in this 
 430  * case the system is overloaded and the profile is probably already 
 433  * We can afford to take faults here.  If the 
 434  * update fails, we simply turn off profiling. 
 437 addupc_task(p
, pc
, ticks
) 
 438         register struct proc 
*p
; 
 445         /* Testing P_PROFIL may be unnecessary, but is certainly safe. */ 
 446         if ((p
->p_flag 
& P_PROFIL
) == 0 || ticks 
== 0) 
 449         if (proc_is64bit(p
)) { 
 450         struct user_uprof 
*prof
; 
 453         for (prof 
= &p
->p_stats
->user_p_prof
; prof
; prof 
= prof
->pr_next
) { 
 454             off 
= PC_TO_INDEX(pc
, prof
); 
 455             cell 
= (prof
->pr_base 
+ off
); 
 456             if (cell 
>= prof
->pr_base 
&& 
 457                 cell 
< (prof
->pr_size 
+ prof
->pr_base
)) { 
 458                 if (copyin(cell
, (caddr_t
) &count
, sizeof(count
)) == 0) { 
 460                     if(copyout((caddr_t
) &count
, cell
, sizeof(count
)) == 0) 
 463                 p
->p_stats
->user_p_prof
.pr_scale 
= 0; 
 473         for (prof 
= &p
->p_stats
->p_prof
; prof
; prof 
= prof
->pr_next
) { 
 474             off 
= PC_TO_INDEX(CAST_DOWN(uint
, pc
),prof
); 
 475             cell 
= (short *)(prof
->pr_base 
+ off
); 
 476             if (cell 
>= (short *)prof
->pr_base 
&& 
 477                 cell 
< (short*)(prof
->pr_size 
+ (int) prof
->pr_base
)) { 
 478                 if (copyin(CAST_USER_ADDR_T(cell
), (caddr_t
) &count
, sizeof(count
)) == 0) { 
 480                     if(copyout((caddr_t
) &count
, CAST_USER_ADDR_T(cell
), sizeof(count
)) == 0) 
 483                 p
->p_stats
->p_prof
.pr_scale 
= 0;