forsyth20121205-174063a80cc13 years agoHistory
484 lines · 8.4 KB · C
RawDownload
#include	"dat.h"#include	"fns.h"#include	"error.h"#include	"interp.h" enum{	Qdir,	Qctl,	Qstate,	Qsum,	Qevent,	Qprof,	Qheap,	Qgc}; staticDirtab memdir[] ={	".",			{Qdir, 0, QTDIR},	0,	DMDIR|0555,	"memctl",		{Qctl},	0,			0666,	"memstate",	{Qstate},	0,			0444,	"memsum",	{Qsum},	0,			0444,	"memevent",	{Qevent},	0,			0444,	"memprof",	{Qprof},	0,			0444,	"memheap",	{Qheap},	0,			0444,	"memgc",		{Qgc},	0,			0444,}; enum{	/* these are the top two bits of size */	Pflags=	3<<30,	  Pfree=	0<<30,	  Palloc=	1<<30,	  Paend=	2<<30,	  Pimmutable=	3<<30, 	Npool = 3,	Nevent = 10000,	Nstate = 12000}; /* * pool snapshots */typedef struct Pstate Pstate;struct Pstate{	ulong	base;	ulong	size;}; static struct{	Pstate	state[3+Nstate];	Pstate*	lim;	Pstate*	ptr;	int	summary;} poolstate[Npool];static Ref	stateopen; /* * pool/heap allocation events */typedef struct Pevent Pevent;struct Pevent{	int	pool;	ulong	pc;	ulong	base;	ulong	size;}; static struct{	Lock	l;	Ref	inuse;	Rendez	r;	int	open;	Pevent	events[Nevent];	int	rd;	int	wr;	int	full;	int	want;	ulong	lost;} poolevents; /* * allocation profiles */typedef struct Pprof Pprof;typedef struct Pbucket Pbucket; struct Pbucket{	ulong	val;	ulong	pool;	ulong	count;	ulong	size;	Pbucket*	next;}; static struct {	Ref	inuse;	/* only one of these things */	Lock	l;	Pbucket	buckets[1000];	Pbucket	snap[1000];	int	used;	int	snapped;	Pbucket*	hash[128];	ulong	lost;} memprof; extern	void	(*memmonitor)(int, ulong, ulong, ulong);extern	ulong	gcnruns;extern	ulong	gcsweeps;extern	ulong	gcbroken;extern	ulong	gchalted;extern	ulong	gcepochs;extern	uvlong	gcdestroys;extern	uvlong	gcinspects;extern	uvlong	gcbusy;extern	uvlong	gcidle;extern	uvlong	gcidlepass;extern	uvlong	gcpartial;  static voidmprofreset(void){	lock(&memprof.l);	/* need ilock in kernel */	memset(memprof.hash, 0, sizeof(memprof.hash));	memprof.used = 0;	memprof.lost = 0;	unlock(&memprof.l);} static voidmprofmonitor(int pool, ulong pc, ulong base, ulong size){	Pbucket **h0, **h, *p; 	if((pool&7) == 1)		return;	/* ignore heap */	USED(base);	h0 = &memprof.hash[((pc>>16)^(pc>>4))&(nelem(memprof.hash)-1)];	lock(&memprof.l);	for(h = h0; (p = *h) != nil; h = &p->next)		if(p->val == pc && p->pool == pool){			p->count++;			p->size += size;			*h = p->next;			p->next = *h0;			*h0 = p;			unlock(&memprof.l);			return;		}	if(memprof.used >= nelem(memprof.buckets)){		memprof.lost++;		unlock(&memprof.l);		return;	}	p = &memprof.buckets[memprof.used++];	p->val = pc;	p->pool = pool;	p->count = 1;	p->size = size;	p->next = *h0;	*h0 = p;	unlock(&memprof.l);} static void_memmonitor(int pool, ulong pc, ulong base, ulong size){	Pevent e; 	e.pool = pool;	e.pc = pc;	e.base = base;	e.size = size;	lock(&poolevents.l);	if(!poolevents.full){		poolevents.events[poolevents.wr] = e;		if(++poolevents.wr == nelem(poolevents.events))			poolevents.wr = 0;		if(poolevents.wr == poolevents.rd)			poolevents.full = 1;	}else		poolevents.lost++;	if(poolevents.want){		poolevents.want = 0;		Wakeup(&poolevents.r);	}	unlock(&poolevents.l);} static intismemdata(void *v){	USED(v);	return poolevents.full || poolevents.rd != poolevents.wr;} static char*memaudit(int pno, Bhdr *b){	Pstate *p; 	if(pno >= Npool)		return "too many pools for memaudit";	if((p = poolstate[pno].ptr) == poolstate[pno].lim){		if(b->magic == MAGIC_E)			return nil;		p = &poolstate[pno].state[1];		if(b->magic == MAGIC_F)			p++;		p->base++;		p->size += b->size;		return nil;	}	poolstate[pno].ptr++;	p->base = (ulong)b;	p->size = b->size;	switch(b->magic){	case MAGIC_A:		p->size |= Palloc;		break;	case MAGIC_F:		p->size |= Pfree;		break;	case MAGIC_E:		p->size = b->csize | Paend;		break;	case MAGIC_I:		p->size |= Pimmutable;		break;	default:		return "bad magic number in block";	}	return nil;} static voidmput4(uchar *m, ulong v){	m[0] = v>>24;	m[1] = v>>16;	m[2] = v>>8;	m[3] = v;} static Chan*memattach(char *spec){	return devattach('%', spec);} static Walkqid*memwalk(Chan *c, Chan *nc, char **name, int nname){	return devwalk(c, nc, name, nname, memdir, nelem(memdir), devgen);} static intmemstat(Chan *c, uchar *db, int n){	return devstat(c, db, n, memdir, nelem(memdir), devgen);} static Chan*memopen(Chan *c, int omode){	if(memmonitor != nil && c->qid.path != Qgc)		error(Einuse);	c = devopen(c, omode, memdir, nelem(memdir), devgen);	switch((ulong)c->qid.path){	case Qevent:		if(incref(&poolevents.inuse) != 1){			decref(&poolevents.inuse);			c->flag &= ~COPEN;			error(Einuse);		}		poolevents.rd = poolevents.wr = 0;		poolevents.full = 0;		poolevents.want = 0;		poolevents.lost = 0;		memmonitor = _memmonitor;		poolevents.open = 1;		break;	case Qstate:		if(incref(&stateopen) != 1){			decref(&stateopen);			c->flag &= ~COPEN;			error(Einuse);		}		break;	case Qprof:		if(incref(&memprof.inuse) != 1){			decref(&memprof.inuse);			c->flag &= ~COPEN;			error(Einuse);		}		memmonitor = mprofmonitor;		break;	}	return c;} static voidmemclose(Chan *c){	if((c->flag & COPEN) == 0)		return;	switch((ulong)c->qid.path) {	case Qevent:		memmonitor = nil;		poolevents.open = 0;		decref(&poolevents.inuse);		break;	case Qstate:		decref(&stateopen);		break;	case Qprof:		decref(&memprof.inuse);		memmonitor = nil;		break;	} } static longmemread(Chan *c, void *va, long count, vlong offset){	uchar *m;	char *e, *s;	int i, summary;	long n, nr;	Pstate *p;	Pevent pe;	Pbucket *b; 	if(c->qid.type & QTDIR)		return devdirread(c, va, count, memdir, nelem(memdir), devgen); 	summary = 0;	switch((ulong)c->qid.path) {	default:		error(Egreg);	case Qctl:		return 0;	case Qsum:		summary = 1;		/* fall through */	case Qstate:		if(offset == 0){			for(i=0; i<Npool; i++){				poolstate[i].ptr = &poolstate[i].state[3];				poolstate[i].lim = poolstate[i].ptr + Nstate;				memset(poolstate[i].state, 0, sizeof(poolstate[i].state));				poolstate[i].summary = summary;			}			e = poolaudit(memaudit);			if(e != nil){				print("mem: %s\n", e);				error(e);			}		}		m = va;		nr = offset/8;		for(i=0; i<Npool && count >= 8; i++){			n = poolstate[i].ptr - poolstate[i].state;			poolstate[i].state[0].base = i;			poolstate[i].state[0].size = n;			if(nr >= n){				nr -= n;				continue;			}			n -= nr;			p = &poolstate[i].state[nr];			for(; --n >= 0 && (count -= 8) >= 0; m += 8, p++){				mput4(m, p->base);				mput4(m+4, p->size);			}		}		return m-(uchar*)va;	case Qevent:		while(!ismemdata(nil)){			poolevents.want = 1;			Sleep(&poolevents.r, ismemdata, nil);		}		m = va;		do{			if((count -= 4*4) < 0)				return m-(uchar*)va;			pe = poolevents.events[poolevents.rd];			mput4(m, pe.pool);			mput4(m+4, pe.pc);			mput4(m+8, pe.base);			mput4(m+12, pe.size);			m += 4*4;			if(++poolevents.rd >= nelem(poolevents.events))				poolevents.rd = 0;		}while(poolevents.rd != poolevents.wr);		poolevents.full = 0;		return m-(uchar*)va;	case Qprof:		if(offset == 0){			lock(&memprof.l);			memmove(memprof.snap, memprof.buckets, memprof.used*sizeof(memprof.buckets[0]));			memprof.snapped = memprof.used;			unlock(&memprof.l);		}		m = va;		for(i = offset/(4*4); i < memprof.snapped && (count -= 4*4) >= 0; i++){			b = &memprof.snap[i];			mput4(m, b->pool);			mput4(m+4, b->val);			mput4(m+8, b->count);			mput4(m+12, b->size);			m += 4*4;		}		return m-(uchar*)va;	case Qgc:		s = malloc(READSTR);		if(s == nil)			error(Enomem);		if(waserror()){			free(s);			nexterror();		}		snprint(s, READSTR, "runs: %lud\nsweeps: %lud\nbchain: %lud\nhalted: %lud\nepochs: %lud\ndestroy: %llud\ninspects: %llud\nbusy: %llud\nidle: %llud\nidlepass: %llud\npartial: %llud\n",			gcnruns, gcsweeps, gcbroken, gchalted, gcepochs, gcdestroys, gcinspects, gcbusy, gcidle, gcidlepass, gcpartial);		count = readstr(offset, va, count, s);		poperror();		free(s);		return count;	}} static longmemwrite(Chan *c, void *va, long count, vlong offset){	USED(offset);	USED(count);	USED(va); 	if(c->qid.type & QTDIR)		error(Eperm); 	switch((ulong)c->qid.path) {	default:		error(Egreg);	case Qctl:		error(Ebadarg);	case Qstate:		error(Eperm);	case Qprof:		mprofreset();		break;	}	return 0;} Dev memdevtab = {	'%',	"mem", 	devinit,	memattach,	memwalk,	memstat,	memopen,	devcreate,	memclose,	memread,	devbread,	memwrite,	devbwrite,	devremove,	devwstat};