#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <machine/spl.h>
#include <sys/mount.h>
#include <kern/cpu_number.h>
#ifdef GPROF
#include <sys/malloc.h>
#include <sys/gmon.h>
#include <kern/mach_header.h>
#include <machine/profile.h>
struct gmonparam _gmonparam = { GMON_PROF_OFF };
kmstartup()
{
char *cp;
u_long fromssize, tossize;
struct segment_command *sgp;
struct gmonparam *p = &_gmonparam;
sgp = getsegbyname("__TEXT");
p->lowpc = (u_long)sgp->vmaddr;
p->highpc = (u_long)(sgp->vmaddr + sgp->vmsize);
p->lowpc = ROUNDDOWN(p->lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
p->highpc = ROUNDUP(p->highpc, HISTFRACTION * sizeof(HISTCOUNTER));
p->textsize = p->highpc - p->lowpc;
printf("Profiling kernel, textsize=%d [0x%08x..0x%08x]\n",
p->textsize, p->lowpc, p->highpc);
p->kcountsize = p->textsize / HISTFRACTION;
p->hashfraction = HASHFRACTION;
p->fromssize = p->textsize / HASHFRACTION;
p->tolimit = p->textsize * ARCDENSITY / 100;
if (p->tolimit < MINARCS)
p->tolimit = MINARCS;
else if (p->tolimit > MAXARCS)
p->tolimit = MAXARCS;
p->tossize = p->tolimit * sizeof(struct tostruct);
cp = (char *)kalloc(p->kcountsize + p->fromssize + p->tossize);
if (cp == 0) {
printf("No memory for profiling.\n");
return;
}
bzero(cp, p->kcountsize + p->tossize + p->fromssize);
p->tos = (struct tostruct *)cp;
cp += p->tossize;
p->kcount = (u_short *)cp;
cp += p->kcountsize;
p->froms = (u_short *)cp;
}
sysctl_doprof(name, namelen, oldp, oldlenp, newp, newlen, p)
int *name;
u_int namelen;
void *oldp;
size_t *oldlenp;
void *newp;
size_t newlen;
{
struct gmonparam *gp = &_gmonparam;
int error;
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case GPROF_STATE:
error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
if (error)
return (error);
if (gp->state == GMON_PROF_OFF)
stopprofclock(kernproc);
else
startprofclock(kernproc);
return (0);
case GPROF_COUNT:
return (sysctl_struct(oldp, oldlenp, newp, newlen,
gp->kcount, gp->kcountsize));
case GPROF_FROMS:
return (sysctl_struct(oldp, oldlenp, newp, newlen,
gp->froms, gp->fromssize));
case GPROF_TOS:
return (sysctl_struct(oldp, oldlenp, newp, newlen,
gp->tos, gp->tossize));
case GPROF_GMONPARAM:
return (sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp));
default:
return (EOPNOTSUPP);
}
}
void
mcount(
register u_long frompc,
register u_long selfpc
)
{
unsigned short *frompcindex;
register struct tostruct *top, *prevtop;
struct gmonparam *p = &_gmonparam;
register long toindex;
MCOUNT_INIT;
if (p->state != GMON_PROF_ON)
return;
MCOUNT_ENTER;
frompc -= p->lowpc;
if (frompc > p->textsize)
goto done;
frompcindex = &p->froms[frompc / (p->hashfraction * sizeof(*p->froms))];
toindex = *frompcindex;
if (toindex == 0) {
toindex = ++p->tos[0].link;
if (toindex >= p->tolimit) {
goto overflow;
}
*frompcindex = toindex;
top = &p->tos[toindex];
top->selfpc = selfpc;
top->count = 1;
top->link = 0;
goto done;
}
top = &p->tos[toindex];
if (top->selfpc == selfpc) {
top->count++;
goto done;
}
for (; ; ) {
if (top->link == 0) {
toindex = ++p->tos[0].link;
if (toindex >= p->tolimit) {
goto overflow;
}
top = &p->tos[toindex];
top->selfpc = selfpc;
top->count = 1;
top->link = *frompcindex;
*frompcindex = toindex;
goto done;
}
prevtop = top;
top = &p->tos[top->link];
if (top->selfpc == selfpc) {
top->count++;
toindex = prevtop->link;
prevtop->link = top->link;
top->link = *frompcindex;
*frompcindex = toindex;
goto done;
}
}
done:
MCOUNT_EXIT;
return;
overflow:
p->state = GMON_PROF_ERROR;
MCOUNT_EXIT;
printf("mcount: tos overflow\n");
return;
}
#endif
#if NCPUS > 1
#define PROFILE_LOCK(x) simple_lock(x)
#define PROFILE_UNLOCK(x) simple_unlock(x)
#else
#define PROFILE_LOCK(x)
#define PROFILE_UNLOCK(x)
#endif
struct profil_args {
short *bufbase;
u_int bufsize;
u_int pcoffset;
u_int pcscale;
};
int
profil(p, uap, retval)
struct proc *p;
register struct profil_args *uap;
register_t *retval;
{
register struct uprof *upp = &p->p_stats->p_prof;
struct uprof *upc, *nupc;
int s;
if (uap->pcscale > (1 << 16))
return (EINVAL);
if (uap->pcscale == 0) {
stopprofclock(p);
return (0);
}
s = splstatclock();
PROFILE_LOCK(&upp->pr_lock);
upp->pr_base = (caddr_t)uap->bufbase;
upp->pr_size = uap->bufsize;
upp->pr_off = uap->pcoffset;
upp->pr_scale = uap->pcscale;
for (upc = upp->pr_next; upc; upc = nupc) {
nupc = upc->pr_next;
kfree(upc, sizeof (struct uprof));
}
upp->pr_next = 0;
PROFILE_UNLOCK(&upp->pr_lock);
startprofclock(p);
splx(s);
return(0);
}
struct add_profile_args {
short *bufbase;
u_int bufsize;
u_int pcoffset;
u_int pcscale;
};
int
add_profil(p, uap, retval)
struct proc *p;
register struct add_profile_args *uap;
register_t *retval;
{
struct uprof *upp = &p->p_stats->p_prof, *upc;
int s;
if (upp->pr_scale == 0)
return (0);
s = splstatclock();
upc = (struct uprof *) kalloc(sizeof (struct uprof));
upc->pr_base = (caddr_t)uap->bufbase;
upc->pr_size = uap->bufsize;
upc->pr_off = uap->pcoffset;
upc->pr_scale = uap->pcscale;
PROFILE_LOCK(&upp->pr_lock);
upc->pr_next = upp->pr_next;
upp->pr_next = upc;
PROFILE_UNLOCK(&upp->pr_lock);
splx(s);
return(0);
}
#define PC_TO_INDEX(pc, prof) \
((int)(((u_quad_t)((pc) - (prof)->pr_off) * \
(u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
void
addupc_task(p, pc, ticks)
register struct proc *p;
register u_long pc;
u_int ticks;
{
register struct uprof *prof;
register short *cell;
register u_int off;
u_short count;
if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
return;
for (prof = &p->p_stats->p_prof; prof; prof = prof->pr_next) {
off = PC_TO_INDEX(pc,prof);
cell = (short *)(prof->pr_base + off);
if (cell >= (short *)prof->pr_base &&
cell < (short*)(prof->pr_size + (int) prof->pr_base)) {
if (copyin((caddr_t)cell, (caddr_t) &count, sizeof(count)) == 0) {
count += ticks;
if(copyout((caddr_t) &count, (caddr_t)cell, sizeof(count)) == 0)
return;
}
p->p_stats->p_prof.pr_scale = 0;
stopprofclock(p);
break;
}
}
}