diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /emu/port/devmem.c | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'emu/port/devmem.c')
| -rw-r--r-- | emu/port/devmem.c | 485 |
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 +}; |
