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