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