X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/de355530ae67247cbd0da700edb3a2a1dae884c2..8a3053a07cee346dca737a5670e546fd26a7c9d6:/bsd/kern/subr_prof.c diff --git a/bsd/kern/subr_prof.c b/bsd/kern/subr_prof.c index 2fcd135c7..80b6edc27 100644 --- a/bsd/kern/subr_prof.c +++ b/bsd/kern/subr_prof.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2008 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ /*- @@ -55,38 +61,58 @@ * @(#)subr_prof.c 8.3 (Berkeley) 9/23/93 */ +#ifdef GPROF +#include +#endif + #include #include #include -#include +#include #include #include +#include -#include +#include +#include +#include +#include #include +#include #ifdef GPROF #include #include -#include -#include + +extern int sysctl_doprof(int *, u_int, user_addr_t, size_t *, + user_addr_t, size_t newlen); +extern int sysctl_struct(user_addr_t, size_t *, + user_addr_t, size_t, void *, int); + +lck_spin_t * mcount_lock; +lck_grp_t * mcount_lock_grp; +lck_attr_t * mcount_lock_attr; /* * Froms is actually a bunch of unsigned shorts indexing tos */ -struct gmonparam _gmonparam = { GMON_PROF_OFF }; +struct gmonparam _gmonparam = { .state = GMON_PROF_OFF }; -kmstartup() +/* + * This code uses 32 bit mach object segment information from the currently + * running kernel. + */ +void +kmstartup(void) { - char *cp; - u_long fromssize, tossize; - struct segment_command *sgp; + tostruct_t *cp; + kernel_segment_command_t *sgp; /* 32 bit mach object file segment */ struct gmonparam *p = &_gmonparam; sgp = getsegbyname("__TEXT"); - p->lowpc = (u_long)sgp->vmaddr; - p->highpc = (u_long)(sgp->vmaddr + sgp->vmsize); + p->lowpc = (u_int32_t)sgp->vmaddr; + p->highpc = (u_int32_t)(sgp->vmaddr + sgp->vmsize); /* * Round lowpc and highpc to multiples of the density we're using @@ -95,7 +121,7 @@ kmstartup() p->lowpc = ROUNDDOWN(p->lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); p->highpc = ROUNDUP(p->highpc, HISTFRACTION * sizeof(HISTCOUNTER)); p->textsize = p->highpc - p->lowpc; - printf("Profiling kernel, textsize=%d [0x%08x..0x%08x]\n", + printf("Profiling kernel, textsize=%lu [0x%016lx..0x%016lx]\n", p->textsize, p->lowpc, p->highpc); p->kcountsize = p->textsize / HISTFRACTION; p->hashfraction = HASHFRACTION; @@ -105,35 +131,49 @@ kmstartup() p->tolimit = MINARCS; else if (p->tolimit > MAXARCS) p->tolimit = MAXARCS; - p->tossize = p->tolimit * sizeof(struct tostruct); + p->tossize = p->tolimit * sizeof(tostruct_t); /* Why not use MALLOC with M_GPROF ? */ - cp = (char *)kalloc(p->kcountsize + p->fromssize + p->tossize); + cp = (tostruct_t *)kalloc(p->kcountsize + p->fromssize + p->tossize); if (cp == 0) { printf("No memory for profiling.\n"); return; } bzero(cp, p->kcountsize + p->tossize + p->fromssize); - p->tos = (struct tostruct *)cp; - cp += p->tossize; + p->tos = cp; + cp = (tostruct_t *)((vm_offset_t)cp + p->tossize); p->kcount = (u_short *)cp; - cp += p->kcountsize; + cp = (tostruct_t *)((vm_offset_t)cp + p->kcountsize); p->froms = (u_short *)cp; + + mcount_lock_grp = lck_grp_alloc_init("MCOUNT", LCK_GRP_ATTR_NULL); + mcount_lock_attr = lck_attr_alloc_init(); + mcount_lock = lck_spin_alloc_init(mcount_lock_grp, mcount_lock_attr); + } /* - * Return kernel profiling information. + * XXX These should be broken out into per-argument OID values, + * XXX since there are no sub-OID parameter values, but unfortunately + * XXX there is barely enough time for an initial conversion. + * + * Note: These items appear to be read/write. */ -int -sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen) - int *name; - u_int namelen; - void *oldp; - size_t *oldlenp; - void *newp; - size_t newlen; +STATIC int +sysctl_doprofhandle SYSCTL_HANDLER_ARGS +{ +sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, + user_addr_t newp, size_t newlen) { + __unused int cmd = oidp->oid_arg2; /* subcommand*/ + int *name = arg1; /* oid element argument vector */ + int namelen = arg2; /* number of oid element arguments */ + user_addr_t oldp = req->oldptr; /* user buffer copy out address */ + size_t *oldlenp = req->oldlen; /* user buffer copy out size */ + user_addr_t newp = req->newptr; /* user buffer copy in address */ + size_t newlen = req->newlen; /* user buffer copy in size */ + struct gmonparam *gp = &_gmonparam; - int error; + int error = 0; /* all sysctl names at this level are terminal */ if (namelen != 1) @@ -143,28 +183,44 @@ sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen) case GPROF_STATE: error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state); if (error) - return (error); + break; if (gp->state == GMON_PROF_OFF) stopprofclock(kernproc); else startprofclock(kernproc); - return (0); + break; case GPROF_COUNT: - return (sysctl_struct(oldp, oldlenp, newp, newlen, - gp->kcount, gp->kcountsize)); + error = sysctl_struct(oldp, oldlenp, newp, newlen, + gp->kcount, gp->kcountsize); + break; case GPROF_FROMS: - return (sysctl_struct(oldp, oldlenp, newp, newlen, - gp->froms, gp->fromssize)); + error = sysctl_struct(oldp, oldlenp, newp, newlen, + gp->froms, gp->fromssize); + break; case GPROF_TOS: - return (sysctl_struct(oldp, oldlenp, newp, newlen, - gp->tos, gp->tossize)); + error = sysctl_struct(oldp, oldlenp, newp, newlen, + gp->tos, gp->tossize); + break; case GPROF_GMONPARAM: - return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp)); + error = sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp); + break; default: - return (EOPNOTSUPP); + error = ENOTSUP; + break; } - /* NOTREACHED */ + + /* adjust index so we return the right required/consumed amount */ + if (!error) + req->oldidx += req->oldlen; + + return(error); } +SYSCTL_PROC(_kern, KERN_PROF, prof, STLFLAG_NODE|CTLFLAG_RW | CTLFLAG_LOCKED, + 0, /* Pointer argument (arg1) */ + 0, /* Integer argument (arg2) */ + sysctl_doprofhandle, /* Handler function */ + NULL, /* No explicit data */ + ""); /* @@ -172,15 +228,14 @@ sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen) */ void mcount( - register u_long frompc, - register u_long selfpc + uintptr_t frompc, + uintptr_t selfpc ) { unsigned short *frompcindex; - register struct tostruct *top, *prevtop; + tostruct_t *top, *prevtop; struct gmonparam *p = &_gmonparam; - register long toindex; - MCOUNT_INIT; + long toindex; /* * check that we are profiling @@ -189,7 +244,7 @@ mcount( if (p->state != GMON_PROF_ON) return; - MCOUNT_ENTER; + lck_spin_lock(mcount_lock); /* * check that frompcindex is a reasonable pc value. @@ -272,108 +327,30 @@ mcount( } done: - MCOUNT_EXIT; + lck_spin_unlock(mcount_lock); return; overflow: p->state = GMON_PROF_ERROR; - MCOUNT_EXIT; + lck_spin_unlock(mcount_lock); printf("mcount: tos overflow\n"); return; } #endif /* GPROF */ -#if NCPUS > 1 -#define PROFILE_LOCK(x) simple_lock(x) -#define PROFILE_UNLOCK(x) simple_unlock(x) -#else #define PROFILE_LOCK(x) #define PROFILE_UNLOCK(x) -#endif - -struct profil_args { - short *bufbase; - u_int bufsize; - u_int pcoffset; - u_int pcscale; -}; -int -profil(p, uap, retval) - struct proc *p; - register struct profil_args *uap; - register_t *retval; -{ - register struct uprof *upp = &p->p_stats->p_prof; - struct uprof *upc, *nupc; - int s; - - if (uap->pcscale > (1 << 16)) - return (EINVAL); - if (uap->pcscale == 0) { - stopprofclock(p); - return (0); - } - - /* Block profile interrupts while changing state. */ - s = splstatclock(); - PROFILE_LOCK(&upp->pr_lock); - upp->pr_base = (caddr_t)uap->bufbase; - upp->pr_size = uap->bufsize; - upp->pr_off = uap->pcoffset; - upp->pr_scale = uap->pcscale; - - /* remove buffers previously allocated with add_profil() */ - for (upc = upp->pr_next; upc; upc = nupc) { - nupc = upc->pr_next; - kfree(upc, sizeof (struct uprof)); - } - - upp->pr_next = 0; - PROFILE_UNLOCK(&upp->pr_lock); - startprofclock(p); - splx(s); - return(0); -} -struct add_profile_args { - short *bufbase; - u_int bufsize; - u_int pcoffset; - u_int pcscale; -}; -int -add_profil(p, uap, retval) - struct proc *p; - register struct add_profile_args *uap; - register_t *retval; -{ - struct uprof *upp = &p->p_stats->p_prof, *upc; - int s; - - if (upp->pr_scale == 0) - return (0); - s = splstatclock(); - upc = (struct uprof *) kalloc(sizeof (struct uprof)); - upc->pr_base = (caddr_t)uap->bufbase; - upc->pr_size = uap->bufsize; - upc->pr_off = uap->pcoffset; - upc->pr_scale = uap->pcscale; - PROFILE_LOCK(&upp->pr_lock); - upc->pr_next = upp->pr_next; - upp->pr_next = upc; - PROFILE_UNLOCK(&upp->pr_lock); - splx(s); - return(0); -} /* * Scale is a fixed-point number with the binary point 16 bits * into the value, and is <= 1.0. pc is at most 32 bits, so the * intermediate result is at most 48 bits. */ +//K64todo - this doesn't fit into 64 bit any more, it needs 64+16 #define PC_TO_INDEX(pc, prof) \ - ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ + ((user_addr_t)(((u_quad_t)((pc) - (prof)->pr_off) * \ (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) /* @@ -391,33 +368,53 @@ add_profil(p, uap, retval) * update fails, we simply turn off profiling. */ void -addupc_task(p, pc, ticks) - register struct proc *p; - register u_long pc; - u_int ticks; +addupc_task(struct proc *p, user_addr_t pc, u_int ticks) { - register struct uprof *prof; - register short *cell; - register u_int off; + user_addr_t off; u_short count; /* Testing P_PROFIL may be unnecessary, but is certainly safe. */ if ((p->p_flag & P_PROFIL) == 0 || ticks == 0) return; - for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) { - off = PC_TO_INDEX(pc,prof); - cell = (short *)(prof->pr_base + off); - if (cell >= (short *)prof->pr_base && - cell < (short*)(prof->pr_size + (int) prof->pr_base)) { - if (copyin((caddr_t)cell, (caddr_t) &count, sizeof(count)) == 0) { - count += ticks; - if(copyout((caddr_t) &count, (caddr_t)cell, sizeof(count)) == 0) - return; - } - p->p_stats->p_prof.pr_scale = 0; - stopprofclock(p); - break; - } + if (proc_is64bit(p)) { + struct user_uprof *prof; + user_addr_t cell; + + for (prof = &p->p_stats->user_p_prof; prof; prof = prof->pr_next) { + off = PC_TO_INDEX(pc, prof); + cell = (prof->pr_base + off); + if (cell >= prof->pr_base && + cell < (prof->pr_size + prof->pr_base)) { + if (copyin(cell, (caddr_t) &count, sizeof(count)) == 0) { + count += ticks; + if(copyout((caddr_t) &count, cell, sizeof(count)) == 0) + return; + } + p->p_stats->user_p_prof.pr_scale = 0; + stopprofclock(p); + break; + } + } + } + else { + struct uprof *prof; + short *cell; + + for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) { + off = PC_TO_INDEX(pc,prof); + cell = (short *)(prof->pr_base + off); + if (cell >= (short *)prof->pr_base && + cell < (short*)(prof->pr_size + prof->pr_base)) { + if (copyin(CAST_USER_ADDR_T(cell), (caddr_t) &count, sizeof(count)) == 0) { + count += ticks; + if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0) + return; + } + p->p_stats->p_prof.pr_scale = 0; + stopprofclock(p); + break; + } + } } }