vmprofile.c revision 3e14f97f673e8a630f076077de35afdd43dc1587
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2010 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#if defined(_UWIN) && defined(_BLD_ast)
void _STUB_vmprofile(){}
#else
#include "vmhdr.h"
/* Method to profile space usage.
**
** Written by Kiem-Phong Vo, kpv@research.att.com, 03/23/94.
*/
#define PFHASH(pf) ((pf)->data.data.hash)
#define PFVM(pf) ((pf)->data.data.vm)
#define PFFILE(pf) ((pf)->data.data.fm.file)
#define PFLINE(pf) ((pf)->line)
#define PFNAME(pf) ((pf)->data.f)
#define PFNALLOC(pf) ((pf)->data.data.nalloc)
#define PFALLOC(pf) ((pf)->data.data.alloc)
#define PFNFREE(pf) ((pf)->data.data.nfree)
#define PFFREE(pf) ((pf)->data.data.free)
#define PFREGION(pf) ((pf)->data.data.region)
#define PFMAX(pf) ((pf)->data.data.fm.max)
typedef struct _pfdata_s Pfdata_t;
struct _pfdata_s
{ Vmulong_t hash; /* hash value */
union
{ char* file; /* file name */
Vmulong_t max; /* max busy space for region */
} fm;
Vmalloc_t* vm; /* region alloc from */
Pfobj_t* region; /* pointer to region record */
Vmulong_t nalloc; /* number of alloc calls */
Vmulong_t alloc; /* amount allocated */
Vmulong_t nfree; /* number of free calls */
Vmulong_t free; /* amount freed */
};
struct _pfobj_s
{ Pfobj_t* next; /* next in linked list */
int line; /* line #, 0 for name holder */
union
{
Pfdata_t data;
char f[1]; /* actual file name */
} data;
};
static Pfobj_t** Pftable; /* hash table */
#define PFTABLE 1019 /* table size */
static Vmalloc_t* Vmpf; /* heap for our own use */
#if __STD_C
static Pfobj_t* pfsearch(Vmalloc_t* vm, const char* file, int line)
#else
static Pfobj_t* pfsearch(vm, file, line)
Vmalloc_t* vm; /* region allocating from */
const char* file; /* the file issuing the allocation request */
int line; /* line number */
#endif
{
reg Pfobj_t *pf, *last;
reg Vmulong_t h;
reg int n;
reg const char* cp;
if(!Vmpf && !(Vmpf = vmopen(Vmdcheap,Vmpool,0)) )
return NIL(Pfobj_t*);
/* make hash table; PFTABLE'th slot hold regions' records */
if(!Pftable)
{ if(!(Pftable = (Pfobj_t**)vmalloc(Vmheap,(PFTABLE+1)*sizeof(Pfobj_t*))) )
return NIL(Pfobj_t*);
for(n = PFTABLE; n >= 0; --n)
Pftable[n] = NIL(Pfobj_t*);
}
/* see if it's there with a combined hash value of vm,file,line */
h = line + (((Vmulong_t)vm)>>4);
for(cp = file; *cp; ++cp)
h += (h<<7) + ((*cp)&0377) + 987654321L;
n = (int)(h%PFTABLE);
for(last = NIL(Pfobj_t*), pf = Pftable[n]; pf; last = pf, pf = pf->next)
if(PFLINE(pf) == line && PFVM(pf) == vm && strcmp(PFFILE(pf),file) == 0)
break;
/* insert if not there yet */
if(!pf)
{ reg Pfobj_t* fn;
reg Pfobj_t* pfvm;
reg Vmulong_t hn;
/* first get/construct the file name slot */
hn = 0;
for(cp = file; *cp; ++cp)
hn += (hn<<7) + ((*cp)&0377) + 987654321L;
n = (int)(hn%PFTABLE);
for(fn = Pftable[n]; fn; fn = fn->next)
if(PFLINE(fn) < 0 && strcmp(PFNAME(fn),file) == 0)
break;
if(!fn)
{ reg size_t s;
s = sizeof(Pfobj_t) - sizeof(Pfdata_t) + strlen(file) + 1;
if(!(fn = (Pfobj_t*)vmalloc(Vmheap,s)) )
return NIL(Pfobj_t*);
fn->next = Pftable[n];
Pftable[n] = fn;
PFLINE(fn) = -1;
strcpy(PFNAME(fn),file);
}
/* get region record; note that these are ordered by vm */
last = NIL(Pfobj_t*);
for(pfvm = Pftable[PFTABLE]; pfvm; last = pfvm, pfvm = pfvm->next)
if(vm >= PFVM(pfvm))
break;
if(!pfvm || PFVM(pfvm) > vm)
{ if(!(pfvm = (Pfobj_t*)vmalloc(Vmpf,sizeof(Pfobj_t))) )
return NIL(Pfobj_t*);
if(last)
{ pfvm->next = last->next;
last->next = pfvm;
}
else
{ pfvm->next = Pftable[PFTABLE];
Pftable[PFTABLE] = pfvm;
}
PFNALLOC(pfvm) = PFALLOC(pfvm) = 0;
PFNFREE(pfvm) = PFFREE(pfvm) = 0;
PFMAX(pfvm) = 0;
PFVM(pfvm) = vm;
PFLINE(pfvm) = 0;
}
if(!(pf = (Pfobj_t*)vmalloc(Vmpf,sizeof(Pfobj_t))) )
return NIL(Pfobj_t*);
n = (int)(h%PFTABLE);
pf->next = Pftable[n];
Pftable[n] = pf;
PFLINE(pf) = line;
PFFILE(pf) = PFNAME(fn);
PFREGION(pf) = pfvm;
PFVM(pf) = vm;
PFNALLOC(pf) = 0;
PFALLOC(pf) = 0;
PFNFREE(pf) = 0;
PFFREE(pf) = 0;
PFHASH(pf) = h;
}
else if(last) /* do a move-to-front */
{ last->next = pf->next;
pf->next = Pftable[n];
Pftable[n] = pf;
}
return pf;
}
#if __STD_C
static void pfclose(Vmalloc_t* vm)
#else
static void pfclose(vm)
Vmalloc_t* vm;
#endif
{
reg int n;
reg Pfobj_t *pf, *next, *last;
/* free all records related to region vm */
for(n = PFTABLE; n >= 0; --n)
{ for(last = NIL(Pfobj_t*), pf = Pftable[n]; pf; )
{ next = pf->next;
if(PFLINE(pf) >= 0 && PFVM(pf) == vm)
{ if(last)
last->next = next;
else Pftable[n] = next;
vmfree(Vmpf,pf);
}
else last = pf;
pf = next;
}
}
}
#if __STD_C
static void pfsetinfo(Vmalloc_t* vm, Vmuchar_t* data, size_t size, const char* file, int line)
#else
static void pfsetinfo(vm, data, size, file, line)
Vmalloc_t* vm;
Vmuchar_t* data;
size_t size;
const char* file;
int line;
#endif
{
reg Pfobj_t* pf;
reg Vmulong_t s;
/* let vmclose knows that there are records for region vm */
_Vmpfclose = pfclose;
if(!file || line <= 0)
{ file = "";
line = 0;
}
if((pf = pfsearch(vm,file,line)) )
{ PFALLOC(pf) += size;
PFNALLOC(pf) += 1;
}
PFOBJ(data) = pf;
PFSIZE(data) = size;
if(pf)
{ /* update region statistics */
pf = PFREGION(pf);
PFALLOC(pf) += size;
PFNALLOC(pf) += 1;
if((s = PFALLOC(pf) - PFFREE(pf)) > PFMAX(pf) )
PFMAX(pf) = s;
}
}
/* sort by file names and line numbers */
#if __STD_C
static Pfobj_t* pfsort(Pfobj_t* pf)
#else
static Pfobj_t* pfsort(pf)
Pfobj_t* pf;
#endif
{
reg Pfobj_t *one, *two, *next;
reg int cmp;
if(!pf->next)
return pf;
/* partition to two equal size lists */
one = two = NIL(Pfobj_t*);
while(pf)
{ next = pf->next;
pf->next = one;
one = pf;
if((pf = next) )
{ next = pf->next;
pf->next = two;
two = pf;
pf = next;
}
}
/* sort and merge the lists */
one = pfsort(one);
two = pfsort(two);
for(pf = next = NIL(Pfobj_t*);; )
{ /* make sure that the "<>" file comes first */
if(PFLINE(one) == 0 && PFLINE(two) == 0)
cmp = PFVM(one) > PFVM(two) ? 1 : -1;
else if(PFLINE(one) == 0)
cmp = -1;
else if(PFLINE(two) == 0)
cmp = 1;
else if((cmp = strcmp(PFFILE(one),PFFILE(two))) == 0)
{ cmp = PFLINE(one) - PFLINE(two);
if(cmp == 0)
cmp = PFVM(one) > PFVM(two) ? 1 : -1;
}
if(cmp < 0)
{ if(!pf)
pf = one;
else next->next = one;
next = one;
if(!(one = one->next) )
{ if(two)
next->next = two;
return pf;
}
}
else
{ if(!pf)
pf = two;
else next->next = two;
next = two;
if(!(two = two->next) )
{ if(one)
next->next = one;
return pf;
}
}
}
}
#if __STD_C
static char* pfsummary(char* buf, Vmulong_t na, Vmulong_t sa,
Vmulong_t nf, Vmulong_t sf, Vmulong_t max, Vmulong_t size)
#else
static char* pfsummary(buf, na, sa, nf, sf, max, size)
char* buf;
Vmulong_t na;
Vmulong_t sa;
Vmulong_t nf;
Vmulong_t sf;
Vmulong_t max;
Vmulong_t size;
#endif
{
buf = (*_Vmstrcpy)(buf,"n_alloc", '=');
buf = (*_Vmstrcpy)(buf, (*_Vmitoa)(na,-1), ':');
buf = (*_Vmstrcpy)(buf,"n_free", '=');
buf = (*_Vmstrcpy)(buf, (*_Vmitoa)(nf,-1), ':');
buf = (*_Vmstrcpy)(buf,"s_alloc", '=');
buf = (*_Vmstrcpy)(buf, (*_Vmitoa)(sa,-1), ':');
buf = (*_Vmstrcpy)(buf,"s_free", '=');
buf = (*_Vmstrcpy)(buf, (*_Vmitoa)(sf,-1), ':');
if(max > 0)
{ buf = (*_Vmstrcpy)(buf,"max_busy", '=');
buf = (*_Vmstrcpy)(buf, (*_Vmitoa)(max,-1), ':');
buf = (*_Vmstrcpy)(buf,"extent", '=');
buf = (*_Vmstrcpy)(buf, (*_Vmitoa)(size,-1), ':');
}
*buf++ = '\n';
return buf;
}
/* print profile data */
#if __STD_C
int vmprofile(Vmalloc_t* vm, int fd)
#else
int vmprofile(vm, fd)
Vmalloc_t* vm;
int fd;
#endif
{
reg Pfobj_t *pf, *list, *next, *last;
reg int n;
reg Vmulong_t nalloc, alloc, nfree, free;
reg Seg_t* seg;
char buf[1024], *bufp, *endbuf;
#define INITBUF() (bufp = buf, endbuf = buf+sizeof(buf)-128)
#define CHKBUF() (bufp >= endbuf ? (write(fd,buf,bufp-buf), bufp=buf) : bufp)
#define FLSBUF() (bufp > buf ? write(fd,buf,bufp-buf) : 0)
if(fd < 0)
return -1;
/* initialize functions from vmtrace.c that we use below */
if((n = vmtrace(-1)) >= 0)
vmtrace(n);
alloc = free = nalloc = nfree = 0;
list = NIL(Pfobj_t*);
for(n = PFTABLE-1; n >= 0; --n)
{ for(pf = Pftable[n], last = NIL(Pfobj_t*); pf; )
{ next = pf->next;
if(PFLINE(pf) < 0 || (vm && vm != PFVM(pf)) )
{ last = pf;
goto next_pf;
}
/* remove from hash table */
if(last)
last->next = next;
else Pftable[n] = next;
/* put on output list */
pf->next = list;
list = pf;
nalloc += PFNALLOC(pf);
alloc += PFALLOC(pf);
nfree += PFNFREE(pf);
free += PFFREE(pf);
next_pf:
pf = next;
}
}
INITBUF();
bufp = (*_Vmstrcpy)(bufp,"ALLOCATION USAGE SUMMARY", ':');
bufp = pfsummary(bufp,nalloc,alloc,nfree,free,0,0);
/* print regions' summary data */
for(pf = Pftable[PFTABLE]; pf; pf = pf->next)
{ if(vm && PFVM(pf) != vm)
continue;
alloc = 0;
for(seg = PFVM(pf)->data->seg; seg; seg = seg->next)
alloc += seg->extent;
bufp = (*_Vmstrcpy)(bufp,"region", '=');
bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(PFVM(pf)),0), ':');
bufp = pfsummary(bufp,PFNALLOC(pf),PFALLOC(pf),
PFNFREE(pf),PFFREE(pf),PFMAX(pf),alloc);
}
/* sort then output detailed profile */
list = pfsort(list);
for(pf = list; pf; )
{ /* compute summary for file */
alloc = free = nalloc = nfree = 0;
for(last = pf; last; last = last->next)
{ if(strcmp(PFFILE(last),PFFILE(pf)) != 0)
break;
nalloc += PFNALLOC(pf);
alloc += PFALLOC(last);
nfree += PFNFREE(last);
free += PFFREE(last);
}
CHKBUF();
bufp = (*_Vmstrcpy)(bufp,"file",'=');
bufp = (*_Vmstrcpy)(bufp,PFFILE(pf)[0] ? PFFILE(pf) : "<>" ,':');
bufp = pfsummary(bufp,nalloc,alloc,nfree,free,0,0);
while(pf != last) /* detailed data */
{ CHKBUF();
bufp = (*_Vmstrcpy)(bufp,"\tline",'=');
bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(PFLINE(pf),-1), ':');
bufp = (*_Vmstrcpy)(bufp, "region", '=');
bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(PFVM(pf)),0), ':');
bufp = pfsummary(bufp,PFNALLOC(pf),PFALLOC(pf),
PFNFREE(pf),PFFREE(pf),0,0);
/* reinsert into hash table */
next = pf->next;
n = (int)(PFHASH(pf)%PFTABLE);
pf->next = Pftable[n];
Pftable[n] = pf;
pf = next;
}
}
FLSBUF();
return 0;
}
#if __STD_C
static Void_t* pfalloc(Vmalloc_t* vm, size_t size)
#else
static Void_t* pfalloc(vm, size)
Vmalloc_t* vm;
size_t size;
#endif
{
reg size_t s;
reg Void_t* data;
reg char* file;
reg int line, local, inuse;
reg Void_t* func;
reg Vmdata_t* vd = vm->data;
VMFLF(vm,file,line,func);
SETINUSE(vd, inuse);
if(!(local = vd->mode&VM_TRUST) )
{ GETLOCAL(vd, local);
if(ISLOCK(vd, local))
{ CLRINUSE(vd, inuse);
return NIL(Void_t*);
}
SETLOCK(vd, local);
}
s = ROUND(size,ALIGN) + PF_EXTRA;
if(!(data = KPVALLOC(vm,s,(*(Vmbest->allocf))) ) )
goto done;
pfsetinfo(vm,(Vmuchar_t*)data,size,file,line);
if(!local && (vd->mode&VM_TRACE) && _Vmtrace)
{ vm->file = file; vm->line = line; vm->func = func;
(*_Vmtrace)(vm,NIL(Vmuchar_t*),(Vmuchar_t*)data,size,0);
}
done:
CLRLOCK(vd, local);
ANNOUNCE(local, vm, VM_ALLOC, (Void_t*)data, vm->disc);
CLRINUSE(vd, inuse);
return data;
}
#if __STD_C
static int pffree(Vmalloc_t* vm, Void_t* data)
#else
static int pffree(vm, data)
Vmalloc_t* vm;
Void_t* data;
#endif
{
reg Pfobj_t* pf;
reg size_t s;
reg char* file;
reg int line, rv, local, inuse;
reg Void_t* func;
reg Vmdata_t* vd = vm->data;
VMFLF(vm,file,line,func);
if(!data)
return 0;
SETINUSE(vd, inuse);
if(!(local = vd->mode&VM_TRUST) )
{ GETLOCAL(vd,local);
if(ISLOCK(vd,local))
{ CLRINUSE(vd, inuse);
return -1;
}
SETLOCK(vd,local);
}
if(KPVADDR(vm,data,Vmbest->addrf) != 0 )
{ if(vm->disc->exceptf)
(void)(*vm->disc->exceptf)(vm,VM_BADADDR,data,vm->disc);
CLRLOCK(vd,0);
CLRINUSE(vd, inuse);
return -1;
}
pf = PFOBJ(data);
s = PFSIZE(data);
if(pf)
{ PFNFREE(pf) += 1;
PFFREE(pf) += s;
pf = PFREGION(pf);
PFNFREE(pf) += 1;
PFFREE(pf) += s;
}
if(!local && (vd->mode&VM_TRACE) && _Vmtrace)
{ vm->file = file; vm->line = line; vm->func = func;
(*_Vmtrace)(vm,(Vmuchar_t*)data,NIL(Vmuchar_t*),s,0);
}
rv = KPVFREE((vm), (Void_t*)data, (*Vmbest->freef));
CLRLOCK(vd,local);
ANNOUNCE(local, vm, VM_FREE, data, vm->disc);
CLRINUSE(vd, inuse);
return rv;
}
#if __STD_C
static Void_t* pfresize(Vmalloc_t* vm, Void_t* data, size_t size, int type)
#else
static Void_t* pfresize(vm, data, size, type)
Vmalloc_t* vm;
Void_t* data;
size_t size;
int type;
#endif
{
reg Pfobj_t* pf;
reg size_t s;
reg size_t news;
reg Void_t* addr;
reg char* file;
reg int line, local, inuse;
reg Void_t* func;
reg size_t oldsize;
reg Vmdata_t* vd = vm->data;
SETINUSE(vd, inuse);
if(!data)
{ oldsize = 0;
addr = pfalloc(vm,size);
goto done;
}
if(size == 0)
{ (void)pffree(vm,data);
CLRINUSE(vd, inuse);
return NIL(Void_t*);
}
VMFLF(vm,file,line,func);
if(!(local = vd->mode&VM_TRUST))
{ GETLOCAL(vd, local);
if(ISLOCK(vd, local))
{ CLRINUSE(vd, inuse);
return NIL(Void_t*);
}
SETLOCK(vd, local);
}
if(KPVADDR(vm,data,Vmbest->addrf) != 0 )
{ if(vm->disc->exceptf)
(void)(*vm->disc->exceptf)(vm,VM_BADADDR,data,vm->disc);
CLRLOCK(vd, local);
CLRINUSE(vd, inuse);
return NIL(Void_t*);
}
pf = PFOBJ(data);
s = oldsize = PFSIZE(data);
news = ROUND(size,ALIGN) + PF_EXTRA;
if((addr = KPVRESIZE(vm,data,news,(type&~VM_RSZERO),Vmbest->resizef)) )
{ if(pf)
{ PFFREE(pf) += s;
PFNFREE(pf) += 1;
pf = PFREGION(pf);
PFFREE(pf) += s;
PFNFREE(pf) += 1;
pfsetinfo(vm,(Vmuchar_t*)addr,size,file,line);
}
if(!local && (vd->mode&VM_TRACE) && _Vmtrace)
{ vm->file = file; vm->line = line; vm->func = func;
(*_Vmtrace)(vm,(Vmuchar_t*)data,(Vmuchar_t*)addr,size,0);
}
}
else if(pf) /* reset old info */
{ PFALLOC(pf) -= s;
PFNALLOC(pf) -= 1;
pf = PFREGION(pf);
PFALLOC(pf) -= s;
PFNALLOC(pf) -= 1;
file = PFFILE(pf);
line = PFLINE(pf);
pfsetinfo(vm,(Vmuchar_t*)data,s,file,line);
}
CLRLOCK(vd, local);
ANNOUNCE(local, vm, VM_RESIZE, (Void_t*)addr, vm->disc);
done: if(addr && (type&VM_RSZERO) && oldsize < size)
{ reg Vmuchar_t *d = (Vmuchar_t*)addr+oldsize, *ed = (Vmuchar_t*)addr+size;
do { *d++ = 0; } while(d < ed);
}
CLRINUSE(vd, inuse);
return addr;
}
#if __STD_C
static long pfsize(Vmalloc_t* vm, Void_t* addr)
#else
static long pfsize(vm, addr)
Vmalloc_t* vm;
Void_t* addr;
#endif
{
return (*Vmbest->addrf)(vm,addr) != 0 ? -1L : (long)PFSIZE(addr);
}
#if __STD_C
static long pfaddr(Vmalloc_t* vm, Void_t* addr)
#else
static long pfaddr(vm, addr)
Vmalloc_t* vm;
Void_t* addr;
#endif
{
return (*Vmbest->addrf)(vm,addr);
}
#if __STD_C
static int pfcompact(Vmalloc_t* vm)
#else
static int pfcompact(vm)
Vmalloc_t* vm;
#endif
{
return (*Vmbest->compactf)(vm);
}
#if __STD_C
static Void_t* pfalign(Vmalloc_t* vm, size_t size, size_t align)
#else
static Void_t* pfalign(vm, size, align)
Vmalloc_t* vm;
size_t size;
size_t align;
#endif
{
reg size_t s;
reg Void_t* data;
reg char* file;
reg int line, local, inuse;
reg Void_t* func;
reg Vmdata_t* vd = vm->data;
VMFLF(vm,file,line,func);
SETINUSE(vd, inuse);
if(!(local = vd->mode&VM_TRUST) )
{ GETLOCAL(vd,local);
if(ISLOCK(vd, local))
{ CLRINUSE(vd, inuse);
return NIL(Void_t*);
}
SETLOCK(vd, local);
}
s = (size <= TINYSIZE ? TINYSIZE : ROUND(size,ALIGN)) + PF_EXTRA;
if(!(data = KPVALIGN(vm,s,align,Vmbest->alignf)) )
goto done;
pfsetinfo(vm,(Vmuchar_t*)data,size,file,line);
if(!local && (vd->mode&VM_TRACE) && _Vmtrace)
{ vm->file = file; vm->line = line; vm->func = func;
(*_Vmtrace)(vm,NIL(Vmuchar_t*),(Vmuchar_t*)data,size,align);
}
done:
CLRLOCK(vd, local);
ANNOUNCE(local, vm, VM_ALLOC, data, vm->disc);
CLRINUSE(vd, inuse);
return data;
}
static Vmethod_t _Vmprofile =
{
pfalloc,
pfresize,
pffree,
pfaddr,
pfsize,
pfcompact,
pfalign,
VM_MTPROFILE
};
__DEFINE__(Vmethod_t*,Vmprofile,&_Vmprofile);
#ifdef NoF
NoF(vmprofile)
#endif
#endif