FreeBSD/Linux Kernel Cross Reference
sys/kern/subr_prof.c
1 /* $OpenBSD: subr_prof.c,v 1.31 2021/09/03 16:45:45 jasper Exp $ */
2 /* $NetBSD: subr_prof.c,v 1.12 1996/04/22 01:38:50 christos Exp $ */
3
4 /*-
5 * Copyright (c) 1982, 1986, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * @(#)subr_prof.c 8.3 (Berkeley) 9/23/93
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/proc.h>
38 #include <sys/resourcevar.h>
39 #include <sys/mount.h>
40 #include <sys/sysctl.h>
41 #include <sys/syscallargs.h>
42
43
44 #if defined(GPROF) || defined(DDBPROF)
45 #include <sys/malloc.h>
46 #include <sys/gmon.h>
47
48 #include <uvm/uvm_extern.h>
49
50 #include <machine/db_machdep.h>
51 #include <ddb/db_extern.h>
52
53 /*
54 * Flag to prevent CPUs from executing the mcount() monitor function
55 * until we're sure they are in a sane state.
56 */
57 int gmoninit = 0;
58
59 extern char etext[];
60
61 void
62 prof_init(void)
63 {
64 CPU_INFO_ITERATOR cii;
65 struct cpu_info *ci;
66 struct gmonparam *p;
67 u_long lowpc, highpc, textsize;
68 u_long kcountsize, fromssize, tossize;
69 long tolimit;
70 char *cp;
71 int size;
72
73 /*
74 * Round lowpc and highpc to multiples of the density we're using
75 * so the rest of the scaling (here and in gprof) stays in ints.
76 */
77 lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
78 highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
79 textsize = highpc - lowpc;
80 #ifdef GPROF
81 printf("Profiling kernel, textsize=%ld [%lx..%lx]\n",
82 textsize, lowpc, highpc);
83 #endif
84 kcountsize = textsize / HISTFRACTION;
85 fromssize = textsize / HASHFRACTION;
86 tolimit = textsize * ARCDENSITY / 100;
87 if (tolimit < MINARCS)
88 tolimit = MINARCS;
89 else if (tolimit > MAXARCS)
90 tolimit = MAXARCS;
91 tossize = tolimit * sizeof(struct tostruct);
92 size = sizeof(*p) + kcountsize + fromssize + tossize;
93
94 /* Allocate and initialize one profiling buffer per CPU. */
95 CPU_INFO_FOREACH(cii, ci) {
96 cp = km_alloc(round_page(size), &kv_any, &kp_zero, &kd_nowait);
97 if (cp == NULL) {
98 printf("No memory for profiling.\n");
99 return;
100 }
101
102 p = (struct gmonparam *)cp;
103 cp += sizeof(*p);
104 p->tos = (struct tostruct *)cp;
105 cp += tossize;
106 p->kcount = (u_short *)cp;
107 cp += kcountsize;
108 p->froms = (u_short *)cp;
109
110 p->state = GMON_PROF_OFF;
111 p->lowpc = lowpc;
112 p->highpc = highpc;
113 p->textsize = textsize;
114 p->hashfraction = HASHFRACTION;
115 p->kcountsize = kcountsize;
116 p->fromssize = fromssize;
117 p->tolimit = tolimit;
118 p->tossize = tossize;
119
120 ci->ci_gmon = p;
121 }
122 }
123
124 int
125 prof_state_toggle(struct gmonparam *gp, int oldstate)
126 {
127 int error = 0;
128
129 if (gp->state == oldstate)
130 return (0);
131
132 switch (gp->state) {
133 case GMON_PROF_ON:
134 #if !defined(GPROF)
135 /*
136 * If this is not a profiling kernel, we need to patch
137 * all symbols that can be instrummented.
138 */
139 error = db_prof_enable();
140 #endif
141 if (error == 0)
142 startprofclock(&process0);
143 break;
144 default:
145 error = EINVAL;
146 gp->state = GMON_PROF_OFF;
147 /* FALLTHROUGH */
148 case GMON_PROF_OFF:
149 stopprofclock(&process0);
150 #if !defined(GPROF)
151 db_prof_disable();
152 #endif
153 break;
154 }
155
156 return (error);
157 }
158
159 /*
160 * Return kernel profiling information.
161 */
162 int
163 sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
164 size_t newlen)
165 {
166 CPU_INFO_ITERATOR cii;
167 struct cpu_info *ci;
168 struct gmonparam *gp = NULL;
169 int error, cpuid, op, state;
170
171 /* all sysctl names at this level are name and field */
172 if (namelen != 2)
173 return (ENOTDIR); /* overloaded */
174
175 op = name[0];
176 cpuid = name[1];
177
178 CPU_INFO_FOREACH(cii, ci) {
179 if (cpuid == CPU_INFO_UNIT(ci)) {
180 gp = ci->ci_gmon;
181 break;
182 }
183 }
184
185 if (gp == NULL)
186 return (EOPNOTSUPP);
187
188 /* Assume that if we're here it is safe to execute profiling. */
189 gmoninit = 1;
190
191 switch (op) {
192 case GPROF_STATE:
193 state = gp->state;
194 error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
195 if (error)
196 return (error);
197 return (prof_state_toggle(gp, state));
198 case GPROF_COUNT:
199 return (sysctl_struct(oldp, oldlenp, newp, newlen,
200 gp->kcount, gp->kcountsize));
201 case GPROF_FROMS:
202 return (sysctl_struct(oldp, oldlenp, newp, newlen,
203 gp->froms, gp->fromssize));
204 case GPROF_TOS:
205 return (sysctl_struct(oldp, oldlenp, newp, newlen,
206 gp->tos, gp->tossize));
207 case GPROF_GMONPARAM:
208 return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
209 default:
210 return (EOPNOTSUPP);
211 }
212 /* NOTREACHED */
213 }
214 #endif /* GPROF || DDBPROF */
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 int
223 sys_profil(struct proc *p, void *v, register_t *retval)
224 {
225 struct sys_profil_args /* {
226 syscallarg(caddr_t) samples;
227 syscallarg(size_t) size;
228 syscallarg(u_long) offset;
229 syscallarg(u_int) scale;
230 } */ *uap = v;
231 struct process *pr = p->p_p;
232 struct uprof *upp;
233 int s;
234
235 if (SCARG(uap, scale) > (1 << 16))
236 return (EINVAL);
237 if (SCARG(uap, scale) == 0) {
238 stopprofclock(pr);
239 return (0);
240 }
241 upp = &pr->ps_prof;
242
243 /* Block profile interrupts while changing state. */
244 s = splstatclock();
245 upp->pr_off = SCARG(uap, offset);
246 upp->pr_scale = SCARG(uap, scale);
247 upp->pr_base = (caddr_t)SCARG(uap, samples);
248 upp->pr_size = SCARG(uap, size);
249 startprofclock(pr);
250 splx(s);
251
252 return (0);
253 }
254
255 /*
256 * Scale is a fixed-point number with the binary point 16 bits
257 * into the value, and is <= 1.0. pc is at most 32 bits, so the
258 * intermediate result is at most 48 bits.
259 */
260 #define PC_TO_INDEX(pc, prof) \
261 ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
262 (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
263
264 /*
265 * Collect user-level profiling statistics; called on a profiling tick,
266 * when a process is running in user-mode. This routine may be called
267 * from an interrupt context. Schedule an AST that will vector us to
268 * trap() with a context in which copyin and copyout will work.
269 * Trap will then call addupc_task().
270 */
271 void
272 addupc_intr(struct proc *p, u_long pc)
273 {
274 struct uprof *prof;
275
276 prof = &p->p_p->ps_prof;
277 if (pc < prof->pr_off || PC_TO_INDEX(pc, prof) >= prof->pr_size)
278 return; /* out of range; ignore */
279
280 p->p_prof_addr = pc;
281 p->p_prof_ticks++;
282 atomic_setbits_int(&p->p_flag, P_OWEUPC);
283 need_proftick(p);
284 }
285
286
287 /*
288 * Much like before, but we can afford to take faults here. If the
289 * update fails, we simply turn off profiling.
290 */
291 void
292 addupc_task(struct proc *p, u_long pc, u_int nticks)
293 {
294 struct process *pr = p->p_p;
295 struct uprof *prof;
296 caddr_t addr;
297 u_int i;
298 u_short v;
299
300 /* Testing PS_PROFIL may be unnecessary, but is certainly safe. */
301 if ((pr->ps_flags & PS_PROFIL) == 0 || nticks == 0)
302 return;
303
304 prof = &pr->ps_prof;
305 if (pc < prof->pr_off ||
306 (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
307 return;
308
309 addr = prof->pr_base + i;
310 if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) {
311 v += nticks;
312 if (copyout((caddr_t)&v, addr, sizeof(v)) == 0)
313 return;
314 }
315 stopprofclock(pr);
316 }
Cache object: e7cef23dc1f7dd67835a39d0ef5ad815
|