348 lines
5.6 KiB
C
348 lines
5.6 KiB
C
|
#include "u.h"
|
||
|
#include "../port/lib.h"
|
||
|
#include "mem.h"
|
||
|
#include "dat.h"
|
||
|
#include "fns.h"
|
||
|
#include "../port/error.h"
|
||
|
|
||
|
typedef struct Mprof Mprof;
|
||
|
typedef struct Mevents Mevents;
|
||
|
typedef struct Mevent Mevent;
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
Qdir,
|
||
|
Qctl,
|
||
|
Qevent,
|
||
|
Qprof,
|
||
|
Qsum,
|
||
|
};
|
||
|
|
||
|
static
|
||
|
Dirtab memdir[] =
|
||
|
{
|
||
|
".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
|
||
|
"memctl", {Qctl}, 0, 0664,
|
||
|
"memevent", {Qevent}, 0, 0444,
|
||
|
"memprof", {Qprof}, 0, 0444,
|
||
|
"memsum", {Qsum}, 0, 0444,
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
Nevent = 10000,
|
||
|
BucketLg2= 15, /* for 512k kernel, allows allocation every 16 bytes */
|
||
|
Nbucket= 1<<BucketLg2,
|
||
|
BucketMask= Nbucket-1,
|
||
|
Noverflow= 100, /* number of entries for duplicates */
|
||
|
Profreclen= 3*4, /* tag, na, busy */
|
||
|
Evreclen= 4*4, /* 0, tag, koff, size */
|
||
|
|
||
|
MaxInt= 0x7FFFFFFF,
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* allocation profile
|
||
|
*/
|
||
|
struct Mprof{
|
||
|
uint32_t tag; /* usually low-order 32 bits of pc */
|
||
|
int na; /* active allocations */
|
||
|
int busy; /* bytes currently allocated */
|
||
|
uint32_t ovfl; /* !=0, next index on overflow */
|
||
|
};
|
||
|
|
||
|
static struct
|
||
|
{
|
||
|
Mprof bucket[Nbucket+Noverflow+1]; /* last entry as catchall */
|
||
|
int novfl;
|
||
|
Lock ovlk;
|
||
|
} memprof;
|
||
|
|
||
|
/*
|
||
|
* allocation events
|
||
|
*/
|
||
|
struct Mevent
|
||
|
{
|
||
|
uint32_t tag; /* usually low-order 32 bits of pc */
|
||
|
uint32_t koff; /* base-KZERO */
|
||
|
int size; /* > 0, alloc; < 0, free */
|
||
|
};
|
||
|
|
||
|
static struct Mevents
|
||
|
{
|
||
|
Lock;
|
||
|
Ref;
|
||
|
Rendez r;
|
||
|
Mevent events[Nevent];
|
||
|
uint32_t rd;
|
||
|
uint32_t wr;
|
||
|
int want;
|
||
|
uint32_t lost;
|
||
|
} memevents;
|
||
|
|
||
|
static Ref monitoring;
|
||
|
|
||
|
extern void setmemprof(void (*)(void*, uint32_t, usize, int)); /* qmalloc.c */
|
||
|
|
||
|
static void
|
||
|
aadd(int *addr, int delta)
|
||
|
{
|
||
|
int value;
|
||
|
|
||
|
do
|
||
|
value = *addr;
|
||
|
while(!CASW(addr, value, value+delta));
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
isnonempty(void *v)
|
||
|
{
|
||
|
Mevents *evs;
|
||
|
|
||
|
evs = v;
|
||
|
return evs->rd != evs->wr;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
isnotfull(Mevents *evs)
|
||
|
{
|
||
|
return (evs->wr - evs->rd) < Nevent;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
addmemevent(void *a, uint32_t tag, usize nb, int w)
|
||
|
{
|
||
|
Mevents *evs;
|
||
|
Mevent e;
|
||
|
int empty;
|
||
|
|
||
|
e.tag = tag;
|
||
|
e.koff = (uintptr_t)a - KZERO;
|
||
|
if(nb > MaxInt)
|
||
|
nb = MaxInt;
|
||
|
e.size = w < 0? -nb: nb;
|
||
|
|
||
|
evs = &memevents;
|
||
|
ilock(evs);
|
||
|
if(isnotfull(evs)){
|
||
|
empty = evs->rd == evs->wr;
|
||
|
evs->events[evs->wr++%nelem(evs->events)] = e;
|
||
|
}else{
|
||
|
evs->lost++;
|
||
|
empty = 0;
|
||
|
}
|
||
|
iunlock(evs);
|
||
|
if(empty)
|
||
|
wakeup(&evs->r);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
mprofmonitor(void *a, uint32_t tag, usize nb, int w)
|
||
|
{
|
||
|
Mprof *p;
|
||
|
|
||
|
if(memevents.ref != 0)
|
||
|
addmemevent(a, tag, nb, w);
|
||
|
p = memprof.bucket;
|
||
|
for(int i = 0; i < nelem(memprof.bucket)-1; i++, p++){
|
||
|
if(p->tag == 0 || p->tag == tag)
|
||
|
break;
|
||
|
}
|
||
|
if(p->tag != 0 && p->tag != tag)
|
||
|
p->tag = ~0;
|
||
|
else
|
||
|
p->tag = tag;
|
||
|
if(w == 2) /* tag change */
|
||
|
return;
|
||
|
if(w < 0){
|
||
|
aadd(&p->na, -1);
|
||
|
aadd(&p->busy, -nb);
|
||
|
}else{
|
||
|
aadd(&p->na, 1);
|
||
|
aadd(&p->busy, nb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
mput4(uint8_t *m, uint32_t v)
|
||
|
{
|
||
|
m[0] = v>>24;
|
||
|
m[1] = v>>16;
|
||
|
m[2] = v>>8;
|
||
|
m[3] = v;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
memprofinit(void)
|
||
|
{
|
||
|
incref(&monitoring);
|
||
|
setmemprof(mprofmonitor);
|
||
|
}
|
||
|
|
||
|
static Chan*
|
||
|
memattach(Chan *c, Chan *ac, char *spec, int flags)
|
||
|
{
|
||
|
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 long
|
||
|
memstat(Chan *c, uint8_t *db, long n)
|
||
|
{
|
||
|
return devstat(c, db, n, memdir, nelem(memdir), devgen);
|
||
|
}
|
||
|
|
||
|
static Chan*
|
||
|
memopen(Chan *c, int omode)
|
||
|
{
|
||
|
Mevents *evs;
|
||
|
|
||
|
c = devopen(c, omode, memdir, nelem(memdir), devgen);
|
||
|
switch((uint32_t)c->qid.path){
|
||
|
case Qevent:
|
||
|
evs = &memevents;
|
||
|
if(incref(evs) != 1){
|
||
|
decref(evs);
|
||
|
c->flag &= ~COPEN;
|
||
|
error(Einuse);
|
||
|
}
|
||
|
evs->want = 0;
|
||
|
evs->lost = 0;
|
||
|
incref(&monitoring);
|
||
|
setmemprof(mprofmonitor);
|
||
|
break;
|
||
|
case Qprof:
|
||
|
break;
|
||
|
}
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
memclose(Chan *c)
|
||
|
{
|
||
|
if((c->flag & COPEN) == 0)
|
||
|
return;
|
||
|
switch((uint32_t)c->qid.path) {
|
||
|
case Qevent:
|
||
|
if(decref(&monitoring) == 0)
|
||
|
setmemprof(nil);
|
||
|
decref(&memevents);
|
||
|
break;
|
||
|
case Qprof:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
static long
|
||
|
memread(Chan *c, void *va, long count, int64_t offset)
|
||
|
{
|
||
|
uint8_t *a;
|
||
|
int i;
|
||
|
Mevent *pe;
|
||
|
Mevents *evs;
|
||
|
Mprof *p;
|
||
|
|
||
|
if(c->qid.type & QTDIR)
|
||
|
return devdirread(c, va, count, memdir, nelem(memdir), devgen);
|
||
|
|
||
|
switch((uint32_t)c->qid.path) {
|
||
|
default:
|
||
|
error(Egreg);
|
||
|
case Qctl:
|
||
|
return 0;
|
||
|
case Qsum:
|
||
|
return mallocreadsummary(c, va, count, offset);
|
||
|
case Qevent:
|
||
|
evs = &memevents;
|
||
|
while(!isnonempty(evs)){
|
||
|
evs->want = 1;
|
||
|
sleep(&evs->r, isnonempty, evs);
|
||
|
}
|
||
|
a = va;
|
||
|
do{
|
||
|
if((count -= Evreclen) < 0)
|
||
|
break;
|
||
|
pe = &evs->events[evs->rd%nelem(evs->events)];
|
||
|
mput4(a+0, 0);
|
||
|
mput4(a+4, pe->tag);
|
||
|
mput4(a+8, pe->koff);
|
||
|
mput4(a+12, pe->size);
|
||
|
a += Evreclen;
|
||
|
}while(++evs->rd != evs->wr);
|
||
|
return a-(uint8_t*)va;
|
||
|
case Qprof:
|
||
|
a = va;
|
||
|
for(i = offset/Profreclen; i < nelem(memprof.bucket); i++){
|
||
|
p = &memprof.bucket[i];
|
||
|
if((p->tag|p->na|p->busy) != 0){
|
||
|
if((count -= Profreclen) < 0)
|
||
|
break;
|
||
|
mput4(a+0, p->tag);
|
||
|
mput4(a+4, p->na);
|
||
|
mput4(a+8, p->busy);
|
||
|
a += Profreclen;
|
||
|
}
|
||
|
}
|
||
|
return a-(uint8_t*)va;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static long
|
||
|
memwrite(Chan *c, void *a, long n, int64_t)
|
||
|
{
|
||
|
Cmdbuf *cb;
|
||
|
|
||
|
if(c->qid.type & QTDIR)
|
||
|
error(Eperm);
|
||
|
|
||
|
switch((uint32_t)c->qid.path) {
|
||
|
default:
|
||
|
error(Egreg);
|
||
|
case Qctl:
|
||
|
cb = parsecmd(a, n);
|
||
|
if(waserror()){
|
||
|
free(cb);
|
||
|
nexterror();
|
||
|
}
|
||
|
if(cb->nf == 1 && strcmp(cb->f[0], "start") == 0){
|
||
|
if(incref(&monitoring) == 1)
|
||
|
setmemprof(mprofmonitor);
|
||
|
}else if(cb->nf == 1 && strcmp(cb->f[0], "stop") == 0){
|
||
|
if(decref(&monitoring) == 0)
|
||
|
setmemprof(nil);
|
||
|
}else
|
||
|
cmderror(cb, "unknown command");
|
||
|
poperror();
|
||
|
free(cb);
|
||
|
break;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
Dev memdevtab = {
|
||
|
'%',
|
||
|
"mem",
|
||
|
|
||
|
devreset,
|
||
|
memprofinit, //devinit,
|
||
|
devshutdown,
|
||
|
memattach,
|
||
|
memwalk,
|
||
|
memstat,
|
||
|
memopen,
|
||
|
devcreate,
|
||
|
memclose,
|
||
|
memread,
|
||
|
devbread,
|
||
|
memwrite,
|
||
|
devbwrite,
|
||
|
devremove,
|
||
|
devwstat
|
||
|
};
|