xref: /openbsd-src/sys/kern/subr_prof.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
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 }
317