1 /*- 2 * Copyright (c) 1982, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)subr_prof.c 8.4 (Berkeley) 02/14/95 8 */ 9 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 16 #include <sys/mount.h> 17 #include <sys/syscallargs.h> 18 19 #include <machine/cpu.h> 20 21 #ifdef GPROF 22 #include <sys/malloc.h> 23 #include <sys/gmon.h> 24 25 /* 26 * Froms is actually a bunch of unsigned shorts indexing tos 27 */ 28 struct gmonparam _gmonparam = { GMON_PROF_OFF }; 29 30 extern char etext[]; 31 32 void 33 kmstartup() 34 { 35 char *cp; 36 struct gmonparam *p = &_gmonparam; 37 /* 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. 40 */ 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; 44 printf("Profiling kernel, textsize=%d [%x..%x]\n", 45 p->textsize, p->lowpc, p->highpc); 46 p->kcountsize = p->textsize / HISTFRACTION; 47 p->hashfraction = HASHFRACTION; 48 p->fromssize = p->textsize / HASHFRACTION; 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; 54 p->tossize = p->tolimit * sizeof(struct tostruct); 55 cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize, 56 M_GPROF, M_NOWAIT); 57 if (cp == 0) { 58 printf("No memory for profiling.\n"); 59 return; 60 } 61 bzero(cp, p->kcountsize + p->tossize + p->fromssize); 62 p->tos = (struct tostruct *)cp; 63 cp += p->tossize; 64 p->kcount = (u_short *)cp; 65 cp += p->kcountsize; 66 p->froms = (u_short *)cp; 67 } 68 69 /* 70 * Return kernel profiling information. 71 */ 72 int 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; 82 int error; 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: 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); 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)); 107 case GPROF_GMONPARAM: 108 return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp)); 109 default: 110 return (EOPNOTSUPP); 111 } 112 /* NOTREACHED */ 113 } 114 #endif /* GPROF */ 115 116 /* 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. 121 */ 122 /* ARGSUSED */ 123 int 124 profil(p, uap, retval) 125 struct proc *p; 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; 133 { 134 register struct uprof *upp; 135 int s; 136 137 if (SCARG(uap, scale) > (1 << 16)) 138 return (EINVAL); 139 if (SCARG(uap, scale) == 0) { 140 stopprofclock(p); 141 return (0); 142 } 143 upp = &p->p_stats->p_prof; 144 145 /* Block profile interrupts while changing state. */ 146 s = splstatclock(); 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); 151 startprofclock(p); 152 splx(s); 153 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); 202 } 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 220 /* Testing P_PROFIL may be unnecessary, but is certainly safe. */ 221 if ((p->p_flag & P_PROFIL) == 0 || ticks == 0) 222 return; 223 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; 234 } 235 stopprofclock(p); 236 } 237