1 /*- 2 * Copyright (c) 1982, 1986 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 * 7 * @(#)subr_prof.c 7.20 (Berkeley) 05/10/93 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 #include <machine/cpu.h> 16 17 #ifdef GPROF 18 #include <sys/malloc.h> 19 #include <sys/gmon.h> 20 21 /* 22 * Froms is actually a bunch of unsigned shorts indexing tos 23 */ 24 struct gmonparam _gmonparam = { GMON_PROF_OFF }; 25 26 extern char etext[]; 27 28 kmstartup() 29 { 30 char *cp; 31 struct gmonparam *p = &_gmonparam; 32 /* 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. 35 */ 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; 39 printf("Profiling kernel, textsize=%d [%x..%x]\n", 40 p->textsize, p->lowpc, p->highpc); 41 p->kcountsize = p->textsize / HISTFRACTION; 42 p->hashfraction = HASHFRACTION; 43 p->fromssize = p->textsize / HASHFRACTION; 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; 49 p->tossize = p->tolimit * sizeof(struct tostruct); 50 cp = (char *)malloc(p->kcountsize + p->fromssize + p->tossize, 51 M_GPROF, M_NOWAIT); 52 if (cp == 0) { 53 printf("No memory for profiling.\n"); 54 return; 55 } 56 bzero(cp, p->kcountsize + p->tossize + p->fromssize); 57 p->tos = (struct tostruct *)cp; 58 cp += p->tossize; 59 p->kcount = (u_short *)cp; 60 cp += p->kcountsize; 61 p->froms = (u_short *)cp; 62 } 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 int error; 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: 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); 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)); 101 case GPROF_GMONPARAM: 102 return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp)); 103 default: 104 return (EOPNOTSUPP); 105 } 106 /* NOTREACHED */ 107 } 108 #endif /* GPROF */ 109 110 /* 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. 115 */ 116 struct profil_args { 117 caddr_t buf; 118 u_int bufsize; 119 u_int offset; 120 u_int scale; 121 }; 122 /* ARGSUSED */ 123 profil(p, uap, retval) 124 struct proc *p; 125 register struct profil_args *uap; 126 int *retval; 127 { 128 register struct uprof *upp; 129 int s; 130 131 if (uap->scale > (1 << 16)) 132 return (EINVAL); 133 if (uap->scale == 0) { 134 stopprofclock(p); 135 return (0); 136 } 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); 193 } 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; 214 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; 225 } 226 stopprofclock(p); 227 } 228