#ifdef GPROF
#include <libkern/kernel_mach_header.h>
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc_internal.h>
#include <sys/user.h>
#include <machine/spl.h>
#include <machine/machine_routines.h>
#include <sys/mount_internal.h>
#include <sys/sysproto.h>
#include <mach/mach_types.h>
#include <kern/kern_types.h>
#include <kern/cpu_number.h>
#include <kern/kalloc.h>
#ifdef GPROF
#include <sys/malloc.h>
#include <sys/gmon.h>
extern int sysctl_doprof(int *, u_int, user_addr_t, size_t *,
user_addr_t, size_t newlen);
extern int sysctl_struct(user_addr_t, size_t *,
user_addr_t, size_t, void *, int);
lck_spin_t * mcount_lock;
lck_grp_t * mcount_lock_grp;
lck_attr_t * mcount_lock_attr;
struct gmonparam _gmonparam = { .state = GMON_PROF_OFF };
void
kmstartup(void)
{
tostruct_t *cp;
kernel_segment_command_t *sgp;
struct gmonparam *p = &_gmonparam;
sgp = getsegbyname("__TEXT");
p->lowpc = (u_int32_t)sgp->vmaddr;
p->highpc = (u_int32_t)(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=%lu [0x%016lx..0x%016lx]\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(tostruct_t);
cp = (tostruct_t *)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 = cp;
cp = (tostruct_t *)((vm_offset_t)cp + p->tossize);
p->kcount = (u_short *)cp;
cp = (tostruct_t *)((vm_offset_t)cp + p->kcountsize);
p->froms = (u_short *)cp;
mcount_lock_grp = lck_grp_alloc_init("MCOUNT", LCK_GRP_ATTR_NULL);
mcount_lock_attr = lck_attr_alloc_init();
mcount_lock = lck_spin_alloc_init(mcount_lock_grp, mcount_lock_attr);
}
STATIC int
sysctl_doprofhandle SYSCTL_HANDLER_ARGS
{
sysctl_doprof(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
user_addr_t newp, size_t newlen)
{
__unused int cmd = oidp->oid_arg2;
int *name = arg1;
int namelen = arg2;
user_addr_t oldp = req->oldptr;
size_t *oldlenp = req->oldlen;
user_addr_t newp = req->newptr;
size_t newlen = req->newlen;
struct gmonparam *gp = &_gmonparam;
int error = 0;
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case GPROF_STATE:
error = sysctl_int(oldp, oldlenp, newp, newlen, &gp->state);
if (error)
break;
if (gp->state == GMON_PROF_OFF)
stopprofclock(kernproc);
else
startprofclock(kernproc);
break;
case GPROF_COUNT:
error = sysctl_struct(oldp, oldlenp, newp, newlen,
gp->kcount, gp->kcountsize);
break;
case GPROF_FROMS:
error = sysctl_struct(oldp, oldlenp, newp, newlen,
gp->froms, gp->fromssize);
break;
case GPROF_TOS:
error = sysctl_struct(oldp, oldlenp, newp, newlen,
gp->tos, gp->tossize);
break;
case GPROF_GMONPARAM:
error = sysctl_rdstruct(oldp, oldlenp, newp, gp, sizeof *gp);
break;
default:
error = ENOTSUP;
break;
}
if (!error)
req->oldidx += req->oldlen;
return(error);
}
SYSCTL_PROC(_kern, KERN_PROF, prof, STLFLAG_NODE|CTLFLAG_RW | CTLFLAG_LOCKED,
0,
0,
sysctl_doprofhandle,
NULL,
"");
void
mcount(
uintptr_t frompc,
uintptr_t selfpc
)
{
unsigned short *frompcindex;
tostruct_t *top, *prevtop;
struct gmonparam *p = &_gmonparam;
long toindex;
if (p->state != GMON_PROF_ON)
return;
lck_spin_lock(mcount_lock);
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:
lck_spin_unlock(mcount_lock);
return;
overflow:
p->state = GMON_PROF_ERROR;
lck_spin_unlock(mcount_lock);
printf("mcount: tos overflow\n");
return;
}
#endif
#define PROFILE_LOCK(x)
#define PROFILE_UNLOCK(x)
int
profil(struct proc *p, struct profil_args *uap, int32_t *retval)
{
void *tmp;
tmp = p;
tmp = uap;
tmp = retval;
return EINVAL;
}
int
add_profil(struct proc *p, struct add_profil_args *uap, int32_t *retval)
{
void *tmp;
tmp = p;
tmp = uap;
tmp = retval;
return EINVAL;
}
#define PC_TO_INDEX(pc, prof) \
((user_addr_t)(((u_quad_t)((pc) - (prof)->pr_off) * \
(u_quad_t)((prof)->pr_scale)) >> 16) & ~1)
void
addupc_task(struct proc *p, user_addr_t pc, u_int ticks)
{
user_addr_t off;
u_short count;
if ((p->p_flag & P_PROFIL) == 0 || ticks == 0)
return;
if (proc_is64bit(p)) {
struct user_uprof *prof;
user_addr_t cell;
for (prof = &p->p_stats->user_p_prof; prof; prof = prof->pr_next) {
off = PC_TO_INDEX(pc, prof);
cell = (prof->pr_base + off);
if (cell >= prof->pr_base &&
cell < (prof->pr_size + prof->pr_base)) {
if (copyin(cell, (caddr_t) &count, sizeof(count)) == 0) {
count += ticks;
if(copyout((caddr_t) &count, cell, sizeof(count)) == 0)
return;
}
p->p_stats->user_p_prof.pr_scale = 0;
stopprofclock(p);
break;
}
}
}
else {
struct uprof *prof;
short *cell;
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 + prof->pr_base)) {
if (copyin(CAST_USER_ADDR_T(cell), (caddr_t) &count, sizeof(count)) == 0) {
count += ticks;
if(copyout((caddr_t) &count, CAST_USER_ADDR_T(cell), sizeof(count)) == 0)
return;
}
p->p_stats->p_prof.pr_scale = 0;
stopprofclock(p);
break;
}
}
}
}