]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/subr_prof.c
xnu-792.17.14.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 //lck_attr_setdebug(mcount_lock_attr);
147 mcount_lock = lck_spin_alloc_init(mcount_lock_grp, mcount_lock_attr);
148
149 }
150
151 /*
152 * Return kernel profiling information.
153 */
154 int
155 sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
156 user_addr_t newp, size_t newlen)
157 {
158 struct gmonparam *gp = &_gmonparam;
159 int error;
160
161 /* all sysctl names at this level are terminal */
162 if (namelen != 1)
163 return (ENOTDIR); /* overloaded */
164
165 switch (name[0]) {
166 case GPROF_STATE:
167 error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
168 if (error)
169 return (error);
170 if (gp->state == GMON_PROF_OFF)
171 stopprofclock(kernproc);
172 else
173 startprofclock(kernproc);
174 return (0);
175 case GPROF_COUNT:
176 return (sysctl_struct(oldp, oldlenp, newp, newlen,
177 gp->kcount, gp->kcountsize));
178 case GPROF_FROMS:
179 return (sysctl_struct(oldp, oldlenp, newp, newlen,
180 gp->froms, gp->fromssize));
181 case GPROF_TOS:
182 return (sysctl_struct(oldp, oldlenp, newp, newlen,
183 gp->tos, gp->tossize));
184 case GPROF_GMONPARAM:
185 return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
186 default:
187 return (ENOTSUP);
188 }
189 /* NOTREACHED */
190 }
191
192
193 /*
194 * mcount() called with interrupts disabled.
195 */
196 void
197 mcount(
198 register u_long frompc,
199 register u_long selfpc
200 )
201 {
202 unsigned short *frompcindex;
203 register struct tostruct *top, *prevtop;
204 struct gmonparam *p = &_gmonparam;
205 register long toindex;
206
207 /*
208 * check that we are profiling
209 * and that we aren't recursively invoked.
210 */
211 if (p->state != GMON_PROF_ON)
212 return;
213
214 lck_spin_lock(mcount_lock);
215
216 /*
217 * check that frompcindex is a reasonable pc value.
218 * for example: signal catchers get called from the stack,
219 * not from text space. too bad.
220 */
221 frompc -= p->lowpc;
222 if (frompc > p->textsize)
223 goto done;
224
225 frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
226 toindex = *frompcindex;
227 if (toindex == 0) {
228 /*
229 * first time traversing this arc
230 */
231 toindex = ++p->tos[0].link;
232 if (toindex >= p->tolimit) {
233 /* halt further profiling */
234 goto overflow;
235 }
236 *frompcindex = toindex;
237 top = &p->tos[toindex];
238 top->selfpc = selfpc;
239 top->count = 1;
240 top->link = 0;
241 goto done;
242 }
243 top = &p->tos[toindex];
244 if (top->selfpc == selfpc) {
245 /*
246 * arc at front of chain; usual case.
247 */
248 top->count++;
249 goto done;
250 }
251 /*
252 * have to go looking down chain for it.
253 * top points to what we are looking at,
254 * prevtop points to previous top.
255 * we know it is not at the head of the chain.
256 */
257 for (; /* goto done */; ) {
258 if (top->link == 0) {
259 /*
260 * top is end of the chain and none of the chain
261 * had top->selfpc == selfpc.
262 * so we allocate a new tostruct
263 * and link it to the head of the chain.
264 */
265 toindex = ++p->tos[0].link;
266 if (toindex >= p->tolimit) {
267 goto overflow;
268 }
269 top = &p->tos[toindex];
270 top->selfpc = selfpc;
271 top->count = 1;
272 top->link = *frompcindex;
273 *frompcindex = toindex;
274 goto done;
275 }
276 /*
277 * otherwise, check the next arc on the chain.
278 */
279 prevtop = top;
280 top = &p->tos[top->link];
281 if (top->selfpc == selfpc) {
282 /*
283 * there it is.
284 * increment its count
285 * move it to the head of the chain.
286 */
287 top->count++;
288 toindex = prevtop->link;
289 prevtop->link = top->link;
290 top->link = *frompcindex;
291 *frompcindex = toindex;
292 goto done;
293 }
294
295 }
296 done:
297 lck_spin_unlock(mcount_lock);
298 return;
299
300 overflow:
301 p->state = GMON_PROF_ERROR;
302 lck_spin_unlock(mcount_lock);
303 printf("mcount: tos overflow\n");
304 return;
305 }
306
307 #endif /* GPROF */
308
309 #define PROFILE_LOCK(x)
310 #define PROFILE_UNLOCK(x)
311
312 int
313 profil(struct proc *p, register struct profil_args *uap, __unused register_t *retval)
314 {
315 struct uprof *upp = &p->p_stats->p_prof;
316 int s;
317
318 if (uap->pcscale > (1 << 16))
319 return (EINVAL);
320 if (uap->pcscale == 0) {
321 stopprofclock(p);
322 return (0);
323 }
324
325 /* Block profile interrupts while changing state. */
326 s = ml_set_interrupts_enabled(FALSE);
327
328 if (proc_is64bit(p)) {
329 struct user_uprof *user_upp = &p->p_stats->user_p_prof;
330 struct user_uprof *upc, *nupc;
331
332 PROFILE_LOCK(&user_upp->pr_lock);
333 user_upp->pr_base = uap->bufbase;
334 user_upp->pr_size = uap->bufsize;
335 user_upp->pr_off = uap->pcoffset;
336 user_upp->pr_scale = uap->pcscale;
337 upp->pr_base = NULL;
338 upp->pr_size = 0;
339 upp->pr_scale = 0;
340
341 /* remove buffers previously allocated with add_profil() */
342 for (upc = user_upp->pr_next; upc; upc = nupc) {
343 nupc = upc->pr_next;
344 kfree(upc, sizeof (*upc));
345 }
346 user_upp->pr_next = 0;
347 PROFILE_UNLOCK(&user_upp->pr_lock);
348 }
349 else {
350 struct uprof *upc, *nupc;
351
352 PROFILE_LOCK(&upp->pr_lock);
353 upp->pr_base = CAST_DOWN(caddr_t, uap->bufbase);
354 upp->pr_size = uap->bufsize;
355 upp->pr_off = uap->pcoffset;
356 upp->pr_scale = uap->pcscale;
357
358 /* remove buffers previously allocated with add_profil() */
359 for (upc = upp->pr_next; upc; upc = nupc) {
360 nupc = upc->pr_next;
361 kfree(upc, sizeof (struct uprof));
362 }
363 upp->pr_next = 0;
364 PROFILE_UNLOCK(&upp->pr_lock);
365 }
366
367 startprofclock(p);
368 ml_set_interrupts_enabled(s);
369 return(0);
370 }
371
372 int
373 add_profil(struct proc *p, register struct add_profil_args *uap, __unused register_t *retval)
374 {
375 struct uprof *upp = &p->p_stats->p_prof, *upc;
376 struct user_uprof *user_upp = NULL, *user_upc;
377 int s;
378 boolean_t is64bit = proc_is64bit(p);
379
380 if (is64bit) {
381 user_upp = &p->p_stats->user_p_prof;
382 if (user_upp->pr_scale == 0)
383 return (0);
384 }
385 else {
386 if (upp->pr_scale == 0)
387 return (0);
388 }
389
390 s = ml_set_interrupts_enabled(FALSE);
391
392 if (is64bit) {
393 user_upc = (struct user_uprof *) kalloc(sizeof (struct user_uprof));
394 user_upc->pr_base = uap->bufbase;
395 user_upc->pr_size = uap->bufsize;
396 user_upc->pr_off = uap->pcoffset;
397 user_upc->pr_scale = uap->pcscale;
398 PROFILE_LOCK(&user_upp->pr_lock);
399 user_upc->pr_next = user_upp->pr_next;
400 user_upp->pr_next = user_upc;
401 PROFILE_UNLOCK(&user_upp->pr_lock);
402 }
403 else {
404 upc = (struct uprof *) kalloc(sizeof (struct uprof));
405 upc->pr_base = CAST_DOWN(caddr_t, uap->bufbase);
406 upc->pr_size = uap->bufsize;
407 upc->pr_off = uap->pcoffset;
408 upc->pr_scale = uap->pcscale;
409 PROFILE_LOCK(&upp->pr_lock);
410 upc->pr_next = upp->pr_next;
411 upp->pr_next = upc;
412 PROFILE_UNLOCK(&upp->pr_lock);
413 }
414
415 ml_set_interrupts_enabled(s);
416 return(0);
417 }
418
419 /*
420 * Scale is a fixed-point number with the binary point 16 bits
421 * into the value, and is <= 1.0. pc is at most 32 bits, so the
422 * intermediate result is at most 48 bits.
423 */
424 #define PC_TO_INDEX(pc, prof) \
425 ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
426 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
427
428 /*
429 * Collect user-level profiling statistics; called on a profiling tick,
430 * when a process is running in user-mode. We use
431 * an AST that will vector us to trap() with a context in which copyin
432 * and copyout will work. Trap will then call addupc_task().
433 *
434 * Note that we may (rarely) not get around to the AST soon enough, and
435 * lose profile ticks when the next tick overwrites this one, but in this
436 * case the system is overloaded and the profile is probably already
437 * inaccurate.
438 *
439 * We can afford to take faults here. If the
440 * update fails, we simply turn off profiling.
441 */
442 void
443 addupc_task(p, pc, ticks)
444 register struct proc *p;
445 user_addr_t pc;
446 u_int ticks;
447 {
448 register u_int off;
449 u_short count;
450
451 /* Testing P_PROFIL may be unnecessary, but is certainly safe. */
452 if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
453 return;
454
455 if (proc_is64bit(p)) {
456 struct user_uprof *prof;
457 user_addr_t cell;
458
459 for (prof = &p->p_stats->user_p_prof; prof; prof = prof->pr_next) {
460 off = PC_TO_INDEX(pc, prof);
461 cell = (prof->pr_base + off);
462 if (cell >= prof->pr_base &&
463 cell < (prof->pr_size + prof->pr_base)) {
464 if (copyin(cell, (caddr_t) &count, sizeof(count)) == 0) {
465 count += ticks;
466 if(copyout((caddr_t) &count, cell, sizeof(count)) == 0)
467 return;
468 }
469 p->p_stats->user_p_prof.pr_scale = 0;
470 stopprofclock(p);
471 break;
472 }
473 }
474 }
475 else {
476 struct uprof *prof;
477 short *cell;
478
479 for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) {
480 off = PC_TO_INDEX(CAST_DOWN(uint, pc),prof);
481 cell = (short *)(prof->pr_base + off);
482 if (cell >= (short *)prof->pr_base &&
483 cell < (short*)(prof->pr_size + (int) prof->pr_base)) {
484 if (copyin(CAST_USER_ADDR_T(cell), (caddr_t) &count, sizeof(count)) == 0) {
485 count += ticks;
486 if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0)
487 return;
488 }
489 p->p_stats->p_prof.pr_scale = 0;
490 stopprofclock(p);
491 break;
492 }
493 }
494 }
495 }