X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d7e50217d7adf6e52786a38bcaa4cd698cb9a79e..4d15aeb193b2c68f1d38666c317f8d3734f5f083:/bsd/kern/subr_prof.c?ds=sidebyside

diff --git a/bsd/kern/subr_prof.c b/bsd/kern/subr_prof.c
index 4b84e1430..80b6edc27 100644
--- a/bsd/kern/subr_prof.c
+++ b/bsd/kern/subr_prof.c
@@ -1,16 +1,19 @@
 /*
- * 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
@@ -20,7 +23,7 @@
  * 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 */
 /*-
@@ -58,38 +61,58 @@
  *	@(#)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
@@ -98,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;
@@ -108,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)
@@ -146,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 */
+	"");
 
 
 /*
@@ -175,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
@@ -192,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.
@@ -275,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)
 
 /*
@@ -394,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;
+            }
+        }
 	}
 }