578 lines · 10.5 KB · C
RawDownload
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h"#include "archtrace.h" #pragma profile 0 enum {	Qdir,	Qctl,	Qdata, 	TraceFree	= 0,	TraceEntry, 	TraceExit, 	Logsize		= 8192,	Maxpidwatch	= 64,	Printsize		= 121,	Cacheline	= 64,}; typedef struct Trace Trace;typedef struct Tracelog Tracelog;typedef struct Lockln Lockln; struct Trace {	Archtrace;	Trace	*next;	uchar	*start;	uchar	*end;	int	enabled;	char	name[16];}; /* This represents a trace "hit" or event */struct Tracelog {	uvlong	ticks;	int	info;	uintptr	pc;	uintptr	dat[5];		/* depends on type */}; /* bogus, non-portable experiment to reduce false sharing */struct Lockln {	union {		Lock;		uchar	pad[Cacheline];	};}; static	Rendez	tracesleep;static	QLock	traceslock;static	Trace	*traces;		/* all traces */static	Lockln	loglk;static	Tracelog	*tracelog;	int	traceactive;/* * trace indices. These are just ulongs. You mask them  * to get an index. This makes fifo empty/full etc. trivial.  */static	uint	pw;static	uint	pr;static	int	tracesactive;static	int	all;static	long	traceinhits;static	Lockln	traceinhitslk;static	long	traceouthits;static	Lockln	traceouthitslk;static	ulong	logsize		= Logsize;static	ulong	logmask		= Logsize - 1;static	int	pidwatch[Maxpidwatch];static	int	numpids;	int	codesize;static	char	hex[] 		= "0123456789abcdef"; static char eventname[] = {[TraceEntry] 	'E',[TraceExit] 	'X',}; static Dirtab tracedir[]={	".",		{Qdir, 0, QTDIR},	0,		DMDIR|0555,	"tracectl",	{Qctl},			0,		0664,	"trace",	{Qdata},		0,		0440,}; extern	void	sfence(void); char*hex32(ulong l, char *c){	int i; 	for(i = 7; i >= 0; i--){		c[i] = hex[l&0xf];		l >>= 4;	}	return c + 8;} char*hex64(uvlong l, char *c){	return hex32(l, hex32(l>>32, c));} static intlognonempty(void){	return pw - pr;} static intlogfull(void){	return pw - pr == logsize;} static uvlong idx(uvlong f){	return f & logmask;} /* * Check if the given trace overlaps any others * Returns 1 if there is overlap, 0 if clear. */intoverlapping(Trace *p){	Trace *t; 	for(t = traces; t != nil; t = t->next)		if(p->start <= t->end && p->end >= t->start)			return 1;	return 0;} /* * Return 1 if pid is being watched or no pids are being watched. * Return 0 if pids are being watched and the argument is not * among them. */intwatchingpid(int pid){	int *tab, *m, n, i;		if(numpids == 0)		return 1; 	tab = pidwatch;	n = numpids;	while(n > 0){		i = n/2;		m = tab+i;		if(*m == pid)			return 1;		if(*m < pid){			tab += i+1;			n -= i+1;		}else			n = i;	}	return 0;			} voidremovetrace(Trace *p){	Trace *prev, *t; 	prev = nil;	for(t = traces; t != nil; prev = t, t = t->next)		if(t == p){			if(prev != nil)				prev->next = t->next;			if(t == traces)				traces = nil;			free(t);		}} /* these next two functions assume tracelock is locked */voidtraceon(Trace *p){	if(p->enabled != 1){		p->enabled = 1;		archtraceinstall(p);		tracesactive++;	}} voidtraceoff(Trace *p){	if(p->enabled == 1){		p->enabled = 0;		archtraceuninstall(p);		tracesactive--;	}} /* * Make a new tracelog (an event) */static Tracelog*newtracelog(void){	Tracelog *t; 	t = nil;	ilock(&loglk);	if(!logfull())		t = tracelog + idx(pw++); 	iunlock(&loglk); 	return t;} voidtracein(uintptr pc, uintptr a[4]){		Tracelog *t; //	_xinc(&traceinhits);	ilock(&traceinhitslk);	traceinhits++;	iunlock(&traceinhitslk);	if(!all)		if(!up || !watchingpid(up->pid))			return;	t = newtracelog();	if(!t)		return;	cycles(&t->ticks);	t->pc = pc;	t->dat[0] = -1;	if(up)		t->dat[0] = up->pid;	t->dat[1] = a[0];	t->dat[2] = a[1];	t->dat[3] = a[2];	t->dat[4] = a[3];	sfence();	t->info = TraceEntry;} voidtraceout(uintptr pc, uintptr retval){	Tracelog *t; //	_xinc(&traceouthits);	ilock(&traceouthitslk);	traceouthits++;	iunlock(&traceouthitslk);	if(!all)		if(!up || !watchingpid(up->pid))			return; 	t = newtracelog();	if(!t)		return;	cycles(&t->ticks);	t->pc = (uintptr)pc;	t->dat[0] = -1;	if(up)		t->dat[0] = up->pid;	t->dat[1] = retval;	t->dat[2] = 0;	t->dat[3] = 0;	sfence();	t->info = TraceExit;} /* Create a new trace with the given range */static Trace*mktrace(uchar *start, uchar *end){	Trace *p; 	p = malloc(sizeof *p);	if(mkarchtrace(p, start, &end) == -1){		free(p);		return nil;	}	p->start = start;	p->end = end;	return p;} static Chan*traceattach(char *spec){	return devattach('T', spec);} static Walkqid*tracewalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, tracedir, nelem(tracedir), devgen);} static inttracestat(Chan *c, uchar *db, int n){	return devstat(c, db, n, tracedir, nelem(tracedir), devgen);} static Chan*traceopen(Chan *c, int omode){	if(tracelog == nil)		tracelog = malloc(sizeof *tracelog * logsize);	if(tracelog == nil)		error(Enomem); 	return devopen(c, omode, tracedir, nelem(tracedir), devgen);} static voidtraceclose(Chan*){} static longtraceread(Chan *c, void *a, long n, vlong offset){	char *buf, *e, *s, *s0;	uint i, l, epr;	Tracelog *pl;	Trace *p;	static QLock gate; 	if(c->qid.type == QTDIR)		return devdirread(c, a, n, tracedir, nelem(tracedir), devgen); 	switch((int)c->qid.path){	default:		error("traceread: bad qid");	case Qctl:		buf = s = malloc(READSTR);		e = buf + READSTR;		s = seprint(s, e, "logsize %lud\n", logsize); 		qlock(&traceslock);		for(p = traces; p != nil; p = p->next){			s = seprint(s, e, "trace %p %p new %s\n",				p->start, p->end, p->name);			s = archtracectlr(p, s, e);		}		for(p = traces; p != nil; p = p->next)			s = seprint(s, e, "#trace %p traced? %d\n",				p->start, p->enabled);		for(p = traces; p != nil; p = p->next)			if(p->enabled)				s = seprint(s, e, "trace %s on\n", p->name);		qunlock(&traceslock);		for(i = 0; i < numpids; i++)			s = seprint(s, e, "watch %d\n", pidwatch[i]);		if(traceactive)			s = seprint(s, e, "start\n"); 		s = seprint(s, e, "#tracehits %d, in queue %d\n", pw, pw-pr); 		s = seprint(s, e, "#tracelog %p\n", tracelog);		s = seprint(s, e, "#traceactive %d\n", traceactive);		s = seprint(s, e, "#traceinhits %lud\n", traceinhits);		s = seprint(s, e, "#traceouthits %lud\n", traceouthits);		USED(s);		n = readstr(offset, a, n, buf);		free(buf);		break;	case Qdata:		/*		 * print is avoided because it might be traced.		 */		s = s0 = a;		qlock(&gate);		for(epr = pr + n/Printsize; pr != epr && lognonempty(); pr++){			pl = tracelog + idx(pr);			if((l = eventname[pl->info]) == TraceFree)				break;			pl->info = TraceFree;			/* simple format */			*s++ = l;			*s++ = ' ';			s = hex64((uvlong)pl->pc, s);			*s++ = ' ' ;			s = hex64(pl->ticks, s);			*s++ = ' ';			for(i = 0; ; i++){				s = hex64(pl->dat[i], s);				if(i == 4)					break;				*s++ = ' ';			}			*s++ = '\n';		}		qunlock(&gate);		n = s - s0;		break;	}	return n;} static char notfound[] = "devtrace: trace not found";static char badaddr[] = "devtrace: bad address"; static uchar*getaddr(char *s){	char *e;	uvlong u; 	u = strtoull(s, &e, 16);	if(*e)		error(badaddr);	if(u < KTZERO)		u |= KTZERO;	if((char*)u > etext)		error(badaddr);	return (uchar*)u;}	static longtracewrite(Chan *c, void *a, long n, vlong){	char *tok[6], *ep, *s;	uchar *start, *end;	int *w, l, ntok, pid, saveactive;	Trace *p, *t;	Tracelog *lg; 	saveactive = traceactive;	traceactive = 0;	mfence();	s = nil;	qlock(&traceslock);	if(waserror()){		qunlock(&traceslock);		if(s != nil)			free(s);		traceactive = saveactive;		nexterror();	}	switch((uintptr)c->qid.path){	default:		error("tracewrite: 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], "trace")){			for(p = traces; p != nil; p = p->next)				if(strcmp(tok[1], p->name) == 0)					break;			if(ntok > 3 && !strcmp(tok[3], "new")){				if(ntok != 5)					error("devtrace: usage: trace <ktextstart> <ktextend> new <name>");				start = getaddr(tok[1]);				end = getaddr(tok[2]);				if(start > end)					error("devtrace: invalid address range");				if(p)					error("devtrace: trace already exists"); 				if((p = mktrace(start, end)) == nil)					error(Egreg);				for(t = traces; t != nil; t = t->next)					if(strcmp(tok[4], t->name) == 0)						error("devtrace: trace with that name already exists");				if(overlapping(p))					error("devtrace: given range overlaps with existing trace");				if(ntok < 5)					snprint(p->name, sizeof p->name, "%p", start);				else					strncpy(p->name, tok[4], sizeof p->name);				p->next = traces;				traces = p;			}else if(!strcmp(tok[2], "remove")){				if(ntok != 3)					error("devtrace: usage: trace <name> remove");				if(p == nil)					error(notfound);				traceoff(p);				removetrace(p);			}else if(!strcmp(tok[2], "on")){				if(ntok != 3)					error("devtrace: usage: trace <name> on");				if(p == nil)					error(notfound);				traceon(p);			}else if(!strcmp(tok[2], "off")){				if(ntok != 3) 					error("devtrace: usage: trace <name> off");

Lines 1–500 of 578.