/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2008 Apple Computer, Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
- *
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* 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. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
+ * 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.
+ *
+ * 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
* 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 */
/*-
* @(#)subr_prof.c 8.3 (Berkeley) 9/23/93
*/
+#ifdef GPROF
+#include <libkern/kernel_mach_header.h>
+#endif
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
-#include <sys/proc.h>
+#include <sys/proc_internal.h>
#include <sys/user.h>
#include <machine/spl.h>
+#include <machine/machine_routines.h>
-#include <sys/mount.h>
+#include <sys/mount_internal.h>
+#include <sys/sysproto.h>
+#include <mach/mach_types.h>
+#include <kern/kern_types.h>
#include <kern/cpu_number.h>
+#include <kern/kalloc.h>
#ifdef GPROF
#include <sys/malloc.h>
#include <sys/gmon.h>
-#include <kern/mach_header.h>
-#include <machine/profile.h>
+
+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
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;
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)
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 */
+ "");
/*
*/
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
if (p->state != GMON_PROF_ON)
return;
- MCOUNT_ENTER;
+ lck_spin_lock(mcount_lock);
/*
* check that frompcindex is a reasonable pc value.
}
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)
/*
* 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;
+ }
+ }
}
}