#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "netif.h"
/* this does not work */
#pragma profile 0
typedef struct Probe Probe;
struct Probe {
struct Probe *next;
void *func;
void *start;
void *end;
int enabled;
char name[16];
};
enum {
Qdir,
Qctl,
Qdata,
};
enum {
ProbeEntry = 1,
ProbeExit
};
/* fix me make this programmable */
enum {
defaultlogsize = 1024,
printsize = 64,
};
typedef struct Probelog Probelog;
struct Probelog {
uvlong ticks;
/* yeah, waste a whole int on something stupid but ... */
int info;
ulong pc;
/* these are different depending on type */
long dat[4];
};
static Rendez profprobesleep;
static QLock profprobeslock;
/* this will contain as many entries as there are valid pc values */
static Probe **probemap;
static Probe *probes;
static Lock loglk;
static Probelog *profprobelog = nil;
int probeactive = 0;
/* profprobe indices. These are just unsigned longs. You mask them
* to get an index. This makes fifo empty/full etc. trivial.
*/
static ulong pw = 0, pr = 0;
static int probesactive = 0;
static unsigned long logsize = defaultlogsize, logmask = defaultlogsize - 1;
int codesize = 0;
static char eventname[] = {
[ProbeEntry] = 'E',
[ProbeExit] = 'X'
};
static Dirtab profprobedir[]={
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
"profprobectl", {Qctl}, 0, 0664,
"profprobe", {Qdata}, 0, 0440,
};
char hex[] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'a',
'b',
'c',
'd',
'e',
'f',
};
/* big-endian ... */
void
hex32(ulong l, char *c)
{
int i;
for(i = 8; i; i--){
c[i-1] = hex[l&0xf];
l >>= 4;
}
}
void
hex64(uvlong l, char *c)
{
hex32(l>>32, c);
hex32(l, &c[8]);
}
static int
lognonempty(void *)
{
return pw - pr;
}
static int
logfull(void)
{
return (pw - pr) >= logsize;
}
static ulong
idx(ulong f)
{
return f & logmask;
}
struct Probe **
probeslot(void *pc)
{
int index;
struct Probe **p;
// if ((ulong)pc < (ulong)start) || ((ulong)pc > (ulong)end))
// panic("probed: pc %p", pc);
// index = (ulong)pc - (ulong)start;
index = (ulong)pc - (ulong)KTZERO;
if (index > codesize)
return nil;
p = &probemap[index];
return p;
}
struct Probe *
probed(void *pc)
{
struct Probe **p;
p = probeslot(pc);
return *p;
}
/* it is recommended that you call these with something sane. */
/* these next two functions assume you locked profprobelock */
void
probeon(struct Probe *p)
{
unsigned char *cp;
struct Probe **slot;
slot = probeslot(p->start);
print("probeon: slot %p\n", slot);
for(cp = p->start; cp <= p->end; slot++, cp++)
*slot = p;
p->enabled = 1;
probesactive++;
}
void
probeoff(struct Probe *p)
{
unsigned char *cp;
struct Probe **slot;
slot = probeslot(p->start);
print("probeoff: slot %p\n", slot);
for(cp = p->start; cp <= p->end; slot++, cp++)
*slot = nil;
p->enabled = 0;
probesactive--;
}
/* can return NULL, meaning, no record for you */
static struct Probelog *
newpl(void)
{
ulong index;
/*
if (logfull()){
wakeup(&profprobesleep);
return nil;
}
*/
ilock(&loglk);
index = pw++;
iunlock(&loglk);
return &profprobelog[idx(index)];
}
void
profin(ulong pc, ulong a1, ulong a2, ulong a3)
{
uintptr kgetcallerpc(void *firstarg);
struct Probelog *pl;
if (! probeactive)
return;
if (! probed((void *)pc))
return;
pl = newpl();
if (! pl)
return;
cycles(&pl->ticks);
pl->pc = (ulong)pc;
if (up)
pl->dat[0] = up->pid;
else
pl->dat[0] = (unsigned long)-1;
pl->dat[1] = a1;
pl->dat[2] = a2;
pl->dat[3] = a3;
pl->info = ProbeEntry;
}
void
profout(ulong pc, ulong retval)
{
struct Probelog *pl;
if (! probeactive)
return;
/* return here, it works */
if (! probed((void *)pc))
return;
/* return here, it works */
pl = newpl();
if (! pl)
return;
// MOST RECENT CHANGE ENABLE CYCLES
cycles(&pl->ticks);
pl->pc = (ulong)pc;
if (up)
pl->dat[0] = up->pid;
else
pl->dat[0] = (unsigned long)-1;
pl->dat[1] = retval;
pl->dat[2] = 0;
pl->dat[3] = 0;
pl->info = ProbeExit;
}
static Probe *
mkprobe(void *func, void *start, void *end)
{
Probe *p;
p = mallocz(sizeof p[0], 1);
p->func = func;
p->start = start;
p->end = end;
return p;
}
static void
freeprobe(Probe *p)
{
free(p);
}
static Chan*
profprobeattach(char *spec)
{
return devattach('T', spec);
}
static Walkqid*
profprobewalk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, profprobedir, nelem(profprobedir), devgen);
}
static int
profprobestat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, profprobedir, nelem(profprobedir), devgen);
}
static Chan*
profprobeopen(Chan *c, int omode)
{
/* if there is no profprobelog, allocate one. Open always fails
* if the basic alloc fails. You can resize it later.
*/
codesize = ((ulong)etext) - KTZERO;
print("codesize %d\n", codesize);
if (! probemap)
probemap = mallocz(sizeof(struct probemap *)*codesize, 1);
if (! probemap)
error("probemap malloc failed");
if (! profprobelog)
profprobelog = mallocz(sizeof(*profprobelog)*logsize, 1);
/* I guess malloc doesn't toss an error */
if (! profprobelog)
error("profprobelog malloc failed");
c = devopen(c, omode, profprobedir, nelem(profprobedir), devgen);
return c;
}
static void
profprobeclose(Chan *)
{
}
static long
profproberead(Chan *c, void *a, long n, vlong offset)
{
char *buf;
char *cp = a;
struct Probelog *pl;
Probe *p;
int i;
static QLock gate;
if(c->qid.type == QTDIR)
return devdirread(c, a, n, profprobedir, nelem(profprobedir), devgen);
switch((ulong)c->qid.path){
default:
error("profproberead: bad qid");
case Qctl:
buf = malloc(READSTR);
i = 0;
qlock(&profprobeslock);
i += snprint(buf + i, READSTR - i, "logsize %lud\n", logsize);
for(p = probes; p != nil; p = p->next)
i += snprint(buf + i, READSTR - i, "probe %p %p new %s\n",
p->start, p->end, p->name);
for(p = probes; p != nil; p = p->next)
i += snprint(buf + i, READSTR - i, "#probe %p probed? %p\n",
p->func, probed(p->func));
for(p = probes; p != nil; p = p->next)
if (p->enabled)
i += snprint(buf + i, READSTR - i, "probe %s on\n",
p->name);
i += snprint(buf + i, READSTR - i, "#profprobehits %lud, in queue %lud\n",
pw, pw-pr);
snprint(buf + i, READSTR - i, "#profprobelog %p\n", profprobelog);
snprint(buf + i, READSTR - i, "#probeactive %d\n", probeactive);
qunlock(&profprobeslock);
n = readstr(offset, a, n, buf);
free(buf);
break;
case Qdata:
// qlock(&gate);
if(waserror()){
// qunlock(&gate);
nexterror();
}
// sleep(&profprobesleep, lognonempty, nil);
i = 0;
while(lognonempty((void *)0)){
int j;
pl = profprobelog + idx(pr);
if ((i + printsize) >= n)
break;
/* simple format */
cp[0] = eventname[pl->info];
cp ++;
*cp++ = ' ';
hex32(pl->pc, cp);
cp[8] = ' ';
cp += 9;
hex64(pl->ticks, cp);
cp[16] = ' ';
cp += 17;
for(j = 0; j < 4; j++){
hex32(pl->dat[j], cp);
cp[8] = ' ';
cp += 9;
}
/* adjust for extra skip above */
cp--;
*cp++ = '\n';
pr++;
i += printsize;
}
poperror();
// qunlock(&gate);
n = i;
break;
}
return n;
}
static long
profprobewrite(Chan *c, void *a, long n, vlong)
{
char *tok[5];
char *ep, *s = nil;
Probe *p, **pp;
int ntok;
int saveactive = probeactive;
probeactive = 0;
qlock(&profprobeslock);
if(waserror()){
qunlock(&profprobeslock);
if(s != nil) free(s);
nexterror();
}
switch((ulong)c->qid.path){
default:
error("profproberead: bad qid");
case Qctl:
s = malloc(n + 1);
memmove(s, a, n);
s[n] = 0;
ntok = tokenize(s, tok, nelem(tok));
print("write ctl s is :%s: ntok %d\n", s, ntok);
print("what is tok[0]? %s\n", tok[0]);
if(!strcmp(tok[0], "probe")){ /* 'probe' ktextaddr 'on'|'off'|'mk'|'del' [name] */
if(ntok < 3)
error("devprofprobe: usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name]");
for(pp = &probes; *pp != nil; pp = &(*pp)->next){
print("Check %p against %p\n", tok[1], (*pp)->name);
if(!strcmp(tok[1], (*pp)->name))
break;
}
p = *pp;
print("Check tok[3] which is %p or tok[2] which is %p\n", tok[3], tok[2]);
if((ntok > 3) && (!strcmp(tok[3], "new"))){
ulong addr;
void *start, *end, *func;
if (ntok != 5)
error("probe ktextstart ktextend new name");
addr = strtoul(tok[1], &ep, 0);
func = start = (void*)addr;
if(*ep)
error("devprofprobe: start address not in recognized format");
addr = strtoul(tok[2], &ep, 0);
end = (void*)addr;
if(*ep)
error("devprofprobe: end address not in recognized format");
/* What do we do here? start and end are weird *
if((addr < (ulong)start) || (addr > (ulong)end)
error("devprofprobe: address out of bounds");
*/
if(p)
error("devprofprobe: 0x%p already has profprobe");
p = mkprobe(func, start, end);
p->next = probes;
if(ntok < 5)
snprint(p->name, sizeof p->name, "%p", func);
else
strncpy(p->name, tok[4], sizeof p->name);
probes = p;
} else if(!strcmp(tok[2], "on")){
if(p == nil)
error("devprofprobe: 0x%p not found");
print("turn on probe %p probed %p\n", p->func, probed(p->func));
if (! probed(p->func)){
probeon(p);
saveactive += 1;
}
} else if(!strcmp(tok[2], "off")){
if(p == nil)
error("devprofprobe: 0x%p not found");
if(probed(p->func)){
probeoff(p);
}
} else if(!strcmp(tok[2], "del")){
if(p == nil)
error("devprofprobe: 0x%p not found");
if(probed(p->func)){
probeoff(p);
}
*pp = p->next;
freeprobe(p);
} else if(!strcmp(tok[2], "mv")){
if(p == nil)
error("devprofprobe: 0x%p not found");
if(ntok < 4)
error("devprofprobe: rename without new name?");
strncpy(p->name, tok[3], sizeof p->name);
}
} else if(!strcmp(tok[0], "size")){
int l, size;
struct Probelog *newprofprobelog;
l = strtoul(tok[1], &ep, 0);
if(*ep)
error("devprofprobe: size not in recognized format");
size = 1 << l;
/* sort of foolish. Alloc new profprobe first, then free old. */
/* and too bad if there are unread profprobes */
newprofprobelog = mallocz(sizeof(*newprofprobelog)*size, 1);
/* does malloc throw waserror? I don't know */
if (newprofprobelog){
free(profprobelog);
profprobelog = newprofprobelog;
logsize = size;
logmask = size - 1;
pr = pw = 0;
} else error("devprofprobe: can't allocate that much");
} else {
error("devprofprobe: usage: 'profprobe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name] or: 'size' buffersize (power of 2)");
}
free(s);
break;
}
poperror();
qunlock(&profprobeslock);
probeactive = saveactive;
return n;
}
Dev profprobedevtab = {
'T',
"profprobe",
devreset,
devinit,
devshutdown,
profprobeattach,
profprobewalk,
profprobestat,
profprobeopen,
devcreate,
profprobeclose,
profproberead,
devbread,
profprobewrite,
devbwrite,
devremove,
devwstat,
};
|