xref: /openbsd-src/sys/kern/subr_prof.c (revision de8cc8edbc71bd3e3bc7fbffa27ba0e564c37d8b)
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 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 	} */ *uap = v;
235 	struct process *pr = p->p_p;
236 	struct uprof *upp;
237 	int s;
238 
239 	if (SCARG(uap, scale) > (1 << 16))
240 		return (EINVAL);
241 	if (SCARG(uap, scale) == 0) {
242 		stopprofclock(pr);
243 		return (0);
244 	}
245 	upp = &pr->ps_prof;
246 
247 	/* Block profile interrupts while changing state. */
248 	s = splstatclock();
249 	upp->pr_off = SCARG(uap, offset);
250 	upp->pr_scale = SCARG(uap, scale);
251 	upp->pr_base = (caddr_t)SCARG(uap, samples);
252 	upp->pr_size = SCARG(uap, size);
253 	startprofclock(pr);
254 	splx(s);
255 
256 	return (0);
257 }
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 addupc_intr(struct proc *p, u_long pc)
277 {
278 	struct uprof *prof;
279 
280 	prof = &p->p_p->ps_prof;
281 	if (pc < prof->pr_off || PC_TO_INDEX(pc, prof) >= prof->pr_size)
282 		return;			/* out of range; ignore */
283 
284 	p->p_prof_addr = pc;
285 	p->p_prof_ticks++;
286 	atomic_setbits_int(&p->p_flag, P_OWEUPC);
287 	need_proftick(p);
288 }
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 addupc_task(struct proc *p, u_long pc, u_int nticks)
297 {
298 	struct process *pr = p->p_p;
299 	struct uprof *prof;
300 	caddr_t addr;
301 	u_int i;
302 	u_short v;
303 
304 	/* Testing PS_PROFIL may be unnecessary, but is certainly safe. */
305 	if ((pr->ps_flags & PS_PROFIL) == 0 || nticks == 0)
306 		return;
307 
308 	prof = &pr->ps_prof;
309 	if (pc < prof->pr_off ||
310 	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
311 		return;
312 
313 	addr = prof->pr_base + i;
314 	if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) {
315 		v += nticks;
316 		if (copyout((caddr_t)&v, addr, sizeof(v)) == 0)
317 			return;
318 	}
319 	stopprofclock(pr);
320 }
321