#if defined(PROFILE)
#error This module cannot be compiled with profiling
#endif
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)gmon.c 5.2 (Berkeley) 6/21/85";
#endif
#define SCALE_1_TO_1 0x10000L
#define MSG "No space for monitor buffer(s)\n"
#include <stdio.h>
#include <libc.h>
#include <monitor.h>
#include <sys/types.h>
#include <sys/gmon.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
#include <mach-o/getsect.h>
extern void moninit(
void);
extern void monaddition(
char *lowpc,
char *highpc);
extern void moncount(
char *frompc,
char *selfpc);
extern void monreset(
void);
extern void monoutput(
const char *filename);
static char profiling = -1;
static char init = 0;
static unsigned long order = 0;
typedef struct {
char *lowpc;
char *highpc;
unsigned long textsize;
unsigned short *froms;
tostruct_t *tos;
long tolimit;
char *sbuf;
long ssiz;
long scale;
} mon_t;
static mon_t *mon = NULL;
static unsigned long nmon = 0;
static void monsetup(
mon_t *m,
char *lowpc,
char *highpc);
static long getprofhz(
void);
void
moninit(
void)
{
#ifndef __LP64__
const struct section *section;
#else
const struct section_64 *section;
#endif
char *lowpc, *highpc;
unsigned long i;
monreset();
init = 1;
section = getsectbyname("__TEXT", "__text");
lowpc = (char *)section->addr,
highpc = (char *)(section->addr + section->size);
if(mon == NULL){
if((mon = malloc(sizeof(mon_t))) == NULL){
write(2, MSG, sizeof(MSG) - 1);
return;
}
nmon = 1;
memset(mon, '\0', sizeof(mon_t));
}
monsetup(mon, lowpc, highpc);
profil(mon->sbuf + sizeof(gmonhdr_t),
mon->ssiz - sizeof(gmonhdr_t),
(u_long)mon->lowpc, mon->scale);
for(i = 1; i < nmon; i++)
add_profil(mon[i].sbuf + sizeof(gmonhdr_t),
mon[i].ssiz - sizeof(gmonhdr_t),
(u_long)mon[i].lowpc, mon[i].scale);
init = 0;
profiling = 0;
#if defined(__DYNAMIC__)
_dyld_moninit(monaddition);
#endif
}
void
monstartup(
char *lowpc,
char *highpc)
{
monreset();
if(mon == NULL){
if((mon = malloc(sizeof(mon_t))) == NULL){
write(2, MSG, sizeof(MSG) - 1);
return;
}
nmon = 1;
memset(mon, '\0', sizeof(mon_t));
}
monsetup(mon, lowpc, highpc);
}
void
monaddition(
char *lowpc,
char *highpc)
{
char save_profiling;
mon_t *m;
if(mon == NULL){
monstartup(lowpc, highpc);
return;
}
save_profiling = profiling;
profiling = -1;
if((mon = realloc(mon, (nmon + 1) * sizeof(mon_t))) == NULL){
write(2, MSG, sizeof(MSG) - 1);
return;
}
m = mon + nmon;
memset(m, '\0', sizeof(mon_t));
nmon++;
monsetup(m, lowpc, highpc);
profiling = save_profiling;
}
static
void
monsetup(
mon_t *m,
char *lowpc,
char *highpc)
{
long monsize;
char *buffer;
kern_return_t ret;
gmonhdr_t *p;
uintptr_t o;
lowpc = (char *)ROUNDDOWN((uintptr_t)lowpc,
HISTFRACTION * sizeof(HISTCOUNTER));
m->lowpc = lowpc;
highpc = (char *)ROUNDUP((uintptr_t)highpc,
HISTFRACTION * sizeof(HISTCOUNTER));
m->highpc = highpc;
if(m->froms)
vm_deallocate(mach_task_self(),
(vm_address_t)m->froms,
(vm_size_t)(m->textsize / HASHFRACTION));
m->textsize = highpc - lowpc;
ret = vm_allocate(mach_task_self(),
(vm_address_t *)&m->froms,
(vm_size_t)(m->textsize / HASHFRACTION),
TRUE);
if(ret != KERN_SUCCESS){
write(2, MSG, sizeof(MSG) - 1);
m->froms = 0;
return;
}
if(m->sbuf)
vm_deallocate(mach_task_self(),
(vm_address_t)m->sbuf,
(vm_size_t)m->ssiz);
monsize = (m->textsize / HISTFRACTION) + sizeof(gmonhdr_t);
ret = vm_allocate(mach_task_self(),
(vm_address_t *)&buffer,
(vm_size_t)monsize,
TRUE);
if(ret != KERN_SUCCESS){
write(2, MSG, sizeof(MSG) - 1);
m->sbuf = 0;
return;
}
if(m->tos)
vm_deallocate(mach_task_self(),
(vm_address_t)m->tos,
(vm_size_t)(m->tolimit * sizeof(tostruct_t)));
m->tolimit = m->textsize * ARCDENSITY / 100;
if(m->tolimit < MINARCS){
m->tolimit = MINARCS;
}
else if(m->tolimit > 65534){
m->tolimit = 65534;
}
ret = vm_allocate(mach_task_self(),
(vm_address_t *)&m->tos,
(vm_size_t)(m->tolimit * sizeof(tostruct_t)),
TRUE);
if(ret != KERN_SUCCESS){
write(2, MSG, sizeof(MSG) - 1);
m->tos = 0;
return;
}
m->tos[0].link = 0;
if(m == mon && !init){
monitor(lowpc, highpc, buffer, monsize, m->tolimit);
}
else{
m->sbuf = buffer;
m->ssiz = monsize;
p = (gmonhdr_t *)m->sbuf;
memset(p, '\0', sizeof(gmonhdr_t));
p->lpc = (uintptr_t)m->lowpc;
p->hpc = (uintptr_t)m->highpc;
p->ncnt = m->ssiz;
p->version = GMONVERSION;
p->profrate = getprofhz();
o = highpc - lowpc;
if((monsize - sizeof(gmonhdr_t)) < o)
m->scale = ((float) (monsize - sizeof(gmonhdr_t))/ o) *
SCALE_1_TO_1;
else
m->scale = SCALE_1_TO_1;
if(!init)
add_profil(m->sbuf + sizeof(gmonhdr_t),
m->ssiz - sizeof(gmonhdr_t),
(uintptr_t)m->lowpc, m->scale);
}
}
void
monreset(
void)
{
unsigned long i;
mon_t *m;
gmonhdr_t *p;
moncontrol(0);
if(mon == NULL)
return;
for(i = 0; i < nmon; i++){
m = mon + i;
if(m->sbuf != NULL){
memset(m->sbuf, '\0', m->ssiz);
p = (gmonhdr_t *)m->sbuf;
p->lpc = (uintptr_t)m->lowpc;
p->hpc = (uintptr_t)m->highpc;
p->ncnt = m->ssiz;
p->version = GMONVERSION;
p->profrate = getprofhz();
}
if(m->froms != NULL)
memset(m->froms, '\0', m->textsize / HASHFRACTION);
if(m->tos != NULL)
memset(m->tos, '\0', m->tolimit * sizeof(tostruct_t));
}
order = 0;
moncontrol(1);
}
void
monoutput(
const char *filename)
{
int fd;
unsigned long i, fromindex, endfrom, toindex;
uint32_t magic;
gmon_data_t sample_data, arc_data, dyld_data;
char *frompc;
rawarc_order_t rawarc_order;
mon_t *m;
uint32_t image_count;
intptr_t image_header;
const char *image_name;
moncontrol(0);
m = mon;
if(m == NULL)
return;
fd = creat(filename, 0666);
if(fd < 0){
perror("mcount: gmon.out");
return;
}
#ifndef __LP64__
magic = GMON_MAGIC;
#else
magic = GMON_MAGIC_64;
#endif
write(fd, &magic, sizeof(uint32_t));
#if defined(__DYNAMIC__)
{
image_count = _dyld_image_count();
if(image_count > 1){
#ifdef DYLD_DEBUG
printf("image_count = %lu\n", image_count - 1);
for(i = 1; i < image_count; i++){
image_header = _dyld_get_image_header(i);
printf("\timage_header %p\n", image_header);
image_name = _dyld_get_image_name(i);
printf("\timage_name %s\n", image_name);
}
#endif
dyld_data.type = GMONTYPE_DYLD2_STATE;
dyld_data.size = sizeof(uint32_t) +
sizeof(intptr_t) * (image_count - 1);
for(i = 1; i < image_count; i++){
image_name = _dyld_get_image_name(i);
dyld_data.size += strlen(image_name) + 1;
}
write(fd, &dyld_data, sizeof(gmon_data_t));
image_count--;
write(fd, &image_count, sizeof(uint32_t));
image_count++;
for(i = 1; i < image_count; i++){
image_header = (intptr_t)_dyld_get_image_header(i);
write(fd, &image_header, sizeof(intptr_t));
image_name = _dyld_get_image_name(i);
write(fd, image_name, strlen(image_name) + 1);
}
}
}
#endif
for(i = 0; i < nmon; i++){
m = mon + i;
#ifdef DEBUG
fprintf(stderr, "[monoutput] sbuf %p ssiz %d\n", m->sbuf, m->ssiz);
#endif
sample_data.type = GMONTYPE_SAMPLES;
sample_data.size = m->ssiz;
write(fd, &sample_data, sizeof(gmon_data_t));
write(fd, m->sbuf, m->ssiz);
endfrom = m->textsize / (HASHFRACTION * sizeof(*m->froms));
arc_data.type = GMONTYPE_ARCS_ORDERS;
arc_data.size = 0;
#ifdef DEBUG
fprintf(stderr, "[monoutput] raw arcs, total %lu\n", endfrom);
#endif
for(fromindex = 0; fromindex < endfrom; fromindex++){
if(m->froms[fromindex] == 0){
continue;
}
#ifdef DEBUG
fprintf(stderr, "[monoutput] raw arc count at index[%lu] %u\n",
fromindex, m->froms[fromindex]);
#endif
frompc = m->lowpc +
(fromindex * HASHFRACTION * sizeof(*m->froms));
for(toindex = m->froms[fromindex];
toindex != 0;
toindex = m->tos[toindex].link){
arc_data.size += sizeof(rawarc_order_t);
}
}
write(fd, &arc_data, sizeof(gmon_data_t));
for(fromindex = 0; fromindex < endfrom; fromindex++){
if(m->froms[fromindex] == 0){
continue;
}
frompc = m->lowpc +
(fromindex * HASHFRACTION * sizeof(*m->froms));
for(toindex = m->froms[fromindex];
toindex != 0;
toindex = m->tos[toindex].link){
#ifdef DEBUG
fprintf(stderr, "[monoutput] frompc %p selfpc %p "
"count %ld order %lu\n", frompc,
m->tos[toindex].selfpc,
m->tos[toindex].count, m->tos[toindex].order);
#endif
rawarc_order.raw_frompc = (uintptr_t)frompc;
rawarc_order.raw_selfpc = (uintptr_t)
m->tos[toindex].selfpc;
rawarc_order.raw_count = m->tos[toindex].count;
rawarc_order.raw_order = m->tos[toindex].order;
write(fd, &rawarc_order, sizeof(rawarc_order_t));
}
}
}
close(fd);
}
void
monitor(
char *lowpc,
char *highpc,
char *buf,
int bufsiz,
int nfunc)
{
intptr_t o;
gmonhdr_t *p;
mon_t *m;
moncontrol(0);
m = mon;
if(m == NULL)
return;
if(lowpc == 0){
moncontrol(0);
monoutput("gmon.out");
return;
}
m->sbuf = buf;
m->ssiz = bufsiz;
p = (gmonhdr_t *)buf;
memset(p, '\0', sizeof(gmonhdr_t));
p->lpc = (uintptr_t)lowpc;
p->hpc = (uintptr_t)highpc;
p->ncnt = m->ssiz;
p->version = GMONVERSION;
p->profrate = getprofhz();
bufsiz -= sizeof(gmonhdr_t);
if(bufsiz <= 0)
return;
o = highpc - lowpc;
if(bufsiz < o)
m->scale = ((float) bufsiz / o) * SCALE_1_TO_1;
else
m->scale = SCALE_1_TO_1;
moncontrol(1);
}
void
moncontrol(
int mode)
{
mon_t *m;
unsigned long i;
if(mode){
m = mon;
if(m != NULL){
profil(m->sbuf + sizeof(gmonhdr_t),
m->ssiz - sizeof(gmonhdr_t),
(u_long)m->lowpc, m->scale);
for(i = 1; i < nmon; i++)
add_profil(mon[i].sbuf + sizeof(gmonhdr_t),
mon[i].ssiz - sizeof(gmonhdr_t),
(u_long)mon[i].lowpc, mon[i].scale);
profiling = 0;
}
}
else{
profil((char *)0, 0, 0, 0);
profiling = -1;
}
}
void
moncount(
char *frompc,
char *selfpc)
{
unsigned short *frompcindex;
tostruct_t *top, *prevtop;
unsigned long i, toindex;
mon_t *m;
m = mon;
if(m == NULL)
return;
if(profiling)
return;
profiling++;
#ifdef DEBUG
fprintf(stderr, "[moncount] frompc %p selfpc %p\n", frompc, selfpc);
#endif
frompcindex = (unsigned short *)frompc;
for(i = 0; i < nmon; i++){
m = mon + i;
if((uintptr_t)frompcindex >= (uintptr_t)m->lowpc &&
(uintptr_t)frompcindex < (uintptr_t)m->highpc)
break;
}
if(i == nmon){
goto done;
}
else{
frompcindex = (unsigned short *)
((uintptr_t)frompcindex - (uintptr_t)m->lowpc);
}
frompcindex =
&m->froms[((uintptr_t)frompcindex) / (HASHFRACTION * sizeof(*m->froms))];
toindex = *frompcindex;
if(toindex == 0){
toindex = ++m->tos[0].link;
if(toindex >= m->tolimit){
goto overflow;
}
*frompcindex = toindex;
top = &m->tos[toindex];
top->selfpc = (uintptr_t)selfpc;
top->count = 1;
top->link = 0;
top->order = ++order;
goto done;
}
top = &m->tos[toindex];
if(top->selfpc == (uintptr_t)selfpc){
top->count++;
goto done;
}
for(; ; ){
if(top->link == 0){
toindex = ++m->tos[0].link;
if(toindex >= m->tolimit){
goto overflow;
}
top = &m->tos[toindex];
top->selfpc = (uintptr_t)selfpc;
top->count = 1;
top->link = *frompcindex;
top->order = ++order;
*frompcindex = toindex;
goto done;
}
prevtop = top;
top = &m->tos[top->link];
if(top->selfpc == (uintptr_t)selfpc){
top->count++;
toindex = prevtop->link;
prevtop->link = top->link;
top->link = *frompcindex;
*frompcindex = toindex;
goto done;
}
}
done:
profiling--;
return;
overflow:
profiling++;
#define TOLIMIT "mcount: tos overflow\n"
write(2, TOLIMIT, sizeof(TOLIMIT) - 1);
}
static
long
getprofhz(void)
{
int mib[2];
size_t size;
struct clockinfo clockrate;
mib[0] = CTL_KERN;
mib[1] = KERN_CLOCKRATE;
clockrate.profhz = 1;
size = sizeof(clockrate);
if(sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
;
return(clockrate.profhz);
}