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