1/*
2 * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29/*-
30 * Copyright (c) 1982, 1986, 1993
31 *	The Regents of the University of California.  All rights reserved.
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions
35 * are met:
36 * 1. Redistributions of source code must retain the above copyright
37 *    notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright
39 *    notice, this list of conditions and the following disclaimer in the
40 *    documentation and/or other materials provided with the distribution.
41 * 3. All advertising materials mentioning features or use of this software
42 *    must display the following acknowledgement:
43 *	This product includes software developed by the University of
44 *	California, Berkeley and its contributors.
45 * 4. Neither the name of the University nor the names of its contributors
46 *    may be used to endorse or promote products derived from this software
47 *    without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 *
61 *	@(#)subr_prof.c	8.3 (Berkeley) 9/23/93
62 */
63
64#ifdef GPROF
65#include <kern/mach_header.h>
66#endif
67
68#include <sys/param.h>
69#include <sys/systm.h>
70#include <sys/kernel.h>
71#include <sys/proc_internal.h>
72#include <sys/user.h>
73#include <machine/spl.h>
74#include <machine/machine_routines.h>
75
76#include <sys/mount_internal.h>
77#include <sys/sysproto.h>
78
79#include <mach/mach_types.h>
80#include <kern/kern_types.h>
81#include <kern/cpu_number.h>
82#include <kern/kalloc.h>
83
84#ifdef GPROF
85#include <sys/malloc.h>
86#include <sys/gmon.h>
87
88extern int sysctl_doprof(int *, u_int, user_addr_t, size_t *,
89		user_addr_t, size_t newlen);
90extern int sysctl_struct(user_addr_t, size_t *,
91		user_addr_t, size_t, void *, int);
92
93lck_spin_t * mcount_lock;
94lck_grp_t * mcount_lock_grp;
95lck_attr_t * mcount_lock_attr;
96
97/*
98 * Froms is actually a bunch of unsigned shorts indexing tos
99 */
100struct gmonparam _gmonparam = { .state = GMON_PROF_OFF };
101
102/*
103 * This code uses 32 bit mach object segment information from the currently
104 * running kernel.
105 */
106void
107kmstartup(void)
108{
109	char *cp;
110	struct segment_command	*sgp;	/* 32 bit mach object file segment */
111	struct gmonparam *p = &_gmonparam;
112
113	sgp = getsegbyname("__TEXT");
114	p->lowpc = (u_long)sgp->vmaddr;
115	p->highpc = (u_long)(sgp->vmaddr + sgp->vmsize);
116
117	/*
118	 * Round lowpc and highpc to multiples of the density we're using
119	 * so the rest of the scaling (here and in gprof) stays in ints.
120	 */
121	p->lowpc = ROUNDDOWN(p->lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
122	p->highpc = ROUNDUP(p->highpc, HISTFRACTION * sizeof(HISTCOUNTER));
123	p->textsize = p->highpc - p->lowpc;
124	printf("Profiling kernel, textsize=%lu [0x%016lx..0x%016lx]\n",
125	       p->textsize, p->lowpc, p->highpc);
126	p->kcountsize = p->textsize / HISTFRACTION;
127	p->hashfraction = HASHFRACTION;
128	p->fromssize = p->textsize / HASHFRACTION;
129	p->tolimit = p->textsize * ARCDENSITY / 100;
130	if (p->tolimit < MINARCS)
131		p->tolimit = MINARCS;
132	else if (p->tolimit > MAXARCS)
133		p->tolimit = MAXARCS;
134	p->tossize = p->tolimit * sizeof(struct tostruct);
135	/* Why not use MALLOC with M_GPROF ? */
136	cp = (char *)kalloc(p->kcountsize + p->fromssize + p->tossize);
137	if (cp == 0) {
138		printf("No memory for profiling.\n");
139		return;
140	}
141	bzero(cp, p->kcountsize + p->tossize + p->fromssize);
142	p->tos = (struct tostruct *)cp;
143	cp += p->tossize;
144	p->kcount = (u_short *)cp;
145	cp += p->kcountsize;
146	p->froms = (u_short *)cp;
147
148	mcount_lock_grp = lck_grp_alloc_init("MCOUNT", LCK_GRP_ATTR_NULL);
149	mcount_lock_attr = lck_attr_alloc_init();
150	mcount_lock = lck_spin_alloc_init(mcount_lock_grp, mcount_lock_attr);
151
152}
153
154/*
155 * Return kernel profiling information.
156 */
157int
158sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
159              user_addr_t newp, size_t newlen)
160{
161	struct gmonparam *gp = &_gmonparam;
162	int error;
163
164	/* all sysctl names at this level are terminal */
165	if (namelen != 1)
166		return (ENOTDIR);		/* overloaded */
167
168	switch (name[0]) {
169	case GPROF_STATE:
170		error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
171		if (error)
172			return (error);
173		if (gp->state == GMON_PROF_OFF)
174			stopprofclock(kernproc);
175		else
176			startprofclock(kernproc);
177		return (0);
178	case GPROF_COUNT:
179		return (sysctl_struct(oldp, oldlenp, newp, newlen,
180		                      gp->kcount, gp->kcountsize));
181	case GPROF_FROMS:
182		return (sysctl_struct(oldp, oldlenp, newp, newlen,
183		                      gp->froms, gp->fromssize));
184	case GPROF_TOS:
185		return (sysctl_struct(oldp, oldlenp, newp, newlen,
186		                      gp->tos, gp->tossize));
187	case GPROF_GMONPARAM:
188		return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
189	default:
190		return (ENOTSUP);
191	}
192	/* NOTREACHED */
193}
194
195
196/*
197 * mcount() called with interrupts disabled.
198 */
199void
200mcount(
201    u_long frompc,
202    u_long selfpc
203)
204{
205    unsigned short *frompcindex;
206	struct tostruct *top, *prevtop;
207	struct gmonparam *p = &_gmonparam;
208	long toindex;
209
210    /*
211     * check that we are profiling
212     * and that we aren't recursively invoked.
213     */
214    if (p->state != GMON_PROF_ON)
215        return;
216
217	lck_spin_lock(mcount_lock);
218
219	/*
220	 *	check that frompcindex is a reasonable pc value.
221	 *	for example:	signal catchers get called from the stack,
222	 *			not from text space.  too bad.
223	 */
224	frompc -= p->lowpc;
225	if (frompc > p->textsize)
226		goto done;
227
228	frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
229	toindex = *frompcindex;
230	if (toindex == 0) {
231		/*
232		 *	first time traversing this arc
233		 */
234		toindex = ++p->tos[0].link;
235		if (toindex >= p->tolimit) {
236            /* halt further profiling */
237			goto overflow;
238		}
239		*frompcindex = toindex;
240		top = &p->tos[toindex];
241		top->selfpc = selfpc;
242		top->count = 1;
243		top->link = 0;
244		goto done;
245	}
246	top = &p->tos[toindex];
247	if (top->selfpc == selfpc) {
248		/*
249		 *	arc at front of chain; usual case.
250		 */
251		top->count++;
252		goto done;
253	}
254	/*
255	 *	have to go looking down chain for it.
256	 *	top points to what we are looking at,
257	 *	prevtop points to previous top.
258	 *	we know it is not at the head of the chain.
259	 */
260	for (; /* goto done */; ) {
261		if (top->link == 0) {
262			/*
263			 *	top is end of the chain and none of the chain
264			 *	had top->selfpc == selfpc.
265			 *	so we allocate a new tostruct
266			 *	and link it to the head of the chain.
267			 */
268			toindex = ++p->tos[0].link;
269			if (toindex >= p->tolimit) {
270				goto overflow;
271			}
272			top = &p->tos[toindex];
273			top->selfpc = selfpc;
274			top->count = 1;
275			top->link = *frompcindex;
276			*frompcindex = toindex;
277			goto done;
278		}
279		/*
280		 *	otherwise, check the next arc on the chain.
281		 */
282		prevtop = top;
283		top = &p->tos[top->link];
284		if (top->selfpc == selfpc) {
285			/*
286			 *	there it is.
287			 *	increment its count
288			 *	move it to the head of the chain.
289			 */
290			top->count++;
291			toindex = prevtop->link;
292			prevtop->link = top->link;
293			top->link = *frompcindex;
294			*frompcindex = toindex;
295			goto done;
296		}
297
298	}
299done:
300	lck_spin_unlock(mcount_lock);
301	return;
302
303overflow:
304    p->state = GMON_PROF_ERROR;
305        lck_spin_unlock(mcount_lock);
306	printf("mcount: tos overflow\n");
307	return;
308}
309
310#endif /* GPROF */
311
312#define PROFILE_LOCK(x)
313#define PROFILE_UNLOCK(x)
314
315static int profil_funneled(struct proc *p, struct profil_args *uap, register_t *retval);
316static int add_profil_funneled(struct proc *p, struct add_profil_args *uap, register_t *retval);
317
318
319int
320profil(struct proc *p, struct profil_args *uap, register_t *retval)
321{
322	boolean_t funnel_state;
323	int error;
324
325	funnel_state = thread_funnel_set(kernel_flock, TRUE);
326	error = profil_funneled(p, uap, retval);
327	thread_funnel_set(kernel_flock, funnel_state);
328	return(error);
329}
330
331static int
332profil_funneled(struct proc *p, struct profil_args *uap, __unused register_t *retval)
333{
334        struct uprof *upp = &p->p_stats->p_prof;
335	int s;
336
337	if (uap->pcscale > (1 << 16))
338		return (EINVAL);
339
340	if (uap->pcscale == 0) {
341		stopprofclock(p);
342		return (0);
343	}
344	/*
345	 * Block profile interrupts while changing state.
346	 */
347	s = ml_set_interrupts_enabled(FALSE);
348
349	if (proc_is64bit(p)) {
350	        struct user_uprof *user_upp = &p->p_stats->user_p_prof;
351		struct user_uprof *upc, *nupc;
352
353		PROFILE_LOCK(&user_upp->pr_lock);
354
355		user_upp->pr_base = uap->bufbase;
356		user_upp->pr_size = uap->bufsize;
357		user_upp->pr_off = uap->pcoffset;
358		user_upp->pr_scale = uap->pcscale;
359		upp->pr_base = NULL;
360		upp->pr_size = 0;
361		upp->pr_scale = 0;
362
363		/*
364		 * remove buffers previously allocated with add_profil()
365		 * don't do the kfree's while interrupts disabled
366		 */
367		upc = user_upp->pr_next;
368		user_upp->pr_next = 0;
369
370		PROFILE_UNLOCK(&user_upp->pr_lock);
371
372		startprofclock(p);
373		ml_set_interrupts_enabled(s);
374
375		while (upc) {
376		        nupc = upc->pr_next;
377			kfree(upc, sizeof (*upc));
378			upc = nupc;
379		}
380
381	} else {
382	        struct uprof *upc, *nupc;
383
384		PROFILE_LOCK(&upp->pr_lock);
385
386		upp->pr_base = CAST_DOWN(caddr_t, uap->bufbase);
387		upp->pr_size = uap->bufsize;
388		upp->pr_off = uap->pcoffset;
389		upp->pr_scale = uap->pcscale;
390
391		/*
392		 * remove buffers previously allocated with add_profil()
393		 * don't do the kfree's while interrupts disabled
394		 */
395		upc = upp->pr_next;
396		upp->pr_next = 0;
397
398		PROFILE_UNLOCK(&upp->pr_lock);
399
400		startprofclock(p);
401		ml_set_interrupts_enabled(s);
402
403		while (upc) {
404		        nupc = upc->pr_next;
405			kfree(upc, sizeof (struct uprof));
406			upc = nupc;
407		}
408	}
409	return(0);
410}
411
412int
413add_profil(struct proc *p, struct add_profil_args *uap, register_t *retval)
414{
415	boolean_t funnel_state;
416	int error;
417
418	funnel_state = thread_funnel_set(kernel_flock, TRUE);
419	error = add_profil_funneled(p, uap, retval);
420	thread_funnel_set(kernel_flock, funnel_state);
421	return(error);
422}
423
424
425static int
426add_profil_funneled(struct proc *p, struct add_profil_args *uap, __unused register_t *retval)
427{
428	struct uprof *upp = &p->p_stats->p_prof, *upc;
429	struct user_uprof *user_upp = NULL, *user_upc;
430	int s;
431	boolean_t is64bit = proc_is64bit(p);
432
433
434	upc = NULL;
435	user_upc = NULL;
436
437	if (is64bit) {
438	        user_upp = &p->p_stats->user_p_prof;
439
440		if (user_upp->pr_scale == 0)
441		        return (0);
442	}
443	else {
444	        if (upp->pr_scale == 0)
445		        return (0);
446	}
447	if (is64bit) {
448	        user_upc = (struct user_uprof *) kalloc(sizeof (struct user_uprof));
449	        user_upc->pr_base = uap->bufbase;
450		user_upc->pr_size = uap->bufsize;
451		user_upc->pr_off = uap->pcoffset;
452		user_upc->pr_scale = uap->pcscale;
453	} else {
454	        upc = (struct uprof *) kalloc(sizeof (struct uprof));
455	        upc->pr_base = CAST_DOWN(caddr_t, uap->bufbase);
456		upc->pr_size = uap->bufsize;
457		upc->pr_off = uap->pcoffset;
458		upc->pr_scale = uap->pcscale;
459	}
460	s = ml_set_interrupts_enabled(FALSE);
461
462	if (is64bit) {
463		PROFILE_LOCK(&user_upp->pr_lock);
464		if (user_upp->pr_scale) {
465		        user_upc->pr_next = user_upp->pr_next;
466			user_upp->pr_next = user_upc;
467			user_upc = NULL;
468		}
469		PROFILE_UNLOCK(&user_upp->pr_lock);
470	} else {
471		PROFILE_LOCK(&upp->pr_lock);
472	        if (upp->pr_scale) {
473		        upc->pr_next = upp->pr_next;
474			upp->pr_next = upc;
475			upc = NULL;
476		}
477		PROFILE_UNLOCK(&upp->pr_lock);
478	}
479	ml_set_interrupts_enabled(s);
480
481	if (upc)
482	        kfree(upc, sizeof(struct uprof));
483	if (user_upc)
484	        kfree(user_upc, sizeof(struct user_uprof));
485
486	return(0);
487}
488
489/*
490 * Scale is a fixed-point number with the binary point 16 bits
491 * into the value, and is <= 1.0.  pc is at most 32 bits, so the
492 * intermediate result is at most 48 bits.
493 */
494#define PC_TO_INDEX(pc, prof) \
495	((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
496			(u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
497
498/*
499 * Collect user-level profiling statistics; called on a profiling tick,
500 * when a process is running in user-mode. We use
501 * an AST that will vector us to trap() with a context in which copyin
502 * and copyout will work.  Trap will then call addupc_task().
503 *
504 * Note that we may (rarely) not get around to the AST soon enough, and
505 * lose profile ticks when the next tick overwrites this one, but in this
506 * case the system is overloaded and the profile is probably already
507 * inaccurate.
508 *
509 * We can afford to take faults here.  If the
510 * update fails, we simply turn off profiling.
511 */
512void
513addupc_task(struct proc *p, user_addr_t pc, u_int ticks)
514{
515	u_int off;
516	u_short count;
517
518	/* Testing P_PROFIL may be unnecessary, but is certainly safe. */
519	if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
520		return;
521
522	if (proc_is64bit(p)) {
523        struct user_uprof *prof;
524        user_addr_t cell;
525
526        for (prof = &p->p_stats->user_p_prof; prof; prof = prof->pr_next) {
527            off = PC_TO_INDEX(pc, prof);
528            cell = (prof->pr_base + off);
529            if (cell >= prof->pr_base &&
530                cell < (prof->pr_size + prof->pr_base)) {
531                if (copyin(cell, (caddr_t) &count, sizeof(count)) == 0) {
532                    count += ticks;
533                    if(copyout((caddr_t) &count, cell, sizeof(count)) == 0)
534                        return;
535                }
536                p->p_stats->user_p_prof.pr_scale = 0;
537                stopprofclock(p);
538                break;
539            }
540        }
541	}
542	else {
543        struct uprof *prof;
544        short *cell;
545
546        for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) {
547            off = PC_TO_INDEX(CAST_DOWN(uint, pc),prof);
548            cell = (short *)(prof->pr_base + off);
549            if (cell >= (short *)prof->pr_base &&
550                cell < (short*)(prof->pr_size + (int) prof->pr_base)) {
551                if (copyin(CAST_USER_ADDR_T(cell), (caddr_t) &count, sizeof(count)) == 0) {
552                    count += ticks;
553                    if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0)
554                        return;
555                }
556                p->p_stats->p_prof.pr_scale = 0;
557                stopprofclock(p);
558                break;
559            }
560        }
561	}
562}
563