Commit | Line | Data |
---|---|---|
80c60f62 | 1 | /*- |
c34daa85 KB |
2 | * Copyright (c) 1982, 1986, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
da7c5cc6 | 4 | * |
2b75fa20 KM |
5 | * %sccs.include.redist.c% |
6 | * | |
144dbb59 | 7 | * @(#)subr_prof.c 8.4 (Berkeley) %G% |
da7c5cc6 | 8 | */ |
08621963 | 9 | |
80c60f62 CT |
10 | #include <sys/param.h> |
11 | #include <sys/systm.h> | |
12 | #include <sys/kernel.h> | |
13 | #include <sys/proc.h> | |
14 | #include <sys/user.h> | |
144dbb59 CD |
15 | |
16 | #include <sys/mount.h> | |
17 | #include <sys/syscallargs.h> | |
18 | ||
80c60f62 CT |
19 | #include <machine/cpu.h> |
20 | ||
3a3e0b91 | 21 | #ifdef GPROF |
80c60f62 CT |
22 | #include <sys/malloc.h> |
23 | #include <sys/gmon.h> | |
3a3e0b91 SL |
24 | |
25 | /* | |
26 | * Froms is actually a bunch of unsigned shorts indexing tos | |
27 | */ | |
80c60f62 CT |
28 | struct gmonparam _gmonparam = { GMON_PROF_OFF }; |
29 | ||
80c60f62 | 30 | extern char etext[]; |
3a3e0b91 | 31 | |
144dbb59 | 32 | void |
3a3e0b91 SL |
33 | kmstartup() |
34 | { | |
80c60f62 | 35 | char *cp; |
80c60f62 | 36 | struct gmonparam *p = &_gmonparam; |
08621963 | 37 | /* |
fb1db32c MK |
38 | * Round lowpc and highpc to multiples of the density we're using |
39 | * so the rest of the scaling (here and in gprof) stays in ints. | |
08621963 | 40 | */ |
80c60f62 CT |
41 | p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER)); |
42 | p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER)); | |
43 | p->textsize = p->highpc - p->lowpc; | |
80c60f62 CT |
44 | printf("Profiling kernel, textsize=%d [%x..%x]\n", |
45 | p->textsize, p->lowpc, p->highpc); | |
3875eec7 | 46 | p->kcountsize = p->textsize / HISTFRACTION; |
e8735845 | 47 | p->hashfraction = HASHFRACTION; |
3875eec7 | 48 | p->fromssize = p->textsize / HASHFRACTION; |
80c60f62 CT |
49 | p->tolimit = p->textsize * ARCDENSITY / 100; |
50 | if (p->tolimit < MINARCS) | |
51 | p->tolimit = MINARCS; | |
52 | else if (p->tolimit > MAXARCS) | |
53 | p->tolimit = MAXARCS; | |
3875eec7 KM |
54 | p->tossize = p->tolimit * sizeof(struct tostruct); |
55 | cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize, | |
56 | M_GPROF, M_NOWAIT); | |
80c60f62 CT |
57 | if (cp == 0) { |
58 | printf("No memory for profiling.\n"); | |
3a3e0b91 SL |
59 | return; |
60 | } | |
3875eec7 | 61 | bzero(cp, p->kcountsize + p->tossize + p->fromssize); |
80c60f62 | 62 | p->tos = (struct tostruct *)cp; |
3875eec7 KM |
63 | cp += p->tossize; |
64 | p->kcount = (u_short *)cp; | |
65 | cp += p->kcountsize; | |
80c60f62 | 66 | p->froms = (u_short *)cp; |
3a3e0b91 | 67 | } |
3875eec7 KM |
68 | |
69 | /* | |
70 | * Return kernel profiling information. | |
71 | */ | |
144dbb59 | 72 | int |
3875eec7 KM |
73 | sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen, p) |
74 | int *name; | |
75 | u_int namelen; | |
76 | void *oldp; | |
77 | size_t *oldlenp; | |
78 | void *newp; | |
79 | size_t newlen; | |
80 | { | |
81 | struct gmonparam *gp = &_gmonparam; | |
f5602a18 | 82 | int error; |
3875eec7 KM |
83 | |
84 | /* all sysctl names at this level are terminal */ | |
85 | if (namelen != 1) | |
86 | return (ENOTDIR); /* overloaded */ | |
87 | ||
88 | switch (name[0]) { | |
89 | case GPROF_STATE: | |
f5602a18 KM |
90 | error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state); |
91 | if (error) | |
92 | return (error); | |
93 | if (gp->state == GMON_PROF_OFF) | |
94 | stopprofclock(&proc0); | |
95 | else | |
96 | startprofclock(&proc0); | |
97 | return (0); | |
3875eec7 KM |
98 | case GPROF_COUNT: |
99 | return (sysctl_struct(oldp, oldlenp, newp, newlen, | |
100 | gp->kcount, gp->kcountsize)); | |
101 | case GPROF_FROMS: | |
102 | return (sysctl_struct(oldp, oldlenp, newp, newlen, | |
103 | gp->froms, gp->fromssize)); | |
104 | case GPROF_TOS: | |
105 | return (sysctl_struct(oldp, oldlenp, newp, newlen, | |
106 | gp->tos, gp->tossize)); | |
e8735845 KM |
107 | case GPROF_GMONPARAM: |
108 | return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp)); | |
3875eec7 KM |
109 | default: |
110 | return (EOPNOTSUPP); | |
111 | } | |
112 | /* NOTREACHED */ | |
113 | } | |
114 | #endif /* GPROF */ | |
3a3e0b91 SL |
115 | |
116 | /* | |
80c60f62 CT |
117 | * Profiling system call. |
118 | * | |
119 | * The scale factor is a fixed point number with 16 bits of fraction, so that | |
120 | * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling. | |
3a3e0b91 | 121 | */ |
80c60f62 | 122 | /* ARGSUSED */ |
144dbb59 | 123 | int |
80c60f62 CT |
124 | profil(p, uap, retval) |
125 | struct proc *p; | |
144dbb59 CD |
126 | register struct profil_args /* { |
127 | syscallarg(caddr_t) samples; | |
128 | syscallarg(u_int) size; | |
129 | syscallarg(u_int) offset; | |
130 | syscallarg(u_int) scale; | |
131 | } */ *uap; | |
132 | register_t *retval; | |
3a3e0b91 | 133 | { |
80c60f62 CT |
134 | register struct uprof *upp; |
135 | int s; | |
3a3e0b91 | 136 | |
144dbb59 | 137 | if (SCARG(uap, scale) > (1 << 16)) |
80c60f62 | 138 | return (EINVAL); |
144dbb59 | 139 | if (SCARG(uap, scale) == 0) { |
80c60f62 CT |
140 | stopprofclock(p); |
141 | return (0); | |
3a3e0b91 | 142 | } |
80c60f62 | 143 | upp = &p->p_stats->p_prof; |
cb84e0ab KB |
144 | |
145 | /* Block profile interrupts while changing state. */ | |
146 | s = splstatclock(); | |
144dbb59 CD |
147 | upp->pr_off = SCARG(uap, offset); |
148 | upp->pr_scale = SCARG(uap, scale); | |
149 | upp->pr_base = SCARG(uap, samples); | |
150 | upp->pr_size = SCARG(uap, size); | |
80c60f62 CT |
151 | startprofclock(p); |
152 | splx(s); | |
cb84e0ab | 153 | |
80c60f62 CT |
154 | return (0); |
155 | } | |
156 | ||
157 | /* | |
158 | * Scale is a fixed-point number with the binary point 16 bits | |
159 | * into the value, and is <= 1.0. pc is at most 32 bits, so the | |
160 | * intermediate result is at most 48 bits. | |
161 | */ | |
162 | #define PC_TO_INDEX(pc, prof) \ | |
163 | ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ | |
164 | (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) | |
165 | ||
166 | /* | |
167 | * Collect user-level profiling statistics; called on a profiling tick, | |
168 | * when a process is running in user-mode. This routine may be called | |
169 | * from an interrupt context. We try to update the user profiling buffers | |
170 | * cheaply with fuswintr() and suswintr(). If that fails, we revert to | |
171 | * an AST that will vector us to trap() with a context in which copyin | |
172 | * and copyout will work. Trap will then call addupc_task(). | |
173 | * | |
174 | * Note that we may (rarely) not get around to the AST soon enough, and | |
175 | * lose profile ticks when the next tick overwrites this one, but in this | |
176 | * case the system is overloaded and the profile is probably already | |
177 | * inaccurate. | |
178 | */ | |
179 | void | |
180 | addupc_intr(p, pc, ticks) | |
181 | register struct proc *p; | |
182 | register u_long pc; | |
183 | u_int ticks; | |
184 | { | |
185 | register struct uprof *prof; | |
186 | register caddr_t addr; | |
187 | register u_int i; | |
188 | register int v; | |
189 | ||
190 | if (ticks == 0) | |
191 | return; | |
192 | prof = &p->p_stats->p_prof; | |
193 | if (pc < prof->pr_off || | |
194 | (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) | |
195 | return; /* out of range; ignore */ | |
196 | ||
197 | addr = prof->pr_base + i; | |
198 | if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) { | |
199 | prof->pr_addr = pc; | |
200 | prof->pr_ticks = ticks; | |
201 | need_proftick(p); | |
08621963 | 202 | } |
80c60f62 CT |
203 | } |
204 | ||
205 | /* | |
206 | * Much like before, but we can afford to take faults here. If the | |
207 | * update fails, we simply turn off profiling. | |
208 | */ | |
209 | void | |
210 | addupc_task(p, pc, ticks) | |
211 | register struct proc *p; | |
212 | register u_long pc; | |
213 | u_int ticks; | |
214 | { | |
215 | register struct uprof *prof; | |
216 | register caddr_t addr; | |
217 | register u_int i; | |
218 | u_short v; | |
219 | ||
cf5ef508 KB |
220 | /* Testing P_PROFIL may be unnecessary, but is certainly safe. */ |
221 | if ((p->p_flag & P_PROFIL) == 0 || ticks == 0) | |
80c60f62 | 222 | return; |
08621963 | 223 | |
80c60f62 CT |
224 | prof = &p->p_stats->p_prof; |
225 | if (pc < prof->pr_off || | |
226 | (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) | |
227 | return; | |
228 | ||
229 | addr = prof->pr_base + i; | |
230 | if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) { | |
231 | v += ticks; | |
232 | if (copyout((caddr_t)&v, addr, sizeof(v)) == 0) | |
233 | return; | |
3a3e0b91 | 234 | } |
80c60f62 | 235 | stopprofclock(p); |
3a3e0b91 | 236 | } |