]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/subr_prof.c
xnu-792.22.5.tar.gz
[apple/xnu.git] / bsd / kern / subr_prof.c
1 /*
2 * Copyright (c) 2000-2004 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 #include <sys/param.h>
65 #include <sys/systm.h>
66 #include <sys/kernel.h>
67 #include <sys/proc_internal.h>
68 #include <sys/user.h>
69 #include <machine/spl.h>
70 #include <machine/machine_routines.h>
71
72 #include <sys/mount_internal.h>
73 #include <sys/sysproto.h>
74
75 #include <mach/mach_types.h>
76 #include <kern/kern_types.h>
77 #include <kern/cpu_number.h>
78 #include <kern/kalloc.h>
79
80 extern boolean_t ml_set_interrupts_enabled(boolean_t enable);
81
82 #ifdef GPROF
83 #include <sys/malloc.h>
84 #include <sys/gmon.h>
85 #include <kern/mach_header.h>
86 #include <machine/profile.h>
87
88 lck_spin_t * mcount_lock;
89 lck_grp_t * mcount_lock_grp;
90 lck_attr_t * mcount_lock_attr;
91
92 /*
93 * Froms is actually a bunch of unsigned shorts indexing tos
94 */
95 struct gmonparam _gmonparam = { GMON_PROF_OFF };
96
97 /*
98 * This code uses 32 bit mach object segment information from the currently
99 * running kernel.
100 */
101 void
102 kmstartup(void)
103 {
104 char *cp;
105 u_long fromssize, tossize;
106 struct segment_command *sgp; /* 32 bit mach object file segment */
107 struct gmonparam *p = &_gmonparam;
108
109 sgp = getsegbyname("__TEXT");
110 p->lowpc = (u_long)sgp->vmaddr;
111 p->highpc = (u_long)(sgp->vmaddr + sgp->vmsize);
112
113 /*
114 * Round lowpc and highpc to multiples of the density we're using
115 * so the rest of the scaling (here and in gprof) stays in ints.
116 */
117 p->lowpc = ROUNDDOWN(p->lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
118 p->highpc = ROUNDUP(p->highpc, HISTFRACTION * sizeof(HISTCOUNTER));
119 p->textsize = p->highpc - p->lowpc;
120 printf("Profiling kernel, textsize=%d [0x%08x..0x%08x]\n",
121 p->textsize, p->lowpc, p->highpc);
122 p->kcountsize = p->textsize / HISTFRACTION;
123 p->hashfraction = HASHFRACTION;
124 p->fromssize = p->textsize / HASHFRACTION;
125 p->tolimit = p->textsize * ARCDENSITY / 100;
126 if (p->tolimit < MINARCS)
127 p->tolimit = MINARCS;
128 else if (p->tolimit > MAXARCS)
129 p->tolimit = MAXARCS;
130 p->tossize = p->tolimit * sizeof(struct tostruct);
131 /* Why not use MALLOC with M_GPROF ? */
132 cp = (char *)kalloc(p->kcountsize + p->fromssize + p->tossize);
133 if (cp == 0) {
134 printf("No memory for profiling.\n");
135 return;
136 }
137 bzero(cp, p->kcountsize + p->tossize + p->fromssize);
138 p->tos = (struct tostruct *)cp;
139 cp += p->tossize;
140 p->kcount = (u_short *)cp;
141 cp += p->kcountsize;
142 p->froms = (u_short *)cp;
143
144 mcount_lock_grp = lck_grp_alloc_init("MCOUNT", LCK_GRP_ATTR_NULL);
145 mcount_lock_attr = lck_attr_alloc_init();
146 mcount_lock = lck_spin_alloc_init(mcount_lock_grp, mcount_lock_attr);
147
148 }
149
150 /*
151 * Return kernel profiling information.
152 */
153 int
154 sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
155 user_addr_t newp, size_t newlen)
156 {
157 struct gmonparam *gp = &_gmonparam;
158 int error;
159
160 /* all sysctl names at this level are terminal */
161 if (namelen != 1)
162 return (ENOTDIR); /* overloaded */
163
164 switch (name[0]) {
165 case GPROF_STATE:
166 error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
167 if (error)
168 return (error);
169 if (gp->state == GMON_PROF_OFF)
170 stopprofclock(kernproc);
171 else
172 startprofclock(kernproc);
173 return (0);
174 case GPROF_COUNT:
175 return (sysctl_struct(oldp, oldlenp, newp, newlen,
176 gp->kcount, gp->kcountsize));
177 case GPROF_FROMS:
178 return (sysctl_struct(oldp, oldlenp, newp, newlen,
179 gp->froms, gp->fromssize));
180 case GPROF_TOS:
181 return (sysctl_struct(oldp, oldlenp, newp, newlen,
182 gp->tos, gp->tossize));
183 case GPROF_GMONPARAM:
184 return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
185 default:
186 return (ENOTSUP);
187 }
188 /* NOTREACHED */
189 }
190
191
192 /*
193 * mcount() called with interrupts disabled.
194 */
195 void
196 mcount(
197 register u_long frompc,
198 register u_long selfpc
199 )
200 {
201 unsigned short *frompcindex;
202 register struct tostruct *top, *prevtop;
203 struct gmonparam *p = &_gmonparam;
204 register long toindex;
205
206 /*
207 * check that we are profiling
208 * and that we aren't recursively invoked.
209 */
210 if (p->state != GMON_PROF_ON)
211 return;
212
213 lck_spin_lock(mcount_lock);
214
215 /*
216 * check that frompcindex is a reasonable pc value.
217 * for example: signal catchers get called from the stack,
218 * not from text space. too bad.
219 */
220 frompc -= p->lowpc;
221 if (frompc > p->textsize)
222 goto done;
223
224 frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
225 toindex = *frompcindex;
226 if (toindex == 0) {
227 /*
228 * first time traversing this arc
229 */
230 toindex = ++p->tos[0].link;
231 if (toindex >= p->tolimit) {
232 /* halt further profiling */
233 goto overflow;
234 }
235 *frompcindex = toindex;
236 top = &p->tos[toindex];
237 top->selfpc = selfpc;
238 top->count = 1;
239 top->link = 0;
240 goto done;
241 }
242 top = &p->tos[toindex];
243 if (top->selfpc == selfpc) {
244 /*
245 * arc at front of chain; usual case.
246 */
247 top->count++;
248 goto done;
249 }
250 /*
251 * have to go looking down chain for it.
252 * top points to what we are looking at,
253 * prevtop points to previous top.
254 * we know it is not at the head of the chain.
255 */
256 for (; /* goto done */; ) {
257 if (top->link == 0) {
258 /*
259 * top is end of the chain and none of the chain
260 * had top->selfpc == selfpc.
261 * so we allocate a new tostruct
262 * and link it to the head of the chain.
263 */
264 toindex = ++p->tos[0].link;
265 if (toindex >= p->tolimit) {
266 goto overflow;
267 }
268 top = &p->tos[toindex];
269 top->selfpc = selfpc;
270 top->count = 1;
271 top->link = *frompcindex;
272 *frompcindex = toindex;
273 goto done;
274 }
275 /*
276 * otherwise, check the next arc on the chain.
277 */
278 prevtop = top;
279 top = &p->tos[top->link];
280 if (top->selfpc == selfpc) {
281 /*
282 * there it is.
283 * increment its count
284 * move it to the head of the chain.
285 */
286 top->count++;
287 toindex = prevtop->link;
288 prevtop->link = top->link;
289 top->link = *frompcindex;
290 *frompcindex = toindex;
291 goto done;
292 }
293
294 }
295 done:
296 lck_spin_unlock(mcount_lock);
297 return;
298
299 overflow:
300 p->state = GMON_PROF_ERROR;
301 lck_spin_unlock(mcount_lock);
302 printf("mcount: tos overflow\n");
303 return;
304 }
305
306 #endif /* GPROF */
307
308 #define PROFILE_LOCK(x)
309 #define PROFILE_UNLOCK(x)
310
311 int
312 profil(struct proc *p, register struct profil_args *uap, __unused register_t *retval)
313 {
314 struct uprof *upp = &p->p_stats->p_prof;
315 int s;
316
317 if (uap->pcscale > (1 << 16))
318 return (EINVAL);
319 if (uap->pcscale == 0) {
320 stopprofclock(p);
321 return (0);
322 }
323
324 /* Block profile interrupts while changing state. */
325 s = ml_set_interrupts_enabled(FALSE);
326
327 if (proc_is64bit(p)) {
328 struct user_uprof *user_upp = &p->p_stats->user_p_prof;
329 struct user_uprof *upc, *nupc;
330
331 PROFILE_LOCK(&user_upp->pr_lock);
332 user_upp->pr_base = uap->bufbase;
333 user_upp->pr_size = uap->bufsize;
334 user_upp->pr_off = uap->pcoffset;
335 user_upp->pr_scale = uap->pcscale;
336 upp->pr_base = NULL;
337 upp->pr_size = 0;
338 upp->pr_scale = 0;
339
340 /* remove buffers previously allocated with add_profil() */
341 for (upc = user_upp->pr_next; upc; upc = nupc) {
342 nupc = upc->pr_next;
343 kfree(upc, sizeof (*upc));
344 }
345 user_upp->pr_next = 0;
346 PROFILE_UNLOCK(&user_upp->pr_lock);
347 }
348 else {
349 struct uprof *upc, *nupc;
350
351 PROFILE_LOCK(&upp->pr_lock);
352 upp->pr_base = CAST_DOWN(caddr_t, uap->bufbase);
353 upp->pr_size = uap->bufsize;
354 upp->pr_off = uap->pcoffset;
355 upp->pr_scale = uap->pcscale;
356
357 /* remove buffers previously allocated with add_profil() */
358 for (upc = upp->pr_next; upc; upc = nupc) {
359 nupc = upc->pr_next;
360 kfree(upc, sizeof (struct uprof));
361 }
362 upp->pr_next = 0;
363 PROFILE_UNLOCK(&upp->pr_lock);
364 }
365
366 startprofclock(p);
367 ml_set_interrupts_enabled(s);
368 return(0);
369 }
370
371 int
372 add_profil(struct proc *p, register struct add_profil_args *uap, __unused register_t *retval)
373 {
374 struct uprof *upp = &p->p_stats->p_prof, *upc;
375 struct user_uprof *user_upp = NULL, *user_upc;
376 int s;
377 boolean_t is64bit = proc_is64bit(p);
378
379 if (is64bit) {
380 user_upp = &p->p_stats->user_p_prof;
381 if (user_upp->pr_scale == 0)
382 return (0);
383 }
384 else {
385 if (upp->pr_scale == 0)
386 return (0);
387 }
388
389 s = ml_set_interrupts_enabled(FALSE);
390
391 if (is64bit) {
392 user_upc = (struct user_uprof *) kalloc(sizeof (struct user_uprof));
393 user_upc->pr_base = uap->bufbase;
394 user_upc->pr_size = uap->bufsize;
395 user_upc->pr_off = uap->pcoffset;
396 user_upc->pr_scale = uap->pcscale;
397 PROFILE_LOCK(&user_upp->pr_lock);
398 user_upc->pr_next = user_upp->pr_next;
399 user_upp->pr_next = user_upc;
400 PROFILE_UNLOCK(&user_upp->pr_lock);
401 }
402 else {
403 upc = (struct uprof *) kalloc(sizeof (struct uprof));
404 upc->pr_base = CAST_DOWN(caddr_t, uap->bufbase);
405 upc->pr_size = uap->bufsize;
406 upc->pr_off = uap->pcoffset;
407 upc->pr_scale = uap->pcscale;
408 PROFILE_LOCK(&upp->pr_lock);
409 upc->pr_next = upp->pr_next;
410 upp->pr_next = upc;
411 PROFILE_UNLOCK(&upp->pr_lock);
412 }
413
414 ml_set_interrupts_enabled(s);
415 return(0);
416 }
417
418 /*
419 * Scale is a fixed-point number with the binary point 16 bits
420 * into the value, and is <= 1.0. pc is at most 32 bits, so the
421 * intermediate result is at most 48 bits.
422 */
423 #define PC_TO_INDEX(pc, prof) \
424 ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
425 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
426
427 /*
428 * Collect user-level profiling statistics; called on a profiling tick,
429 * when a process is running in user-mode. We use
430 * an AST that will vector us to trap() with a context in which copyin
431 * and copyout will work. Trap will then call addupc_task().
432 *
433 * Note that we may (rarely) not get around to the AST soon enough, and
434 * lose profile ticks when the next tick overwrites this one, but in this
435 * case the system is overloaded and the profile is probably already
436 * inaccurate.
437 *
438 * We can afford to take faults here. If the
439 * update fails, we simply turn off profiling.
440 */
441 void
442 addupc_task(p, pc, ticks)
443 register struct proc *p;
444 user_addr_t pc;
445 u_int ticks;
446 {
447 register u_int off;
448 u_short count;
449
450 /* Testing P_PROFIL may be unnecessary, but is certainly safe. */
451 if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
452 return;
453
454 if (proc_is64bit(p)) {
455 struct user_uprof *prof;
456 user_addr_t cell;
457
458 for (prof = &p->p_stats->user_p_prof; prof; prof = prof->pr_next) {
459 off = PC_TO_INDEX(pc, prof);
460 cell = (prof->pr_base + off);
461 if (cell >= prof->pr_base &&
462 cell < (prof->pr_size + prof->pr_base)) {
463 if (copyin(cell, (caddr_t) &count, sizeof(count)) == 0) {
464 count += ticks;
465 if(copyout((caddr_t) &count, cell, sizeof(count)) == 0)
466 return;
467 }
468 p->p_stats->user_p_prof.pr_scale = 0;
469 stopprofclock(p);
470 break;
471 }
472 }
473 }
474 else {
475 struct uprof *prof;
476 short *cell;
477
478 for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) {
479 off = PC_TO_INDEX(CAST_DOWN(uint, pc),prof);
480 cell = (short *)(prof->pr_base + off);
481 if (cell >= (short *)prof->pr_base &&
482 cell < (short*)(prof->pr_size + (int) prof->pr_base)) {
483 if (copyin(CAST_USER_ADDR_T(cell), (caddr_t) &count, sizeof(count)) == 0) {
484 count += ticks;
485 if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0)
486 return;
487 }
488 p->p_stats->p_prof.pr_scale = 0;
489 stopprofclock(p);
490 break;
491 }
492 }
493 }
494 }