#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>
extern const struct section *getsectbyname(
const char *segname,
const char *sectname);
#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>
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);
extern int add_profil(char *, int, int, int);
static char profiling = -1;
static char init = 0;
static unsigned long order = 0;
struct mon_t {
char *lowpc;
char *highpc;
unsigned long textsize;
unsigned short *froms;
struct tostruct *tos;
long tolimit;
char *sbuf;
int ssiz;
int scale;
};
static struct mon_t *mon = NULL;
static unsigned long nmon = 0;
static void monsetup(
struct mon_t *m,
char *lowpc,
char *highpc);
static int getprofhz(
void);
void
moninit(
void)
{
const struct section *section;
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(struct mon_t))) == NULL){
write(2, MSG, sizeof(MSG) - 1);
return;
}
nmon = 1;
memset(mon, '\0', sizeof(struct mon_t));
}
monsetup(mon, lowpc, highpc);
profil(mon->sbuf + sizeof(struct gmonhdr),
mon->ssiz - sizeof(struct gmonhdr),
(int)mon->lowpc, mon->scale);
for(i = 1; i < nmon; i++)
add_profil(mon[i].sbuf + sizeof(struct gmonhdr),
mon[i].ssiz - sizeof(struct gmonhdr),
(int)mon[i].lowpc, mon[i].scale);
init = 0;
profiling = 0;
#if defined(__DYNAMIC__)
if(_dyld_present())
_dyld_moninit(monaddition);
#endif
}
void
monstartup(
char *lowpc,
char *highpc)
{
monreset();
if(mon == NULL){
if((mon = malloc(sizeof(struct mon_t))) == NULL){
write(2, MSG, sizeof(MSG) - 1);
return;
}
nmon = 1;
memset(mon, '\0', sizeof(struct mon_t));
}
monsetup(mon, lowpc, highpc);
}
void
monaddition(
char *lowpc,
char *highpc)
{
char save_profiling;
struct mon_t *m;
if(mon == NULL){
monstartup(lowpc, highpc);
return;
}
save_profiling = profiling;
profiling = -1;
if((mon = realloc(mon, (nmon + 1) * sizeof(struct mon_t))) == NULL){
write(2, MSG, sizeof(MSG) - 1);
return;
}
m = mon + nmon;
memset(m, '\0', sizeof(struct mon_t));
nmon++;
monsetup(m, lowpc, highpc);
profiling = save_profiling;
}
static
void
monsetup(
struct mon_t *m,
char *lowpc,
char *highpc)
{
int monsize;
char *buffer;
kern_return_t ret;
struct gmonhdr *p;
unsigned int o;
lowpc = (char *)ROUNDDOWN((unsigned)lowpc,
HISTFRACTION * sizeof(HISTCOUNTER));
m->lowpc = lowpc;
highpc = (char *)ROUNDUP((unsigned)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(struct gmonhdr);
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(struct tostruct)));
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(struct tostruct)),
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 = (struct gmonhdr *)m->sbuf;
memset(p, '\0', sizeof(struct gmonhdr));
p->lpc = (unsigned long)m->lowpc;
p->hpc = (unsigned long)m->highpc;
p->ncnt = m->ssiz;
p->version = GMONVERSION;
p->profrate = getprofhz();
o = highpc - lowpc;
if((monsize - sizeof(struct gmonhdr)) < o)
m->scale = ((float) (monsize - sizeof(struct gmonhdr))/ o) *
SCALE_1_TO_1;
else
m->scale = SCALE_1_TO_1;
if(!init)
add_profil(m->sbuf + sizeof(struct gmonhdr),
m->ssiz - sizeof(struct gmonhdr),
(int)m->lowpc, m->scale);
}
}
void
monreset(
void)
{
unsigned long i;
struct mon_t *m;
struct gmonhdr *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 = (struct gmonhdr *)m->sbuf;
p->lpc = (unsigned long)m->lowpc;
p->hpc = (unsigned long)m->highpc;
p->ncnt = m->ssiz;
}
if(m->froms != NULL)
memset(m->froms, '\0', m->textsize / HASHFRACTION);
if(m->tos != NULL)
memset(m->tos, '\0', m->tolimit * sizeof (struct tostruct));
}
order = 0;
moncontrol(1);
}
void
monoutput(
const char *filename)
{
int fd;
unsigned long magic, i, fromindex, endfrom, toindex;
struct gmon_data sample_data, arc_data, dyld_data;
char *frompc;
struct rawarc_order rawarc_order;
struct mon_t *m;
unsigned long image_count, vmaddr_slide;
char *image_name;
moncontrol(0);
m = mon;
if(m == NULL)
return;
fd = creat(filename, 0666);
if(fd < 0){
perror("mcount: gmon.out");
return;
}
magic = GMON_MAGIC;
write(fd, &magic, sizeof(unsigned long));
#if defined(__DYNAMIC__)
if(_dyld_present()){
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++){
vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
printf("\tvmaddr_slide 0x%x\n", (unsigned int)vmaddr_slide);
image_name = _dyld_get_image_name(i);
printf("\timage_name %s\n", image_name);
}
#endif
dyld_data.type = GMONTYPE_DYLD_STATE;
dyld_data.size = sizeof(unsigned long) +
sizeof(unsigned long) * (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(struct gmon_data));
image_count--;
write(fd, &image_count, sizeof(unsigned long));
image_count++;
for(i = 1; i < image_count; i++){
vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
write(fd, &vmaddr_slide, sizeof(unsigned long));
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 0x%x ssiz %d\n",
m->sbuf, m->ssiz);
#endif
sample_data.type = GMONTYPE_SAMPLES;
sample_data.size = m->ssiz;
write(fd, &sample_data, sizeof(struct gmon_data));
write(fd, m->sbuf, m->ssiz);
endfrom = m->textsize / (HASHFRACTION * sizeof(*m->froms));
arc_data.type = GMONTYPE_ARCS_ORDERS;
arc_data.size = 0;
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){
arc_data.size += sizeof(struct rawarc_order);
}
}
write(fd, &arc_data, sizeof(struct gmon_data));
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 0x%x selfpc 0x%x "
"count %ld order %lu\n", (unsigned int)frompc,
(unsigned int)m->tos[toindex].selfpc,
m->tos[toindex].count, m->tos[toindex].order);
#endif
rawarc_order.raw_frompc = (unsigned long)frompc;
rawarc_order.raw_selfpc = (unsigned long)
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(struct rawarc_order));
}
}
}
close(fd);
}
void
monitor(
char *lowpc,
char *highpc,
char *buf,
int bufsiz,
int nfunc)
{
unsigned int o;
struct gmonhdr *p;
struct 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 = (struct gmonhdr *)buf;
memset(p, '\0', sizeof(struct gmonhdr));
p->lpc = (unsigned long)lowpc;
p->hpc = (unsigned long)highpc;
p->ncnt = m->ssiz;
p->version = GMONVERSION;
p->profrate = getprofhz();
bufsiz -= sizeof(struct gmonhdr);
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)
{
struct mon_t *m;
unsigned long i;
if(mode){
m = mon;
if(m != NULL){
profil(m->sbuf + sizeof(struct gmonhdr),
m->ssiz - sizeof(struct gmonhdr),
(int)m->lowpc, m->scale);
for(i = 1; i < nmon; i++)
add_profil(mon[i].sbuf + sizeof(struct gmonhdr),
mon[i].ssiz - sizeof(struct gmonhdr),
(int)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;
struct tostruct *top, *prevtop;
unsigned long i, toindex;
struct mon_t *m;
m = mon;
if(m == NULL)
return;
if(profiling)
return;
profiling++;
#ifdef DEBUG
fprintf(stderr, "[moncount] frompc 0x%x selfpc 0x%x\n",
(unsigned int)frompc, (unsigned int)selfpc);
#endif
frompcindex = (unsigned short *)frompc;
for(i = 0; i < nmon; i++){
m = mon + i;
if((unsigned long)frompcindex >= (unsigned long)m->lowpc &&
(unsigned long)frompcindex < (unsigned long)m->highpc)
break;
}
if(i == nmon){
goto done;
}
else{
frompcindex = (unsigned short *)
((unsigned long)frompcindex - (unsigned long)m->lowpc);
}
frompcindex =
&m->froms[((long)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 = (unsigned long)selfpc;
top->count = 1;
top->link = 0;
top->order = ++order;
goto done;
}
top = &m->tos[toindex];
if(top->selfpc == (unsigned long)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 = (unsigned long)selfpc;
top->count = 1;
top->link = *frompcindex;
top->order = ++order;
*frompcindex = toindex;
goto done;
}
prevtop = top;
top = &m->tos[top->link];
if(top->selfpc == (unsigned long)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
int
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);
}