/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2008 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 * * David Korn * * Phong Vo * * * ***********************************************************************/ #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