#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: profile.c,v 1.18 2019/11/10 21:16:32 chs Exp $");
#include "profiler.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <shark/shark/hat.h>
#include <machine/profileio.h>
#include <dev/ic/i8253reg.h>
#define PROFILER_DEBUG 1
#define countPerTick 500
#define STATUS_MODE_MASK 0x1f
#define USER_MODE 0x10
#define FIQ_MODE 0x11
#define IRQ_MODE 0x12
#define SVC_MODE 0x13
#define ABORT_MODE 0x17
#define UNDEF_MODE 0x1b
#define SYS_MODE 0x1f
struct profiler_sc
{
int state;
#define PROF_OPEN 0x01
#define PROF_PROFILING 0x02
} prof_sc;
#define HATSTACKSIZE 1024
static unsigned char hatStack[HATSTACKSIZE];
struct profHashTable *profTable;
struct profHashTable *phashTables[2];
int nhashTables;
static void profFiq(int x);
static void profHatWedge(int nFIQs);
void profStop(void);
void profStart(struct profStartInfo *);
static void profEnter(struct profHashTable * , unsigned int);
void displayTable(struct profHashTable * );
dev_type_open(profopen);
dev_type_close(profclose);
dev_type_read(profread);
dev_type_ioctl(profioctl);
const struct cdevsw prof_cdevsw = {
.d_open = profopen,
.d_close = profclose,
.d_read = profread,
.d_write = nowrite,
.d_ioctl = profioctl,
.d_stop = nostop,
.d_tty = notty,
.d_poll = nopoll,
.d_mmap = nommap,
.d_kqfilter = nokqfilter,
.d_discard = nodiscard,
.d_flag = 0
};
void
profilerattach(int n)
{
prof_sc.state = 0;
}
int
profopen(dev_t dev, int flag, int mode, struct proc *p)
{
if (minor(dev) >= NPROFILER)
{
return ENXIO;
}
if (prof_sc.state && PROF_OPEN)
{
return EBUSY;
}
if (!(flag && FWRITE))
{
return EROFS;
}
prof_sc.state |= PROF_OPEN;
nhashTables = 0;
phashTables[0] = phashTables[1] = NULL;
return 0;
}
int
profclose(dev_t dev, int flag, int mode, struct proc *p)
{
profStop();
prof_sc.state &= ~PROF_OPEN;
return 0;
}
int
profread(dev_t dev, struct uio *uio, int flags)
{
int error;
int real, backup;
if (!(prof_sc.state & PROF_PROFILING))
{
error = EINVAL;
}
else
{
if (uio->uio_resid != sizeof(struct profHashHeader) +
profTable->hdr.tableSize * sizeof(struct profHashEntry))
{
printf("profile read size is incorrect!");
error = EINVAL;
}
else
{
if (profTable == phashTables[0])
{
real = 0;
backup = 1;
}
else
{
if (profTable == phashTables[1])
{
real = 1;
backup = 0;
}
else
{
panic("profiler lost buffer");
}
}
memset(phashTables[backup]->entries, 0,
profTable->hdr.tableSize * sizeof(struct profHashEntry));
phashTables[backup]->hdr.tableSize = phashTables[real]->hdr.tableSize;
phashTables[backup]->hdr.entries = phashTables[backup]->hdr.last
= phashTables[real]->hdr.entries;
phashTables[backup]->hdr.samples = 0;
phashTables[backup]->hdr.missed = 0;
phashTables[backup]->hdr.fiqs = 0;
phashTables[backup]->hdr.pid = phashTables[real]->hdr.pid;
phashTables[backup]->hdr.mode = phashTables[real]->hdr.mode;
profTable = phashTables[backup];
if ( (error = uiomove(phashTables[real],
sizeof(struct profHashHeader), uio))
!= 0)
{
printf("uiomove failed error is %d\n", error);
}
else
{
if ( (error = uiomove(phashTables[real]->entries,
phashTables[real]->hdr.tableSize *
sizeof(struct profHashEntry), uio))
!= 0)
{
printf("uiomove failed error is %d\n", error);
}
}
}
}
return error;
}
static int profcount = 0;
static int ints = 0;
int
profioctl(dev_t dev, u_long cmd, void *data, int flag, struct proc *p)
{
int error = 0;
struct profStartInfo *info = (struct profStartInfo *) data;
switch (cmd)
{
case PROFIOSTART :
profStart(info);
break;
case PROFIOSTOP :
profStop();
break;
default :
error = EINVAL;
break;
}
return error;
}
void
profStart(struct profStartInfo *info)
{
unsigned int savedInts;
char *buffer;
if ( prof_sc.state & PROF_PROFILING )
{
info->status = ALREADY_SAMPLING;
return ;
}
if (info->entries > info->tableSize)
{
info->status = BAD_TABLE_SIZE;
return ;
}
if ( !(info->mode & SAMPLE_MODE_MASK) )
{
info->status = ILLEGAL_COMMAND;
return ;
}
buffer = malloc(sizeof(struct profHashTable) +
info->tableSize * sizeof(struct profHashEntry),
M_DEVBUF, M_WAITOK);
phashTables[0] = (struct profHashTable *) buffer;
phashTables[0]->entries = (struct profHashEntry *)
( buffer + sizeof(struct profHashTable));
buffer = malloc(sizeof(struct profHashTable) +
info->tableSize * sizeof(struct profHashEntry),
M_DEVBUF, M_WAITOK);
phashTables[1] = (struct profHashTable *) buffer;
phashTables[1]->entries = (struct profHashEntry *)
( buffer + sizeof(struct profHashTable));
memset(phashTables[0]->entries, 0,
info->tableSize * sizeof(struct profHashEntry));
memset(phashTables[1]->entries, 0,
info->tableSize * sizeof(struct profHashEntry));
profTable = phashTables[0];
profTable->hdr.tableSize = info->tableSize;
profTable->hdr.entries = profTable->hdr.last = info->entries;
profTable->hdr.samples = 0;
profTable->hdr.missed = 0;
profTable->hdr.fiqs = 0;
profTable->hdr.pid = info->pid;
profTable->hdr.mode = info->mode;
savedInts = disable_interrupts(I32_bit | F32_bit);
prof_sc.state |= PROF_PROFILING;
hatClkOn(countPerTick,
profFiq,
(int)&prof_sc,
hatStack + HATSTACKSIZE - sizeof(unsigned),
profHatWedge);
restore_interrupts(savedInts);
}
void
profStop(void)
{
unsigned int savedInts;
int spl;
savedInts = disable_interrupts(I32_bit | F32_bit);
hatClkOff();
restore_interrupts(savedInts);
spl = splbio();
if (prof_sc.state & PROF_PROFILING)
{
free(phashTables[0], M_DEVBUF);
free(phashTables[1], M_DEVBUF);
}
phashTables[0] = phashTables[1] = NULL;
prof_sc.state &= ~PROF_PROFILING;
splx(spl);
}
static void
profFiq(int x)
{
int i;
int *ip;
unsigned int spsr, stacklr;
__asm("mov %0, ip" : "=r" (ip) : );
stacklr = *(ip+4);
__asm("mrs %0, spsr" : "=r" (spsr) : );
if ( (profTable->hdr.mode & SAMPLE_PROC) &&
((spsr & STATUS_MODE_MASK) == USER_MODE) )
{
if ( curlwp->p_pid == profTable->hdr.pid )
{
profEnter(profTable, stacklr-4);
}
}
if ( profTable->hdr.mode & SAMPLE_KERN )
{
if ( ((spsr & STATUS_MODE_MASK) == SVC_MODE)
)
{
profEnter(profTable, stacklr-4);
}
}
profTable->hdr.fiqs++;
}
static void
profHatWedge(int nFIQs)
{
#ifdef PROFILER_DEBUG
printf("profHatWedge: nFIQ = %d\n",nFIQs);
#endif
}
static void
profEnter(struct profHashTable *table, unsigned int lr)
{
unsigned int entries, hashShift, index, count;
struct profHashEntry *sample;
struct profHashEntry *first;
struct profHashEntry *prev;
struct profHashEntry tmpEntry;
int tmpIndex;
entries = table->hdr.entries - 1;
hashShift = 0;
do
{
entries = entries << 1;
hashShift++;
} while (!(entries & 0x80000000));
lr = lr >> REDUNDANT_BITS;
count = lr & COUNT_BIT_MASK;
lr = lr >> COUNT_BITS;
index = (lr << hashShift) >> hashShift;
first = sample = &table->entries[index];
while ( (sample->pc != lr) && (table->hdr.last < table->hdr.tableSize) )
{
if (sample->pc == 0)
{
sample->pc = lr;
}
else
{
if (sample->next != 0)
{
prev = sample;
sample = &table->entries[sample->next];
}
else
{
if (table->hdr.last < table->hdr.tableSize)
{
sample = &table->entries[table->hdr.last];
memcpy(sample, first, sizeof(struct profHashEntry));
first->pc = lr;
first->next = table->hdr.last;
first->counts[0] = 0;
first->counts[1] = 0;
first->counts[2] = 0;
first->counts[3] = 0;
table->hdr.last++;
sample = first;
}
}
}
}
if (sample != first)
{
memcpy(&tmpEntry, sample, sizeof(struct profHashEntry));
tmpIndex = prev->next;
prev->next = sample->next;
memcpy(sample, first, sizeof(struct profHashEntry));
memcpy(first, &tmpEntry, sizeof(struct profHashEntry));
first->next = tmpIndex;
}
if (sample->pc == lr)
{
sample->counts[count]++;
table->hdr.samples++;
}
else
{
table->hdr.missed++;
}
}
void
displayTable(struct profHashTable *table)
{
int i;
struct profHashEntry *sample;
char buff[100] = ".............................................\n";
for (i=0; i < table->hdr.tableSize; i++)
{
sample = &table->entries[i];
if ((i * table->hdr.tableSize) >= table->hdr.entries)
{
printf("%s", buff);
buff[0] = '\0';
}
printf("i = %d, pc = 0x%x, next = %d, counts %d %d %d %d\n",
i, sample->pc, sample->next, sample->counts[0],
sample->counts[1], sample->counts[2], sample->counts[3]);
}
return;
}