rminnichmerge57758a716 years agoHistory
400 lines · 7.6 KB · C
RawDownload
#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h" #include	"probe.h" 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 probesleep;static QLock probeslk;static Probe *probes;static Lock loglk;static Probelog *probelog = nil;/* probe 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; static char eventname[] = {	[ProbeEntry] = 'E',	[ProbeExit] = 'X'}; static Dirtab probedir[]={	".",		{Qdir, 0, QTDIR},	0,		DMDIR|0555,	"probectl",	{Qctl},		0,		0664,	"probe",	{Qdata},	0,		0440,}; char hex[] = {	'0',	'1',	'2',	'3',	'4',	'5',	'6',	'7',	'8',	'9',	'A',	'B',	'C',	'D',	'E',	'F',}; /* big-endian ... */voidhex32(ulong l, char *c){	int i;	for(i = 8; i; i--){		c[i-1] = hex[l&0xf];		l >>= 4;	}} voidhex64(uvlong l, char *c){	hex32(l>>32, c);	hex32(l, &c[8]);}static intlognonempty(void *){	return pw - pr;} static intlogfull(void){	return (pw - pr) >= logsize;} static ulongidx(ulong f){	return f & logmask;} /* can return NULL, meaning, no record for you */static struct Probelog *newpl(void){	ulong index; 	if (logfull()){		wakeup(&probesleep);		return nil;	} 	ilock(&loglk);	index = pw++;	iunlock(&loglk); 	return &probelog[idx(index)]; } static voidprobeentry(Probe *p){	struct Probelog *pl;//print("probeentry %p p %p func %p argp %p\n", &p, p, p->func, p->argp);	pl = newpl();	if (! pl)		return;	cycles(&pl->ticks);	pl->pc = (ulong)p->func;	pl->dat[0] = p->argp[0];	pl->dat[1] = p->argp[1];	pl->dat[2] = p->argp[2];	pl->dat[3] = p->argp[3];	pl->info = ProbeEntry;} static voidprobeexit(Probe *p){//print("probeexit %p p %p func %p argp %p\n", &p, p, p->func, p->argp);	struct Probelog *pl;	pl = newpl();	if (! pl)		return;	cycles(&pl->ticks);	pl->pc = (ulong)p->func;	pl->dat[0] = p->rval;	pl->info = ProbeExit;} static Chan*probeattach(char *spec){	return devattach('+', spec);} static Walkqid*probewalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, probedir, nelem(probedir), devgen);} static longprobestat(Chan *c, uchar *db, long n){	return devstat(c, db, n, probedir, nelem(probedir), devgen);} static Chan*probeopen(Chan *c, int omode){	/* if there is no probelog, allocate one. Open always fails	  * if the basic alloc fails. You can resize it later.	  */	if (! probelog)		probelog = malloc(sizeof(*probelog)*logsize);	/* I guess malloc doesn't toss an error */	if (! probelog)		error("probelog malloc failed"); 	c = devopen(c, omode, probedir, nelem(probedir), devgen);	return c;} static voidprobeclose(Chan *){} static longproberead(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, probedir, nelem(probedir), devgen);	switch((ulong)c->qid.path){	default:		error("proberead: bad qid");	case Qctl:		buf = malloc(READSTR);		i = 0;		qlock(&probeslk);		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 new %s\n",				p->func, p->name); 		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, "#probehits %lud, in queue %lud\n",				pw, pw-pr);		snprint(buf + i, READSTR - i, "#probelog %p\n", probelog);		qunlock(&probeslk);		n = readstr(offset, a, n, buf);		free(buf);		break;	case Qdata:		qlock(&gate);		if(waserror()){			qunlock(&gate);			nexterror();		}		while(!lognonempty(nil))			tsleep(&probesleep, lognonempty, nil, 5000);		i = 0;		while(lognonempty((void *)0)){			int j;			pl = probelog + 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 longprobewrite(Chan *c, void *a, long n, vlong){	char *tok[5];	char *ep, *s = nil;	Probe *p, **pp;	int ntok; 	qlock(&probeslk);	if(waserror()){		qunlock(&probeslk);		if(s != nil) free(s);		nexterror();	}	switch((ulong)c->qid.path){	default:		error("proberead: bad qid");	case Qctl:		s = malloc(n + 1);		memmove(s, a, n);		s[n] = 0;		ntok = tokenize(s, tok, nelem(tok));		if(!strcmp(tok[0], "probe")){	/* 'probe' ktextaddr 'on'|'off'|'mk'|'del' [name] */			if(ntok < 3)				error("devprobe: usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name]");			for(pp = &probes; *pp != nil; pp = &(*pp)->next)				if(!strcmp(tok[1], (*pp)->name))					break;			p = *pp;			if(!strcmp(tok[2], "new")){				ulong addr;				void *func;				addr = strtoul(tok[1], &ep, 0);				func = (void*)addr;				if(*ep)					error("devprobe: address not in recognized format");			//	if(addr < ((ulong) start) || addr > ((ulong) end))			//		error("devprobe: address out of bounds");				if(p != nil)					error("devprobe: 0x%p already has probe");				p = mkprobe(func, probeentry, probeexit);				p->next = probes;				if(ntok < 4)					snprint(p->name, sizeof p->name, "%p", func);				else					strncpy(p->name, tok[3], sizeof p->name);				probes = p;			} else if(!strcmp(tok[2], "on")){				if(p == nil)					error("devprobe: probe not found");				if(!p->enabled)					probeinstall(p);print("probeinstall in devprobe\n");				probesactive++;			} else if(!strcmp(tok[2], "off")){				if(p == nil)					error("devprobe: probe not found");				if(p->enabled)					probeuninstall(p);				probesactive--;			} else if(!strcmp(tok[2], "del")){				if(p == nil)					error("devprobe: probe not found");				if(p->enabled)					probeuninstall(p);				probesactive--;				*pp = p->next;				freeprobe(p);			} else if(!strcmp(tok[2], "mv")){				if(p == nil)					error("devprobe: probe not found");				if(ntok < 4)					error("devprobe: rename without new name?");				strncpy(p->name, tok[3], sizeof p->name);			}		} else if(!strcmp(tok[0], "size")){			int l, size;			struct Probelog *newprobelog;			l = strtoul(tok[1], &ep, 0);			if(*ep)				error("devprobe: size not in recognized format");			size = 1 << l;			/* sort of foolish. Alloc new probe first, then free old. */			/* and too bad if there are unread probes */			newprobelog = malloc(sizeof(*newprobelog)*size);			/* does malloc throw waserror? I don't know */			free(probelog);			probelog = newprobelog;			logsize = size;			pr = pw = 0;		} else {			error("devprobe:  usage: 'probe' [ktextaddr|name] 'on'|'off'|'mk'|'del' [name] or:  'size' buffersize (power of 2)");		}		free(s);		break;	}	poperror();	qunlock(&probeslk);	return n;} Dev probedevtab = {	'+',	"probe",	devreset,	devinit,	devshutdown,	probeattach,	probewalk,	probestat,	probeopen,	devcreate,	probeclose,	proberead,	devbread,	probewrite,	devbwrite,	devremove,	devwstat,};