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