xref: /openbsd-src/sys/kern/subr_prof.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: subr_prof.c,v 1.29 2015/12/05 10:11:53 tedu 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 #ifdef GPROF
45 #include <sys/malloc.h>
46 #include <sys/gmon.h>
47 
48 #include <uvm/uvm_extern.h>
49 
50 /*
51  * Flag to prevent CPUs from executing the mcount() monitor function
52  * until we're sure they are in a sane state.
53  */
54 int gmoninit = 0;
55 
56 extern char etext[];
57 
58 void
59 kmstartup(void)
60 {
61 	CPU_INFO_ITERATOR cii;
62 	struct cpu_info *ci;
63 	struct gmonparam *p;
64 	u_long lowpc, highpc, textsize;
65 	u_long kcountsize, fromssize, tossize;
66 	long tolimit;
67 	char *cp;
68 	int size;
69 
70 	/*
71 	 * Round lowpc and highpc to multiples of the density we're using
72 	 * so the rest of the scaling (here and in gprof) stays in ints.
73 	 */
74 	lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER));
75 	highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER));
76 	textsize = highpc - lowpc;
77 	printf("Profiling kernel, textsize=%ld [%lx..%lx]\n",
78 	    textsize, lowpc, highpc);
79 	kcountsize = textsize / HISTFRACTION;
80 	fromssize = textsize / HASHFRACTION;
81 	tolimit = textsize * ARCDENSITY / 100;
82 	if (tolimit < MINARCS)
83 		tolimit = MINARCS;
84 	else if (tolimit > MAXARCS)
85 		tolimit = MAXARCS;
86 	tossize = tolimit * sizeof(struct tostruct);
87 	size = sizeof(*p) + kcountsize + fromssize + tossize;
88 
89 	/* Allocate and initialize one profiling buffer per CPU. */
90 	CPU_INFO_FOREACH(cii, ci) {
91 		cp = km_alloc(round_page(size), &kv_any, &kp_zero, &kd_nowait);
92 		if (cp == NULL) {
93 			printf("No memory for profiling.\n");
94 			return;
95 		}
96 
97 		p = (struct gmonparam *)cp;
98 		cp += sizeof(*p);
99 		p->tos = (struct tostruct *)cp;
100 		cp += tossize;
101 		p->kcount = (u_short *)cp;
102 		cp += kcountsize;
103 		p->froms = (u_short *)cp;
104 
105 		p->state = GMON_PROF_OFF;
106 		p->lowpc = lowpc;
107 		p->highpc = highpc;
108 		p->textsize = textsize;
109 		p->hashfraction = HASHFRACTION;
110 		p->kcountsize = kcountsize;
111 		p->fromssize = fromssize;
112 		p->tolimit = tolimit;
113 		p->tossize = tossize;
114 
115 		ci->ci_gmon = p;
116 	}
117 }
118 
119 /*
120  * Return kernel profiling information.
121  */
122 int
123 sysctl_doprof(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
124     size_t newlen)
125 {
126 	CPU_INFO_ITERATOR cii;
127 	struct cpu_info *ci;
128 	struct gmonparam *gp = NULL;
129 	int error, cpuid, op;
130 
131 	/* all sysctl names at this level are name and field */
132 	if (namelen != 2)
133 		return (ENOTDIR);		/* overloaded */
134 
135 	op = name[0];
136 	cpuid = name[1];
137 
138 	CPU_INFO_FOREACH(cii, ci) {
139 		if (cpuid == CPU_INFO_UNIT(ci)) {
140 			gp = ci->ci_gmon;
141 			break;
142 		}
143 	}
144 
145 	if (gp == NULL)
146 		return (EOPNOTSUPP);
147 
148 	/* Assume that if we're here it is safe to execute profiling. */
149 	gmoninit = 1;
150 
151 	switch (op) {
152 	case GPROF_STATE:
153 		error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
154 		if (error)
155 			return (error);
156 		if (gp->state == GMON_PROF_OFF)
157 			stopprofclock(&process0);
158 		else
159 			startprofclock(&process0);
160 		return (0);
161 	case GPROF_COUNT:
162 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
163 		    gp->kcount, gp->kcountsize));
164 	case GPROF_FROMS:
165 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
166 		    gp->froms, gp->fromssize));
167 	case GPROF_TOS:
168 		return (sysctl_struct(oldp, oldlenp, newp, newlen,
169 		    gp->tos, gp->tossize));
170 	case GPROF_GMONPARAM:
171 		return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
172 	default:
173 		return (EOPNOTSUPP);
174 	}
175 	/* NOTREACHED */
176 }
177 #endif /* GPROF */
178 
179 /*
180  * Profiling system call.
181  *
182  * The scale factor is a fixed point number with 16 bits of fraction, so that
183  * 1.0 is represented as 0x10000.  A scale factor of 0 turns off profiling.
184  */
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