subr_prof.c revision 1.33.20.8 1 /* $NetBSD: subr_prof.c,v 1.33.20.8 2007/01/31 23:38:38 ad Exp $ */
2
3 /*-
4 * Copyright (c) 1982, 1986, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * @(#)subr_prof.c 8.4 (Berkeley) 2/14/95
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: subr_prof.c,v 1.33.20.8 2007/01/31 23:38:38 ad Exp $");
36
37 #include "opt_lockdebug.h"
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/proc.h>
43 #include <sys/user.h>
44 #include <sys/mount.h>
45 #include <sys/syscallargs.h>
46 #include <sys/sysctl.h>
47
48 #include <machine/cpu.h>
49
50 #ifdef GPROF
51 #include <sys/malloc.h>
52 #include <sys/gmon.h>
53
54 MALLOC_DEFINE(M_GPROF, "gprof", "kernel profiling buffer");
55
56 #ifdef LOCKDEBUG
57 #error The LOCKDEBUG option can not be used with a profiling kernel
58 #endif
59
60 /*
61 * Froms is actually a bunch of unsigned shorts indexing tos
62 */
63 struct gmonparam _gmonparam = { .state = GMON_PROF_OFF };
64
65 /* Actual start of the kernel text segment. */
66 extern char kernel_text[];
67
68 extern char etext[];
69
70
71 void
72 kmstartup(void)
73 {
74 char *cp;
75 struct gmonparam *p = &_gmonparam;
76 /*
77 * Round lowpc and highpc to multiples of the density we're using
78 * so the rest of the scaling (here and in gprof) stays in ints.
79 */
80 p->lowpc = rounddown(((u_long)kernel_text),
81 HISTFRACTION * sizeof(HISTCOUNTER));
82 p->highpc = roundup((u_long)etext,
83 HISTFRACTION * sizeof(HISTCOUNTER));
84 p->textsize = p->highpc - p->lowpc;
85 printf("Profiling kernel, textsize=%ld [%lx..%lx]\n",
86 p->textsize, p->lowpc, p->highpc);
87 p->kcountsize = p->textsize / HISTFRACTION;
88 p->hashfraction = HASHFRACTION;
89 p->fromssize = p->textsize / HASHFRACTION;
90 p->tolimit = p->textsize * ARCDENSITY / 100;
91 if (p->tolimit < MINARCS)
92 p->tolimit = MINARCS;
93 else if (p->tolimit > MAXARCS)
94 p->tolimit = MAXARCS;
95 p->tossize = p->tolimit * sizeof(struct tostruct);
96 cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize,
97 M_GPROF, M_NOWAIT | M_ZERO);
98 if (cp == 0) {
99 printf("No memory for profiling.\n");
100 return;
101 }
102 p->tos = (struct tostruct *)cp;
103 cp += p->tossize;
104 p->kcount = (u_short *)cp;
105 cp += p->kcountsize;
106 p->froms = (u_short *)cp;
107 }
108
109 /*
110 * Return kernel profiling information.
111 */
112 /*
113 * sysctl helper routine for kern.profiling subtree. enables/disables
114 * kernel profiling and gives out copies of the profiling data.
115 */
116 static int
117 sysctl_kern_profiling(SYSCTLFN_ARGS)
118 {
119 struct gmonparam *gp = &_gmonparam;
120 int error;
121 struct sysctlnode node;
122
123 node = *rnode;
124
125 switch (node.sysctl_num) {
126 case GPROF_STATE:
127 node.sysctl_data = &gp->state;
128 break;
129 case GPROF_COUNT:
130 node.sysctl_data = gp->kcount;
131 node.sysctl_size = gp->kcountsize;
132 break;
133 case GPROF_FROMS:
134 node.sysctl_data = gp->froms;
135 node.sysctl_size = gp->fromssize;
136 break;
137 case GPROF_TOS:
138 node.sysctl_data = gp->tos;
139 node.sysctl_size = gp->tossize;
140 break;
141 case GPROF_GMONPARAM:
142 node.sysctl_data = gp;
143 node.sysctl_size = sizeof(*gp);
144 break;
145 default:
146 return (EOPNOTSUPP);
147 }
148
149 error = sysctl_lookup(SYSCTLFN_CALL(&node));
150 if (error || newp == NULL)
151 return (error);
152
153 if (node.sysctl_num == GPROF_STATE) {
154 mutex_enter(&proc0.p_stmutex);
155 if (gp->state == GMON_PROF_OFF)
156 stopprofclock(&proc0);
157 else
158 startprofclock(&proc0);
159 mutex_exit(&proc0.p_stmutex);
160 }
161
162 return (0);
163 }
164
165 SYSCTL_SETUP(sysctl_kern_gprof_setup, "sysctl kern.profiling subtree setup")
166 {
167
168 sysctl_createv(clog, 0, NULL, NULL,
169 CTLFLAG_PERMANENT,
170 CTLTYPE_NODE, "kern", NULL,
171 NULL, 0, NULL, 0,
172 CTL_KERN, CTL_EOL);
173 sysctl_createv(clog, 0, NULL, NULL,
174 CTLFLAG_PERMANENT,
175 CTLTYPE_NODE, "profiling",
176 SYSCTL_DESCR("Profiling information (available)"),
177 NULL, 0, NULL, 0,
178 CTL_KERN, KERN_PROF, CTL_EOL);
179
180 sysctl_createv(clog, 0, NULL, NULL,
181 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
182 CTLTYPE_INT, "state",
183 SYSCTL_DESCR("Profiling state"),
184 sysctl_kern_profiling, 0, NULL, 0,
185 CTL_KERN, KERN_PROF, GPROF_STATE, CTL_EOL);
186 sysctl_createv(clog, 0, NULL, NULL,
187 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
188 CTLTYPE_STRUCT, "count",
189 SYSCTL_DESCR("Array of statistical program counters"),
190 sysctl_kern_profiling, 0, NULL, 0,
191 CTL_KERN, KERN_PROF, GPROF_COUNT, CTL_EOL);
192 sysctl_createv(clog, 0, NULL, NULL,
193 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
194 CTLTYPE_STRUCT, "froms",
195 SYSCTL_DESCR("Array indexed by program counter of "
196 "call-from points"),
197 sysctl_kern_profiling, 0, NULL, 0,
198 CTL_KERN, KERN_PROF, GPROF_FROMS, CTL_EOL);
199 sysctl_createv(clog, 0, NULL, NULL,
200 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
201 CTLTYPE_STRUCT, "tos",
202 SYSCTL_DESCR("Array of structures describing "
203 "destination of calls and their counts"),
204 sysctl_kern_profiling, 0, NULL, 0,
205 CTL_KERN, KERN_PROF, GPROF_TOS, CTL_EOL);
206 sysctl_createv(clog, 0, NULL, NULL,
207 CTLFLAG_PERMANENT,
208 CTLTYPE_STRUCT, "gmonparam",
209 SYSCTL_DESCR("Structure giving the sizes of the above "
210 "arrays"),
211 sysctl_kern_profiling, 0, NULL, 0,
212 CTL_KERN, KERN_PROF, GPROF_GMONPARAM, CTL_EOL);
213 }
214 #endif /* GPROF */
215
216 /*
217 * Profiling system call.
218 *
219 * The scale factor is a fixed point number with 16 bits of fraction, so that
220 * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling.
221 */
222 /* ARGSUSED */
223 int
224 sys_profil(struct lwp *l, void *v, register_t *retval)
225 {
226 struct sys_profil_args /* {
227 syscallarg(caddr_t) samples;
228 syscallarg(u_int) size;
229 syscallarg(u_int) offset;
230 syscallarg(u_int) scale;
231 } */ *uap = v;
232 struct proc *p = l->l_proc;
233 struct uprof *upp;
234
235 if (SCARG(uap, scale) > (1 << 16))
236 return (EINVAL);
237 if (SCARG(uap, scale) == 0) {
238 mutex_enter(&p->p_stmutex);
239 stopprofclock(p);
240 mutex_exit(&p->p_stmutex);
241 return (0);
242 }
243 upp = &p->p_stats->p_prof;
244
245 /* Block profile interrupts while changing state. */
246 mutex_enter(&p->p_stmutex);
247 upp->pr_off = SCARG(uap, offset);
248 upp->pr_scale = SCARG(uap, scale);
249 upp->pr_base = SCARG(uap, samples);
250 upp->pr_size = SCARG(uap, size);
251 startprofclock(p);
252 mutex_exit(&p->p_stmutex);
253
254 return (0);
255 }
256
257 /*
258 * Scale is a fixed-point number with the binary point 16 bits
259 * into the value, and is <= 1.0. pc is at most 32 bits, so the
260 * intermediate result is at most 48 bits.
261 */
262 #define PC_TO_INDEX(pc, prof) \
263 ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
264 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
265
266 /*
267 * Collect user-level profiling statistics; called on a profiling tick,
268 * when a process is running in user-mode. This routine may be called
269 * from an interrupt context. We try to update the user profiling buffers
270 * cheaply with fuswintr() and suswintr(). If that fails, we revert to
271 * an AST that will vector us to trap() with a context in which copyin
272 * and copyout will work. Trap will then call addupc_task().
273 *
274 * Note that we may (rarely) not get around to the AST soon enough, and
275 * lose profile ticks when the next tick overwrites this one, but in this
276 * case the system is overloaded and the profile is probably already
277 * inaccurate.
278 */
279 void
280 addupc_intr(struct lwp *l, u_long pc)
281 {
282 struct uprof *prof;
283 struct proc *p;
284 caddr_t addr;
285 u_int i;
286 int v;
287
288 p = l->l_proc;
289
290 LOCK_ASSERT(mutex_owned(&p->p_stmutex));
291
292 prof = &p->p_stats->p_prof;
293 if (pc < prof->pr_off ||
294 (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
295 return; /* out of range; ignore */
296
297 addr = prof->pr_base + i;
298 mutex_exit(&p->p_stmutex);
299 if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + 1) == -1) {
300 /* XXXSMP */
301 prof->pr_addr = pc;
302 prof->pr_ticks++;
303 cpu_need_proftick(l);
304 }
305 mutex_enter(&p->p_stmutex);
306 }
307
308 /*
309 * Much like before, but we can afford to take faults here. If the
310 * update fails, we simply turn off profiling.
311 */
312 void
313 addupc_task(struct lwp *l, u_long pc, u_int ticks)
314 {
315 struct uprof *prof;
316 struct proc *p;
317 caddr_t addr;
318 int error;
319 u_int i;
320 u_short v;
321
322 p = l->l_proc;
323
324 if (ticks == 0)
325 return;
326
327 mutex_enter(&p->p_stmutex);
328 prof = &p->p_stats->p_prof;
329
330 /* Testing P_PROFIL may be unnecessary, but is certainly safe. */
331 if ((p->p_stflag & PST_PROFIL) == 0 || pc < prof->pr_off ||
332 (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) {
333 mutex_exit(&p->p_stmutex);
334 return;
335 }
336
337 addr = prof->pr_base + i;
338 mutex_exit(&p->p_stmutex);
339 if ((error = copyin(addr, (caddr_t)&v, sizeof(v))) == 0) {
340 v += ticks;
341 error = copyout((caddr_t)&v, addr, sizeof(v));
342 }
343 if (error != 0) {
344 mutex_enter(&p->p_stmutex);
345 stopprofclock(p);
346 mutex_exit(&p->p_stmutex);
347 }
348 }
349