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 | * | |
9e97623a | 7 | * @(#)subr_prof.c 7.17 (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 | ||
3a3e0b91 | 26 | u_short *kcount; |
80c60f62 | 27 | extern char etext[]; |
3a3e0b91 SL |
28 | |
29 | kmstartup() | |
30 | { | |
80c60f62 CT |
31 | char *cp; |
32 | int fsize, tsize, ksize; | |
33 | struct gmonparam *p = &_gmonparam; | |
08621963 | 34 | /* |
fb1db32c MK |
35 | * Round lowpc and highpc to multiples of the density we're using |
36 | * so the rest of the scaling (here and in gprof) stays in ints. | |
08621963 | 37 | */ |
80c60f62 CT |
38 | p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER)); |
39 | p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER)); | |
40 | p->textsize = p->highpc - p->lowpc; | |
80c60f62 CT |
41 | printf("Profiling kernel, textsize=%d [%x..%x]\n", |
42 | p->textsize, p->lowpc, p->highpc); | |
43 | ksize = p->textsize / HISTFRACTION; | |
44 | fsize = p->textsize / HASHFRACTION; | |
45 | p->tolimit = p->textsize * ARCDENSITY / 100; | |
46 | if (p->tolimit < MINARCS) | |
47 | p->tolimit = MINARCS; | |
48 | else if (p->tolimit > MAXARCS) | |
49 | p->tolimit = MAXARCS; | |
50 | tsize = p->tolimit * sizeof(struct tostruct); | |
51 | cp = (char *)malloc(ksize + fsize + tsize, M_GPROF, M_NOWAIT); | |
52 | if (cp == 0) { | |
53 | printf("No memory for profiling.\n"); | |
3a3e0b91 SL |
54 | return; |
55 | } | |
80c60f62 CT |
56 | bzero(cp, ksize + tsize + fsize); |
57 | p->tos = (struct tostruct *)cp; | |
58 | cp += tsize; | |
59 | kcount = (u_short *)cp; | |
60 | cp += ksize; | |
61 | p->froms = (u_short *)cp; | |
4ce99dbf | 62 | startprofclock(&proc0); |
3a3e0b91 | 63 | } |
80c60f62 | 64 | #endif |
3a3e0b91 SL |
65 | |
66 | /* | |
80c60f62 CT |
67 | * Profiling system call. |
68 | * | |
69 | * The scale factor is a fixed point number with 16 bits of fraction, so that | |
70 | * 1.0 is represented as 0x10000. A scale factor of 0 turns off profiling. | |
3a3e0b91 | 71 | */ |
9e97623a CT |
72 | struct profil_args { |
73 | caddr_t buf; | |
74 | u_int bufsize; | |
75 | u_int offset; | |
76 | u_int scale; | |
77 | }; | |
80c60f62 CT |
78 | /* ARGSUSED */ |
79 | profil(p, uap, retval) | |
80 | struct proc *p; | |
9e97623a | 81 | register struct profil_args *uap; |
80c60f62 | 82 | int *retval; |
3a3e0b91 | 83 | { |
80c60f62 CT |
84 | register struct uprof *upp; |
85 | int s; | |
3a3e0b91 | 86 | |
80c60f62 CT |
87 | if (uap->scale > (1 << 16)) |
88 | return (EINVAL); | |
89 | if (uap->scale == 0) { | |
90 | stopprofclock(p); | |
91 | return (0); | |
3a3e0b91 | 92 | } |
80c60f62 CT |
93 | upp = &p->p_stats->p_prof; |
94 | s = splstatclock(); /* block profile interrupts while changing state */ | |
95 | upp->pr_base = uap->buf; | |
96 | upp->pr_size = uap->bufsize; | |
97 | upp->pr_off = uap->offset; | |
98 | upp->pr_scale = uap->scale; | |
99 | startprofclock(p); | |
100 | splx(s); | |
101 | return (0); | |
102 | } | |
103 | ||
104 | /* | |
105 | * Scale is a fixed-point number with the binary point 16 bits | |
106 | * into the value, and is <= 1.0. pc is at most 32 bits, so the | |
107 | * intermediate result is at most 48 bits. | |
108 | */ | |
109 | #define PC_TO_INDEX(pc, prof) \ | |
110 | ((int)(((u_quad_t)((pc) - (prof)->pr_off) * \ | |
111 | (u_quad_t)((prof)->pr_scale)) >> 16) & ~1) | |
112 | ||
113 | /* | |
114 | * Collect user-level profiling statistics; called on a profiling tick, | |
115 | * when a process is running in user-mode. This routine may be called | |
116 | * from an interrupt context. We try to update the user profiling buffers | |
117 | * cheaply with fuswintr() and suswintr(). If that fails, we revert to | |
118 | * an AST that will vector us to trap() with a context in which copyin | |
119 | * and copyout will work. Trap will then call addupc_task(). | |
120 | * | |
121 | * Note that we may (rarely) not get around to the AST soon enough, and | |
122 | * lose profile ticks when the next tick overwrites this one, but in this | |
123 | * case the system is overloaded and the profile is probably already | |
124 | * inaccurate. | |
125 | */ | |
126 | void | |
127 | addupc_intr(p, pc, ticks) | |
128 | register struct proc *p; | |
129 | register u_long pc; | |
130 | u_int ticks; | |
131 | { | |
132 | register struct uprof *prof; | |
133 | register caddr_t addr; | |
134 | register u_int i; | |
135 | register int v; | |
136 | ||
137 | if (ticks == 0) | |
138 | return; | |
139 | prof = &p->p_stats->p_prof; | |
140 | if (pc < prof->pr_off || | |
141 | (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) | |
142 | return; /* out of range; ignore */ | |
143 | ||
144 | addr = prof->pr_base + i; | |
145 | if ((v = fuswintr(addr)) == -1 || suswintr(addr, v + ticks) == -1) { | |
146 | prof->pr_addr = pc; | |
147 | prof->pr_ticks = ticks; | |
148 | need_proftick(p); | |
08621963 | 149 | } |
80c60f62 CT |
150 | } |
151 | ||
152 | /* | |
153 | * Much like before, but we can afford to take faults here. If the | |
154 | * update fails, we simply turn off profiling. | |
155 | */ | |
156 | void | |
157 | addupc_task(p, pc, ticks) | |
158 | register struct proc *p; | |
159 | register u_long pc; | |
160 | u_int ticks; | |
161 | { | |
162 | register struct uprof *prof; | |
163 | register caddr_t addr; | |
164 | register u_int i; | |
165 | u_short v; | |
166 | ||
167 | /* testing SPROFIL may be unnecessary, but is certainly safe */ | |
168 | if ((p->p_flag & SPROFIL) == 0 || ticks == 0) | |
169 | return; | |
08621963 | 170 | |
80c60f62 CT |
171 | prof = &p->p_stats->p_prof; |
172 | if (pc < prof->pr_off || | |
173 | (i = PC_TO_INDEX(pc, prof)) >= prof->pr_size) | |
174 | return; | |
175 | ||
176 | addr = prof->pr_base + i; | |
177 | if (copyin(addr, (caddr_t)&v, sizeof(v)) == 0) { | |
178 | v += ticks; | |
179 | if (copyout((caddr_t)&v, addr, sizeof(v)) == 0) | |
180 | return; | |
3a3e0b91 | 181 | } |
80c60f62 | 182 | stopprofclock(p); |
3a3e0b91 | 183 | } |