xref: /openbsd-src/sys/kern/subr_prof.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: subr_prof.c,v 1.26 2014/07/08 17:19:25 deraadt 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/kernel.h>
38 #include <sys/proc.h>
39 #include <sys/resourcevar.h>
40 #include <sys/mount.h>
41 #include <sys/sysctl.h>
42 #include <sys/syscallargs.h>
43 
44 
45 #ifdef GPROF
46 #include <sys/malloc.h>
47 #include <sys/gmon.h>
48 
49 /*
50  * Flag to prevent CPUs from executing the mcount() monitor function
51  * until we're sure they are in a sane state.
52  */
53 int gmoninit = 0;
54 
55 extern char etext[];
56 
57 void
58 kmstartup(void)
59 {
60 	CPU_INFO_ITERATOR cii;
61 	struct cpu_info *ci;
62 	struct gmonparam *p;
63 	u_long lowpc, highpc, textsize;
64 	u_long kcountsize, fromssize, tossize;
65 	long tolimit;
66 	char *cp;
67 	int size;
68 
69 	/*
70 	 * Round lowpc and highpc to multiples of the density we're using
71 	 * so the rest of the scaling (here and in gprof) stays in ints.
72 	 */
73 	lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
74 	highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
75 	textsize = highpc - lowpc;
76 	printf("Profiling kernel, textsize=%ld [%lx..%lx]\n",
77 	    textsize, lowpc, highpc);
78 	kcountsize = textsize / HISTFRACTION;
79 	fromssize = textsize / HASHFRACTION;
80 	tolimit = textsize * ARCDENSITY / 100;
81 	if (tolimit < MINARCS)
82 		tolimit = MINARCS;
83 	else if (tolimit > MAXARCS)
84 		tolimit = MAXARCS;
85 	tossize = tolimit * sizeof(struct tostruct);
86 	size = sizeof(*p) + kcountsize + fromssize + tossize;
87 
88 	/* Allocate and initialize one profiling buffer per CPU. */
89 	CPU_INFO_FOREACH(cii, ci) {
90 		cp = km_alloc(round_page(size), &kv_any, &kp_zero, &kd_nowait);
91 		if (cp == NULL) {
92 			printf("No memory for profiling.\n");
93 			return;
94 		}
95 
96 		p = (struct gmonparam *)cp;
97 		cp += sizeof(*p);
98 		p->tos = (struct tostruct *)cp;
99 		cp += tossize;
100 		p->kcount = (u_short *)cp;
101 		cp += kcountsize;
102 		p->froms = (u_short *)cp;
103 
104 		p->state = GMON_PROF_OFF;
105 		p->lowpc = lowpc;
106 		p->highpc = highpc;
107 		p->textsize = textsize;
108 		p->hashfraction = HASHFRACTION;
109 		p->kcountsize = kcountsize;
110 		p->fromssize = fromssize;
111 		p->tolimit = tolimit;
112 		p->tossize = tossize;
113 
114 		ci->ci_gmon = p;
115 	}
116 }
117 
118 /*
119  * Return kernel profiling information.
120  */
121 int
122 sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
123     size_t newlen)
124 {
125 	CPU_INFO_ITERATOR cii;
126 	struct cpu_info *ci;
127 	struct gmonparam *gp = NULL;
128 	int error, cpuid, op;
129 
130 	/* all sysctl names at this level are name and field */
131 	if (namelen != 2)
132 		return (ENOTDIR);		/* overloaded */
133 
134 	op = name[0];
135 	cpuid = name[1];
136 
137 	CPU_INFO_FOREACH(cii, ci) {
138 		if (cpuid == CPU_INFO_UNIT(ci)) {
139 			gp = ci->ci_gmon;
140 			break;
141 		}
142 	}
143 
144 	if (gp == NULL)
145 		return (EOPNOTSUPP);
146 
147 	/* Assume that if we're here it is safe to execute profiling. */
148 	gmoninit = 1;
149 
150 	switch (op) {
151 	case GPROF_STATE:
152 		error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
153 		if (error)
154 			return (error);
155 		if (gp->state == GMON_PROF_OFF)
156 			stopprofclock(&process0);
157 		else
158 			startprofclock(&process0);
159 		return (0);
160 	case GPROF_COUNT:
161 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
162 		    gp->kcount, gp->kcountsize));
163 	case GPROF_FROMS:
164 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
165 		    gp->froms, gp->fromssize));
166 	case GPROF_TOS:
167 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
168 		    gp->tos, gp->tossize));
169 	case GPROF_GMONPARAM:
170 		return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
171 	default:
172 		return (EOPNOTSUPP);
173 	}
174 	/* NOTREACHED */
175 }
176 #endif /* GPROF */
177 
178 /*
179  * Profiling system call.
180  *
181  * The scale factor is a fixed point number with 16 bits of fraction, so that
182  * 1.0 is represented as 0x10000.  A scale factor of 0 turns off profiling.
183  */
184 /* ARGSUSED */
185 int
186 sys_profil(struct proc *p, void *v, register_t *retval)
187 {
188 	struct sys_profil_args /* {
189 		syscallarg(caddr_t) samples;
190 		syscallarg(size_t) size;
191 		syscallarg(u_long) offset;
192 		syscallarg(u_int) scale;
193 	} */ *uap = v;
194 	struct process *pr = p->p_p;
195 	struct uprof *upp;
196 	int s;
197 
198 	if (SCARG(uap, scale) > (1 << 16))
199 		return (EINVAL);
200 	if (SCARG(uap, scale) == 0) {
201 		stopprofclock(pr);
202 		return (0);
203 	}
204 	upp = &pr->ps_prof;
205 
206 	/* Block profile interrupts while changing state. */
207 	s = splstatclock();
208 	upp->pr_off = SCARG(uap, offset);
209 	upp->pr_scale = SCARG(uap, scale);
210 	upp->pr_base = (caddr_t)SCARG(uap, samples);
211 	upp->pr_size = SCARG(uap, size);
212 	startprofclock(pr);
213 	splx(s);
214 
215 	return (0);
216 }
217 
218 /*
219  * Scale is a fixed-point number with the binary point 16 bits
220  * into the value, and is <= 1.0.  pc is at most 32 bits, so the
221  * intermediate result is at most 48 bits.
222  */
223 #define	PC_TO_INDEX(pc, prof) \
224 	((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
225 	    (u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
226 
227 /*
228  * Collect user-level profiling statistics; called on a profiling tick,
229  * when a process is running in user-mode.  This routine may be called
230  * from an interrupt context. Schedule an AST that will vector us to
231  * trap() with a context in which copyin and copyout will work.
232  * Trap will then call addupc_task().
233  */
234 void
235 addupc_intr(struct proc *p, u_long pc)
236 {
237 	struct uprof *prof;
238 
239 	prof = &p->p_p->ps_prof;
240 	if (pc < prof->pr_off || PC_TO_INDEX(pc, prof) >= prof->pr_size)
241 		return;			/* out of range; ignore */
242 
243 	p->p_prof_addr = pc;
244 	p->p_prof_ticks++;
245 	atomic_setbits_int(&p->p_flag, P_OWEUPC);
246 	need_proftick(p);
247 }
248 
249 
250 /*
251  * Much like before, but we can afford to take faults here.  If the
252  * update fails, we simply turn off profiling.
253  */
254 void
255 addupc_task(struct proc *p, u_long pc, u_int nticks)
256 {
257 	struct process *pr = p->p_p;
258 	struct uprof *prof;
259 	caddr_t addr;
260 	u_int i;
261 	u_short v;
262 
263 	/* Testing PS_PROFIL may be unnecessary, but is certainly safe. */
264 	if ((pr->ps_flags & PS_PROFIL) == 0 || nticks == 0)
265 		return;
266 
267 	prof = &pr->ps_prof;
268 	if (pc < prof->pr_off ||
269 	    (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size)
270 		return;
271 
272 	addr = prof->pr_base + i;
273 	if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) {
274 		v += nticks;
275 		if (copyout((caddr_t)&v, addr, sizeof(v)) == 0)
276 			return;
277 	}
278 	stopprofclock(pr);
279 }
280