summaryrefslogtreecommitdiff
path: root/emu/port/devmem.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /emu/port/devmem.c
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'emu/port/devmem.c')
-rw-r--r--emu/port/devmem.c485
1 files changed, 485 insertions, 0 deletions
diff --git a/emu/port/devmem.c b/emu/port/devmem.c
new file mode 100644
index 00000000..6c09aaad
--- /dev/null
+++ b/emu/port/devmem.c
@@ -0,0 +1,485 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "interp.h"
+
+enum
+{
+ Qdir,
+ Qctl,
+ Qstate,
+ Qsum,
+ Qevent,
+ Qprof,
+ Qheap,
+ Qgc
+};
+
+static
+Dirtab 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 void
+mprofreset(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 void
+mprofmonitor(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 int
+ismemdata(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 void
+mput4(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 int
+memstat(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 void
+memclose(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 long
+memread(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;
+ }
+ return 0;
+}
+
+static long
+memwrite(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
+};