darwin-xnu/bsd/kern/subr_prof.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
   3 *
   4 * @APPLE_LICENSE_HEADER_START@
   5 * 
   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.
  11 * 
  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
  18 * under the License.
  19 * 
  20 * @APPLE_LICENSE_HEADER_END@
  21 */
  22/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
  23/*-
  24 * Copyright (c) 1982, 1986, 1993
  25 *      The Regents of the University of California.  All rights reserved.
  26 *
  27 * Redistribution and use in source and binary forms, with or without
  28 * modification, are permitted provided that the following conditions
  29 * are met:
  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.
  42 *
  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
  53 * SUCH DAMAGE.
  54 *
  55 *      @(#)subr_prof.c 8.3 (Berkeley) 9/23/93
  56 */
  57
  58#include <sys/param.h>
  59#include <sys/systm.h>
  60#include <sys/kernel.h>
  61#include <sys/proc_internal.h>
  62#include <sys/user.h>
  63#include <machine/spl.h>
  64#include <machine/machine_routines.h>
  65
  66#include <sys/mount_internal.h>
  67#include <sys/sysproto.h>
  68
  69#include <mach/mach_types.h>
  70#include <kern/kern_types.h>
  71#include <kern/cpu_number.h>
  72#include <kern/kalloc.h>
  73
  74extern boolean_t ml_set_interrupts_enabled(boolean_t enable);
  75
  76#ifdef GPROF
  77#include <sys/malloc.h>
  78#include <sys/gmon.h>
  79#include <kern/mach_header.h>
  80#include <machine/profile.h>
  81
  82lck_spin_t * mcount_lock;
  83lck_grp_t * mcount_lock_grp;
  84lck_attr_t * mcount_lock_attr;
  85
  86/*
  87 * Froms is actually a bunch of unsigned shorts indexing tos
  88 */
  89struct gmonparam _gmonparam = { GMON_PROF_OFF };
  90
  91/*
  92 * This code uses 32 bit mach object segment information from the currently
  93 * running kernel.
  94 */
  95void
  96kmstartup(void)
  97{
  98        char *cp;
  99        u_long  fromssize, tossize;
 100        struct segment_command  *sgp;   /* 32 bit mach object file segment */
 101        struct gmonparam *p = &_gmonparam;
 102        
 103        sgp = getsegbyname("__TEXT");
 104        p->lowpc = (u_long)sgp->vmaddr;
 105        p->highpc = (u_long)(sgp->vmaddr + sgp->vmsize);
 106        
 107        /*
 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.
 110         */
 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);
 127        if (cp == 0) {
 128                printf("No memory for profiling.\n");
 129                return;
 130        }
 131        bzero(cp, p->kcountsize + p->tossize + p->fromssize);
 132        p->tos = (struct tostruct *)cp;
 133        cp += p->tossize;
 134        p->kcount = (u_short *)cp;
 135        cp += p->kcountsize;
 136        p->froms = (u_short *)cp;
 137        
 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);
 142
 143}
 144
 145/*
 146 * Return kernel profiling information.
 147 */
 148int
 149sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, 
 150              user_addr_t newp, size_t newlen)
 151{
 152        struct gmonparam *gp = &_gmonparam;
 153        int error;
 154
 155        /* all sysctl names at this level are terminal */
 156        if (namelen != 1)
 157                return (ENOTDIR);               /* overloaded */
 158
 159        switch (name[0]) {
 160        case GPROF_STATE:
 161                error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
 162                if (error)
 163                        return (error);
 164                if (gp->state == GMON_PROF_OFF)
 165                        stopprofclock(kernproc);
 166                else
 167                        startprofclock(kernproc);
 168                return (0);
 169        case GPROF_COUNT:
 170                return (sysctl_struct(oldp, oldlenp, newp, newlen, 
 171                                      gp->kcount, gp->kcountsize));
 172        case GPROF_FROMS:
 173                return (sysctl_struct(oldp, oldlenp, newp, newlen,
 174                                      gp->froms, gp->fromssize));
 175        case GPROF_TOS:
 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));
 180        default:
 181                return (ENOTSUP);
 182        }
 183        /* NOTREACHED */
 184}
 185
 186
 187/*
 188 * mcount() called with interrupts disabled.
 189 */
 190void
 191mcount(
 192    register u_long frompc,
 193    register u_long selfpc
 194)
 195{
 196    unsigned short *frompcindex;
 197        register struct tostruct *top, *prevtop;
 198        struct gmonparam *p = &_gmonparam;
 199        register long toindex;
 200
 201    /*
 202     * check that we are profiling
 203     * and that we aren't recursively invoked.
 204     */
 205    if (p->state != GMON_PROF_ON)
 206        return;
 207
 208        lck_spin_lock(mcount_lock);
 209
 210        /*
 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.
 214         */
 215        frompc -= p->lowpc;
 216        if (frompc > p->textsize)
 217                goto done;
 218
 219        frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
 220        toindex = *frompcindex;
 221        if (toindex == 0) {
 222                /*
 223                 *      first time traversing this arc
 224                 */
 225                toindex = ++p->tos[0].link;
 226                if (toindex >= p->tolimit) {
 227            /* halt further profiling */
 228                        goto overflow;
 229                }
 230                *frompcindex = toindex;
 231                top = &p->tos[toindex];
 232                top->selfpc = selfpc;
 233                top->count = 1;
 234                top->link = 0;
 235                goto done;
 236        }
 237        top = &p->tos[toindex];
 238        if (top->selfpc == selfpc) {
 239                /*
 240                 *      arc at front of chain; usual case.
 241                 */
 242                top->count++;
 243                goto done;
 244        }
 245        /*
 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.
 250         */
 251        for (; /* goto done */; ) {
 252                if (top->link == 0) {
 253                        /*
 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.
 258                         */
 259                        toindex = ++p->tos[0].link;
 260                        if (toindex >= p->tolimit) {
 261                                goto overflow;
 262                        }
 263                        top = &p->tos[toindex];
 264                        top->selfpc = selfpc;
 265                        top->count = 1;
 266                        top->link = *frompcindex;
 267                        *frompcindex = toindex;
 268                        goto done;
 269                }
 270                /*
 271                 *      otherwise, check the next arc on the chain.
 272                 */
 273                prevtop = top;
 274                top = &p->tos[top->link];
 275                if (top->selfpc == selfpc) {
 276                        /*
 277                         *      there it is.
 278                         *      increment its count
 279                         *      move it to the head of the chain.
 280                         */
 281                        top->count++;
 282                        toindex = prevtop->link;
 283                        prevtop->link = top->link;
 284                        top->link = *frompcindex;
 285                        *frompcindex = toindex;
 286                        goto done;
 287                }
 288
 289        }
 290done:
 291        lck_spin_unlock(mcount_lock);
 292        return;
 293
 294overflow:
 295    p->state = GMON_PROF_ERROR;
 296        lck_spin_unlock(mcount_lock);
 297        printf("mcount: tos overflow\n");
 298        return;
 299}
 300
 301#endif /* GPROF */
 302
 303#define PROFILE_LOCK(x)
 304#define PROFILE_UNLOCK(x)
 305
 306int
 307profil(struct proc *p, register struct profil_args *uap, __unused register_t *retval)
 308{
 309    struct uprof *upp = &p->p_stats->p_prof;
 310    int s;
 311
 312        if (uap->pcscale > (1 << 16))
 313                return (EINVAL);
 314        if (uap->pcscale == 0) {
 315                stopprofclock(p);
 316                return (0);
 317        }
 318
 319        /* Block profile interrupts while changing state. */
 320    s = ml_set_interrupts_enabled(FALSE);       
 321
 322        if (proc_is64bit(p)) {
 323        struct user_uprof *user_upp = &p->p_stats->user_p_prof;
 324        struct user_uprof *upc, *nupc;
 325        
 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;
 331        upp->pr_base = NULL;
 332        upp->pr_size = 0;
 333        upp->pr_scale = 0;
 334
 335        /* remove buffers previously allocated with add_profil() */
 336        for (upc = user_upp->pr_next; upc; upc = nupc) {
 337            nupc = upc->pr_next;
 338            kfree(upc, sizeof (*upc));
 339        }
 340        user_upp->pr_next = 0;
 341            PROFILE_UNLOCK(&user_upp->pr_lock);
 342        }
 343        else {
 344        struct uprof *upc, *nupc;
 345            
 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;
 351
 352        /* remove buffers previously allocated with add_profil() */
 353        for (upc = upp->pr_next; upc; upc = nupc) {
 354            nupc = upc->pr_next;
 355            kfree(upc, sizeof (struct uprof));
 356        }
 357        upp->pr_next = 0;
 358            PROFILE_UNLOCK(&upp->pr_lock);
 359        }
 360
 361        startprofclock(p);
 362        ml_set_interrupts_enabled(s);
 363        return(0);
 364}
 365
 366int
 367add_profil(struct proc *p, register struct add_profil_args *uap, __unused register_t *retval)
 368{
 369        struct uprof *upp = &p->p_stats->p_prof, *upc;
 370        struct user_uprof *user_upp = NULL, *user_upc;
 371        int s;
 372        boolean_t is64bit = proc_is64bit(p);
 373
 374        if (is64bit) {
 375       user_upp = &p->p_stats->user_p_prof;
 376       if (user_upp->pr_scale == 0)
 377            return (0);
 378    }
 379    else {
 380        if (upp->pr_scale == 0)
 381            return (0);
 382    }
 383
 384    s = ml_set_interrupts_enabled(FALSE);       
 385    
 386        if (is64bit) {
 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);
 396    }
 397    else {
 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;
 405        upp->pr_next = upc;
 406        PROFILE_UNLOCK(&upp->pr_lock);
 407    }
 408    
 409        ml_set_interrupts_enabled(s);           
 410        return(0);
 411}
 412
 413/*
 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.
 417 */
 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)
 421
 422/*
 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().
 427 *
 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
 431 * inaccurate.
 432 *
 433 * We can afford to take faults here.  If the
 434 * update fails, we simply turn off profiling.
 435 */
 436void
 437addupc_task(p, pc, ticks)
 438        register struct proc *p;
 439        user_addr_t pc;
 440        u_int ticks;
 441{
 442        register u_int off;
 443        u_short count;
 444
 445        /* Testing P_PROFIL may be unnecessary, but is certainly safe. */
 446        if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
 447                return;
 448
 449        if (proc_is64bit(p)) {
 450        struct user_uprof *prof;
 451        user_addr_t cell;
 452
 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) {
 459                    count += ticks;
 460                    if(copyout((caddr_t) &count, cell, sizeof(count)) == 0)
 461                        return;
 462                }
 463                p->p_stats->user_p_prof.pr_scale = 0;
 464                stopprofclock(p);
 465                break;
 466            }
 467        }
 468        }
 469        else {
 470        struct uprof *prof;
 471        short *cell;
 472
 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) {
 479                    count += ticks;
 480                    if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0)
 481                        return;
 482                }
 483                p->p_stats->p_prof.pr_scale = 0;
 484                stopprofclock(p);
 485                break;
 486            }
 487        }
 488        }
 489}
 490
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.