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 | * | |
3875eec7 | 7 | * @(#)subr_prof.c 7.18 (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 KM |
41 | p->kcountsize = p->textsize / HISTFRACTION; |
42 | p->fromssize = p->textsize / HASHFRACTION; | |
80c60f62 CT |
43 | p->tolimit = p->textsize * ARCDENSITY / 100; |
44 | if (p->tolimit < MINARCS) | |
45 | p->tolimit = MINARCS; | |
46 | else if (p->tolimit > MAXARCS) | |
47 | p->tolimit = MAXARCS; | |
3875eec7 KM |
48 | p->tossize = p->tolimit * sizeof(struct tostruct); |
49 | cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize, | |
50 | M_GPROF, M_NOWAIT); | |
80c60f62 CT |
51 | if (cp == 0) { |
52 | printf("No memory for profiling.\n"); | |
3a3e0b91 SL |
53 | return; |
54 | } | |
3875eec7 | 55 | bzero(cp, p->kcountsize + p->tossize + p->fromssize); |
80c60f62 | 56 | p->tos = (struct tostruct *)cp; |
3875eec7 KM |
57 | cp += p->tossize; |
58 | p->kcount = (u_short *)cp; | |
59 | cp += p->kcountsize; | |
80c60f62 | 60 | p->froms = (u_short *)cp; |
4ce99dbf | 61 | startprofclock(&proc0); |
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; | |
76 | ||
77 | /* all sysctl names at this level are terminal */ | |
78 | if (namelen != 1) | |
79 | return (ENOTDIR); /* overloaded */ | |
80 | ||
81 | switch (name[0]) { | |
82 | case GPROF_STATE: | |
83 | return (sysctl_int(oldp, oldlenp, newp, newlen, &gp->state)); | |
84 | case GPROF_COUNT: | |
85 | return (sysctl_struct(oldp, oldlenp, newp, newlen, | |
86 | gp->kcount, gp->kcountsize)); | |
87 | case GPROF_FROMS: | |
88 | return (sysctl_struct(oldp, oldlenp, newp, newlen, | |
89 | gp->froms, gp->fromssize)); | |
90 | case GPROF_TOS: | |
91 | return (sysctl_struct(oldp, oldlenp, newp, newlen, | |
92 | gp->tos, gp->tossize)); | |
93 | default: | |
94 | return (EOPNOTSUPP); | |
95 | } | |
96 | /* NOTREACHED */ | |
97 | } | |
98 | #endif /* GPROF */ | |
3a3e0b91 SL |
99 | |
100 | /* | |
80c60f62 CT |
101 | * Profiling system call. |
102 | * | |
103 | * The scale factor is a fixed point number with 16 bits of fraction, so that | |
104 | * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling. | |
3a3e0b91 | 105 | */ |
9e97623a CT |
106 | struct profil_args { |
107 | caddr_t buf; | |
108 | u_int bufsize; | |
109 | u_int offset; | |
110 | u_int scale; | |
111 | }; | |
80c60f62 CT |
112 | /* ARGSUSED */ |
113 | profil(p, uap, retval) | |
114 | struct proc *p; | |
9e97623a | 115 | register struct profil_args *uap; |
80c60f62 | 116 | int *retval; |
3a3e0b91 | 117 | { |
80c60f62 CT |
118 | register struct uprof *upp; |
119 | int s; | |
3a3e0b91 | 120 | |
80c60f62 CT |
121 | if (uap->scale > (1 << 16)) |
122 | return (EINVAL); | |
123 | if (uap->scale == 0) { | |
124 | stopprofclock(p); | |
125 | return (0); | |
3a3e0b91 | 126 | } |
80c60f62 CT |
127 | upp = &p->p_stats->p_prof; |
128 | s = splstatclock(); /* block profile interrupts while changing state */ | |
129 | upp->pr_base = uap->buf; | |
130 | upp->pr_size = uap->bufsize; | |
131 | upp->pr_off = uap->offset; | |
132 | upp->pr_scale = uap->scale; | |
133 | startprofclock(p); | |
134 | splx(s); | |
135 | return (0); | |
136 | } | |
137 | ||
138 | /* | |
139 | * Scale is a fixed-point number with the binary point 16 bits | |
140 | * into the value, and is <= 1.0. pc is at most 32 bits, so the | |
141 | * intermediate result is at most 48 bits. | |
142 | */ | |
143 | #define PC_TO_INDEX(pc, prof) \ | |
144 | ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ | |
145 | (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) | |
146 | ||
147 | /* | |
148 | * Collect user-level profiling statistics; called on a profiling tick, | |
149 | * when a process is running in user-mode. This routine may be called | |
150 | * from an interrupt context. We try to update the user profiling buffers | |
151 | * cheaply with fuswintr() and suswintr(). If that fails, we revert to | |
152 | * an AST that will vector us to trap() with a context in which copyin | |
153 | * and copyout will work. Trap will then call addupc_task(). | |
154 | * | |
155 | * Note that we may (rarely) not get around to the AST soon enough, and | |
156 | * lose profile ticks when the next tick overwrites this one, but in this | |
157 | * case the system is overloaded and the profile is probably already | |
158 | * inaccurate. | |
159 | */ | |
160 | void | |
161 | addupc_intr(p, pc, ticks) | |
162 | register struct proc *p; | |
163 | register u_long pc; | |
164 | u_int ticks; | |
165 | { | |
166 | register struct uprof *prof; | |
167 | register caddr_t addr; | |
168 | register u_int i; | |
169 | register int v; | |
170 | ||
171 | if (ticks == 0) | |
172 | return; | |
173 | prof = &p->p_stats->p_prof; | |
174 | if (pc < prof->pr_off || | |
175 | (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) | |
176 | return; /* out of range; ignore */ | |
177 | ||
178 | addr = prof->pr_base + i; | |
179 | if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) { | |
180 | prof->pr_addr = pc; | |
181 | prof->pr_ticks = ticks; | |
182 | need_proftick(p); | |
08621963 | 183 | } |
80c60f62 CT |
184 | } |
185 | ||
186 | /* | |
187 | * Much like before, but we can afford to take faults here. If the | |
188 | * update fails, we simply turn off profiling. | |
189 | */ | |
190 | void | |
191 | addupc_task(p, pc, ticks) | |
192 | register struct proc *p; | |
193 | register u_long pc; | |
194 | u_int ticks; | |
195 | { | |
196 | register struct uprof *prof; | |
197 | register caddr_t addr; | |
198 | register u_int i; | |
199 | u_short v; | |
200 | ||
201 | /* testing SPROFIL may be unnecessary, but is certainly safe */ | |
202 | if ((p->p_flag & SPROFIL) == 0 || ticks == 0) | |
203 | return; | |
08621963 | 204 | |
80c60f62 CT |
205 | prof = &p->p_stats->p_prof; |
206 | if (pc < prof->pr_off || | |
207 | (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) | |
208 | return; | |
209 | ||
210 | addr = prof->pr_base + i; | |
211 | if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) { | |
212 | v += ticks; | |
213 | if (copyout((caddr_t)&v, addr, sizeof(v)) == 0) | |
214 | return; | |
3a3e0b91 | 215 | } |
80c60f62 | 216 | stopprofclock(p); |
3a3e0b91 | 217 | } |