Line data Source code
1 : /* $OpenBSD: subr_prof.c,v 1.30 2016/09/04 09:22:29 mpi 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 : #if !defined(GPROF) && defined(DDBPROF)
74 : db_prof_init();
75 : #endif
76 :
77 : /*
78 : * Round lowpc and highpc to multiples of the density we're using
79 : * so the rest of the scaling (here and in gprof) stays in ints.
80 : */
81 : lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
82 : highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
83 : textsize = highpc - lowpc;
84 : #ifdef GPROF
85 : printf("Profiling kernel, textsize=%ld [%lx..%lx]\n",
86 : textsize, lowpc, highpc);
87 : #endif
88 : kcountsize = textsize / HISTFRACTION;
89 : fromssize = textsize / HASHFRACTION;
90 : tolimit = textsize * ARCDENSITY / 100;
91 : if (tolimit < MINARCS)
92 : tolimit = MINARCS;
93 : else if (tolimit > MAXARCS)
94 : tolimit = MAXARCS;
95 : tossize = tolimit * sizeof(struct tostruct);
96 : size = sizeof(*p) + kcountsize + fromssize + tossize;
97 :
98 : /* Allocate and initialize one profiling buffer per CPU. */
99 : CPU_INFO_FOREACH(cii, ci) {
100 : cp = km_alloc(round_page(size), &kv_any, &kp_zero, &kd_nowait);
101 : if (cp == NULL) {
102 : printf("No memory for profiling.\n");
103 : return;
104 : }
105 :
106 : p = (struct gmonparam *)cp;
107 : cp += sizeof(*p);
108 : p->tos = (struct tostruct *)cp;
109 : cp += tossize;
110 : p->kcount = (u_short *)cp;
111 : cp += kcountsize;
112 : p->froms = (u_short *)cp;
113 :
114 : p->state = GMON_PROF_OFF;
115 : p->lowpc = lowpc;
116 : p->highpc = highpc;
117 : p->textsize = textsize;
118 : p->hashfraction = HASHFRACTION;
119 : p->kcountsize = kcountsize;
120 : p->fromssize = fromssize;
121 : p->tolimit = tolimit;
122 : p->tossize = tossize;
123 :
124 : ci->ci_gmon = p;
125 : }
126 : }
127 :
128 : int
129 : prof_state_toggle(struct gmonparam *gp, int oldstate)
130 : {
131 : int error = 0;
132 :
133 : if (gp->state == oldstate)
134 : return (0);
135 :
136 : switch (gp->state) {
137 : case GMON_PROF_ON:
138 : #if !defined(GPROF)
139 : /*
140 : * If this is not a profiling kernel, we need to patch
141 : * all symbols that can be instrummented.
142 : */
143 : error = db_prof_enable();
144 : #endif
145 : if (error == 0)
146 : startprofclock(&process0);
147 : break;
148 : default:
149 : error = EINVAL;
150 : gp->state = GMON_PROF_OFF;
151 : /* FALLTHROUGH */
152 : case GMON_PROF_OFF:
153 : stopprofclock(&process0);
154 : #if !defined(GPROF)
155 : db_prof_disable();
156 : #endif
157 : break;
158 : }
159 :
160 : return (error);
161 : }
162 :
163 : /*
164 : * Return kernel profiling information.
165 : */
166 : int
167 : sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
168 : size_t newlen)
169 : {
170 : CPU_INFO_ITERATOR cii;
171 : struct cpu_info *ci;
172 : struct gmonparam *gp = NULL;
173 : int error, cpuid, op, state;
174 :
175 : /* all sysctl names at this level are name and field */
176 : if (namelen != 2)
177 : return (ENOTDIR); /* overloaded */
178 :
179 : op = name[0];
180 : cpuid = name[1];
181 :
182 : CPU_INFO_FOREACH(cii, ci) {
183 : if (cpuid == CPU_INFO_UNIT(ci)) {
184 : gp = ci->ci_gmon;
185 : break;
186 : }
187 : }
188 :
189 : if (gp == NULL)
190 : return (EOPNOTSUPP);
191 :
192 : /* Assume that if we're here it is safe to execute profiling. */
193 : gmoninit = 1;
194 :
195 : switch (op) {
196 : case GPROF_STATE:
197 : state = gp->state;
198 : error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
199 : if (error)
200 : return (error);
201 : return (prof_state_toggle(gp, state));
202 : case GPROF_COUNT:
203 : return (sysctl_struct(oldp, oldlenp, newp, newlen,
204 : gp->kcount, gp->kcountsize));
205 : case GPROF_FROMS:
206 : return (sysctl_struct(oldp, oldlenp, newp, newlen,
207 : gp->froms, gp->fromssize));
208 : case GPROF_TOS:
209 : return (sysctl_struct(oldp, oldlenp, newp, newlen,
210 : gp->tos, gp->tossize));
211 : case GPROF_GMONPARAM:
212 : return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
213 : default:
214 : return (EOPNOTSUPP);
215 : }
216 : /* NOTREACHED */
217 : }
218 : #endif /* GPROF || DDBPROF */
219 :
220 : /*
221 : * Profiling system call.
222 : *
223 : * The scale factor is a fixed point number with 16 bits of fraction, so that
224 : * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling.
225 : */
226 : int
227 0 : sys_profil(struct proc *p, void *v, register_t *retval)
228 : {
229 : struct sys_profil_args /* {
230 : syscallarg(caddr_t) samples;
231 : syscallarg(size_t) size;
232 : syscallarg(u_long) offset;
233 : syscallarg(u_int) scale;
234 0 : } */ *uap = v;
235 0 : struct process *pr = p->p_p;
236 : struct uprof *upp;
237 : int s;
238 :
239 0 : if (SCARG(uap, scale) > (1 << 16))
240 0 : return (EINVAL);
241 0 : if (SCARG(uap, scale) == 0) {
242 0 : stopprofclock(pr);
243 0 : return (0);
244 : }
245 0 : upp = &pr->ps_prof;
246 :
247 : /* Block profile interrupts while changing state. */
248 0 : s = splstatclock();
249 0 : upp->pr_off = SCARG(uap, offset);
250 0 : upp->pr_scale = SCARG(uap, scale);
251 0 : upp->pr_base = (caddr_t)SCARG(uap, samples);
252 0 : upp->pr_size = SCARG(uap, size);
253 0 : startprofclock(pr);
254 0 : splx(s);
255 :
256 0 : return (0);
257 0 : }
258 :
259 : /*
260 : * Scale is a fixed-point number with the binary point 16 bits
261 : * into the value, and is <= 1.0. pc is at most 32 bits, so the
262 : * intermediate result is at most 48 bits.
263 : */
264 : #define PC_TO_INDEX(pc, prof) \
265 : ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
266 : (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
267 :
268 : /*
269 : * Collect user-level profiling statistics; called on a profiling tick,
270 : * when a process is running in user-mode. This routine may be called
271 : * from an interrupt context. Schedule an AST that will vector us to
272 : * trap() with a context in which copyin and copyout will work.
273 : * Trap will then call addupc_task().
274 : */
275 : void
276 0 : addupc_intr(struct proc *p, u_long pc)
277 : {
278 : struct uprof *prof;
279 :
280 0 : prof = &p->p_p->ps_prof;
281 0 : if (pc < prof->pr_off || PC_TO_INDEX(pc, prof) >= prof->pr_size)
282 0 : return; /* out of range; ignore */
283 :
284 0 : p->p_prof_addr = pc;
285 0 : p->p_prof_ticks++;
286 0 : atomic_setbits_int(&p->p_flag, P_OWEUPC);
287 0 : need_proftick(p);
288 0 : }
289 :
290 :
291 : /*
292 : * Much like before, but we can afford to take faults here. If the
293 : * update fails, we simply turn off profiling.
294 : */
295 : void
296 0 : addupc_task(struct proc *p, u_long pc, u_int nticks)
297 : {
298 0 : struct process *pr = p->p_p;
299 : struct uprof *prof;
300 : caddr_t addr;
301 : u_int i;
302 0 : u_short v;
303 :
304 : /* Testing PS_PROFIL may be unnecessary, but is certainly safe. */
305 0 : if ((pr->ps_flags & PS_PROFIL) == 0 || nticks == 0)
306 0 : return;
307 :
308 0 : prof = &pr->ps_prof;
309 0 : if (pc < prof->pr_off ||
310 0 : (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
311 0 : return;
312 :
313 0 : addr = prof->pr_base + i;
314 0 : if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) {
315 0 : v += nticks;
316 0 : if (copyout((caddr_t)&v, addr, sizeof(v)) == 0)
317 0 : return;
318 : }
319 0 : stopprofclock(pr);
320 0 : }
|