Plan 9 from Bell Labs’s /usr/web/sources/contrib/rminnich/trace/9.probe/port/devprofprobe.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#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,
};

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].