/*********************************************************************** * * * 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_vmdebug(){} #else #include "vmhdr.h" /* Method to help with debugging. This does rigorous checks on ** addresses and arena integrity. ** ** Written by Kiem-Phong Vo, kpv@research.att.com, 01/16/94. */ /* structure to keep track of file names */ typedef struct _dbfile_s Dbfile_t; struct _dbfile_s { Dbfile_t* next; char file[1]; }; static Dbfile_t* Dbfile; /* global watch list */ #define S_WATCH 32 static int Dbnwatch; static Void_t* Dbwatch[S_WATCH]; /* types of warnings reported by dbwarn() */ #define DB_CHECK 0 #define DB_ALLOC 1 #define DB_FREE 2 #define DB_RESIZE 3 #define DB_WATCH 4 #define DB_RESIZED 5 #define LONGV(x) ((Vmulong_t)(x)) static int Dbinit = 0; #define DBINIT() (Dbinit ? 0 : (dbinit(), Dbinit=1) ) static void dbinit() { int fd; if((fd = vmtrace(-1)) >= 0) vmtrace(fd); } static int Dbfd = 2; /* default warning file descriptor */ #if __STD_C int vmdebug(int fd) #else int vmdebug(fd) int fd; #endif { int old = Dbfd; Dbfd = fd; return old; } /* just an entry point to make it easy to set break point */ #if __STD_C static void vmdbwarn(Vmalloc_t* vm, char* mesg, int n) #else static void vmdbwarn(vm, mesg, n) Vmalloc_t* vm; char* mesg; int n; #endif { reg Vmdata_t* vd = vm->data; write(Dbfd,mesg,n); if(vd->mode&VM_DBABORT) abort(); } /* issue a warning of some type */ #if __STD_C static void dbwarn(Vmalloc_t* vm, Void_t* data, int where, const char* file, int line, const Void_t* func, int type) #else static void dbwarn(vm, data, where, file, line, func, type) Vmalloc_t* vm; /* region holding the block */ Void_t* data; /* data block */ int where; /* byte that was corrupted */ const char* file; /* file where call originates */ int line; /* line number of call */ const Void_t* func; /* function called from */ int type; /* operation being done */ #endif { char buf[1024], *bufp, *endbuf, *s; #define SLOP 64 /* enough for a message and an int */ DBINIT(); bufp = buf; endbuf = buf + sizeof(buf); if(type == DB_ALLOC) bufp = (*_Vmstrcpy)(bufp, "alloc error", ':'); else if(type == DB_FREE) bufp = (*_Vmstrcpy)(bufp, "free error", ':'); else if(type == DB_RESIZE) bufp = (*_Vmstrcpy)(bufp, "resize error", ':'); else if(type == DB_CHECK) bufp = (*_Vmstrcpy)(bufp, "corrupted data", ':'); else if(type == DB_WATCH) bufp = (*_Vmstrcpy)(bufp, "alert", ':'); /* region info */ bufp = (*_Vmstrcpy)(bufp, "region", '='); bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(vm), 0), ':'); if(data) { bufp = (*_Vmstrcpy)(bufp,"block",'='); bufp = (*_Vmstrcpy)(bufp,(*_Vmitoa)(VLONG(data),0),':'); } if(!data) { if(where == DB_ALLOC) bufp = (*_Vmstrcpy)(bufp, "can't get memory", ':'); else bufp = (*_Vmstrcpy)(bufp, "region is locked", ':'); } else if(type == DB_FREE || type == DB_RESIZE) { if(where == 0) bufp = (*_Vmstrcpy)(bufp, "unallocated block", ':'); else bufp = (*_Vmstrcpy)(bufp, "already freed", ':'); } else if(type == DB_WATCH) { bufp = (*_Vmstrcpy)(bufp, "size", '='); bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(DBSIZE(data),-1), ':'); if(where == DB_ALLOC) bufp = (*_Vmstrcpy)(bufp,"just allocated", ':'); else if(where == DB_FREE) bufp = (*_Vmstrcpy)(bufp,"being freed", ':'); else if(where == DB_RESIZE) bufp = (*_Vmstrcpy)(bufp,"being resized", ':'); else if(where == DB_RESIZED) bufp = (*_Vmstrcpy)(bufp,"just resized", ':'); } else if(type == DB_CHECK) { bufp = (*_Vmstrcpy)(bufp, "bad byte at", '='); bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(LONGV(where),-1), ':'); if((s = DBFILE(data)) && (bufp + strlen(s) + SLOP) < endbuf) { bufp = (*_Vmstrcpy)(bufp,"allocated at", '='); bufp = (*_Vmstrcpy)(bufp, s, ','); bufp = (*_Vmstrcpy)(bufp,(*_Vmitoa)(LONGV(DBLINE(data)),-1),':'); } } /* location where offending call originates from */ if(file && file[0] && line > 0 && (bufp + strlen(file) + SLOP) < endbuf) { bufp = (*_Vmstrcpy)(bufp, "detected at", '='); bufp = (*_Vmstrcpy)(bufp, file, ','); bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(LONGV(line),-1), ','); bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(func),-1), ':'); } *bufp++ = '\n'; *bufp = '\0'; vmdbwarn(vm,buf,(bufp-buf)); } /* check for watched address and issue warnings */ #if __STD_C static void dbwatch(Vmalloc_t* vm, Void_t* data, const char* file, int line, const Void_t* func, int type) #else static void dbwatch(vm, data, file, line, func, type) Vmalloc_t* vm; Void_t* data; const char* file; int line; const Void_t* func; int type; #endif { reg int n; for(n = Dbnwatch; n >= 0; --n) { if(Dbwatch[n] == data) { dbwarn(vm,data,type,file,line,func,DB_WATCH); return; } } } /* record information about the block */ #if __STD_C static void dbsetinfo(Vmuchar_t* data, size_t size, const char* file, int line) #else static void dbsetinfo(data, size, file, line) Vmuchar_t* data; /* real address not the one from Vmbest */ size_t size; /* the actual requested size */ const char* file; /* file where the request came from */ int line; /* and line number */ #endif { reg Vmuchar_t *begp, *endp; reg Dbfile_t *last, *db; DBINIT(); /* find the file structure */ if(!file || !file[0]) db = NIL(Dbfile_t*); else { for(last = NIL(Dbfile_t*), db = Dbfile; db; last = db, db = db->next) if(strcmp(db->file,file) == 0) break; if(!db) { db = (Dbfile_t*)vmalloc(Vmheap,sizeof(Dbfile_t)+strlen(file)); if(db) { (*_Vmstrcpy)(db->file,file,0); db->next = Dbfile; Dbfile = db->next; } } else if(last) /* move-to-front heuristic */ { last->next = db->next; db->next = Dbfile; Dbfile = db->next; } } DBSETFL(data,(db ? db->file : NIL(char*)),line); DBSIZE(data) = size; DBSEG(data) = SEG(DBBLOCK(data)); DBHEAD(data,begp,endp); while(begp < endp) *begp++ = DB_MAGIC; DBTAIL(data,begp,endp); while(begp < endp) *begp++ = DB_MAGIC; } /* Check to see if an address is in some data block of a region. ** This returns -(offset+1) if block is already freed, +(offset+1) ** if block is live, 0 if no match. */ #if __STD_C static long dbaddr(Vmalloc_t* vm, Void_t* addr) #else static long dbaddr(vm, addr) Vmalloc_t* vm; Void_t* addr; #endif { reg Block_t *b, *endb; reg Seg_t* seg; reg Vmuchar_t* data; reg long offset = -1L; reg Vmdata_t* vd = vm->data; reg int local, inuse; SETINUSE(vd, inuse); GETLOCAL(vd,local); if(ISLOCK(vd,local) || !addr) { CLRINUSE(vd, inuse); return -1L; } SETLOCK(vd,local); b = endb = NIL(Block_t*); for(seg = vd->seg; seg; seg = seg->next) { b = SEGBLOCK(seg); endb = (Block_t*)(seg->baddr - sizeof(Head_t)); if((Vmuchar_t*)addr > (Vmuchar_t*)b && (Vmuchar_t*)addr < (Vmuchar_t*)endb) break; } if(!seg) goto done; if(local) /* must be vmfree or vmresize checking address */ { if(DBSEG(addr) == seg) { b = DBBLOCK(addr); if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) ) offset = 0; else offset = -2L; } goto done; } while(b < endb) { data = (Vmuchar_t*)DATA(b); if((Vmuchar_t*)addr >= data && (Vmuchar_t*)addr < data+SIZE(b)) { if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) ) { data = DB2DEBUG(data); if((Vmuchar_t*)addr >= data && (Vmuchar_t*)addr < data+DBSIZE(data)) offset = (Vmuchar_t*)addr - data; } goto done; } b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS) ); } done: CLRLOCK(vd,local); CLRINUSE(vd, inuse); return offset; } #if __STD_C static long dbsize(Vmalloc_t* vm, Void_t* addr) #else static long dbsize(vm, addr) Vmalloc_t* vm; Void_t* addr; #endif { reg Block_t *b, *endb; reg Seg_t* seg; reg long size; reg Vmdata_t* vd = vm->data; reg int inuse; SETINUSE(vd, inuse); if(ISLOCK(vd,0)) { CLRINUSE(vd, inuse); return -1L; } SETLOCK(vd,0); size = -1L; for(seg = vd->seg; seg; seg = seg->next) { b = SEGBLOCK(seg); endb = (Block_t*)(seg->baddr - sizeof(Head_t)); if((Vmuchar_t*)addr <= (Vmuchar_t*)b || (Vmuchar_t*)addr >= (Vmuchar_t*)endb) continue; while(b < endb) { if(addr == (Void_t*)DB2DEBUG(DATA(b))) { if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) ) size = (long)DBSIZE(addr); goto done; } b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS) ); } } done: CLRLOCK(vd,0); CLRINUSE(vd, inuse); return size; } #if __STD_C static Void_t* dballoc(Vmalloc_t* vm, size_t size) #else static Void_t* dballoc(vm, size) Vmalloc_t* vm; size_t size; #endif { reg size_t s; reg Vmuchar_t* data; reg char* file; reg int line; reg Void_t* func; reg Vmdata_t* vd = vm->data; reg int inuse; SETINUSE(vd, inuse); VMFLF(vm,file,line,func); if(ISLOCK(vd,0) ) { dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_ALLOC); CLRINUSE(vd, inuse); return NIL(Void_t*); } SETLOCK(vd,0); if(vd->mode&VM_DBCHECK) vmdbcheck(vm); s = ROUND(size,ALIGN) + DB_EXTRA; if(s < sizeof(Body_t)) /* no tiny blocks during Vmdebug */ s = sizeof(Body_t); if(!(data = (Vmuchar_t*)KPVALLOC(vm,s,(*(Vmbest->allocf))) ) ) { dbwarn(vm,NIL(Vmuchar_t*),DB_ALLOC,file,line,func,DB_ALLOC); goto done; } data = DB2DEBUG(data); dbsetinfo(data,size,file,line); if((vd->mode&VM_TRACE) && _Vmtrace) { vm->file = file; vm->line = line; vm->func = func; (*_Vmtrace)(vm,NIL(Vmuchar_t*),data,size,0); } if(Dbnwatch > 0 ) dbwatch(vm,data,file,line,func,DB_ALLOC); done: CLRLOCK(vd,0); ANNOUNCE(0, vm, VM_ALLOC, (Void_t*)data, vm->disc); CLRINUSE(vd, inuse); return (Void_t*)data; } #if __STD_C static int dbfree(Vmalloc_t* vm, Void_t* data ) #else static int dbfree(vm, data ) Vmalloc_t* vm; Void_t* data; #endif { char* file; int line; Void_t* func; reg long offset; reg int rv, *ip, *endip; reg Vmdata_t* vd = vm->data; reg int inuse; SETINUSE(vd, inuse); VMFLF(vm,file,line,func); if(!data) { CLRINUSE(vd, inuse); return 0; } if(ISLOCK(vd,0) ) { dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_FREE); CLRINUSE(vd, inuse); return -1; } SETLOCK(vd,0); if(vd->mode&VM_DBCHECK) vmdbcheck(vm); if((offset = KPVADDR(vm,data,dbaddr)) != 0) { if(vm->disc->exceptf) (void)(*vm->disc->exceptf)(vm,VM_BADADDR,data,vm->disc); dbwarn(vm,(Vmuchar_t*)data,offset == -1L ? 0 : 1,file,line,func,DB_FREE); CLRLOCK(vd,0); CLRINUSE(vd, inuse); return -1; } if(Dbnwatch > 0) dbwatch(vm,data,file,line,func,DB_FREE); if((vd->mode&VM_TRACE) && _Vmtrace) { vm->file = file; vm->line = line; vm->func = func; (*_Vmtrace)(vm,(Vmuchar_t*)data,NIL(Vmuchar_t*),DBSIZE(data),0); } /* clear free space */ ip = (int*)data; endip = ip + (DBSIZE(data)+sizeof(int)-1)/sizeof(int); while(ip < endip) *ip++ = 0; rv = KPVFREE((vm), (Void_t*)DB2BEST(data), (*Vmbest->freef)); CLRLOCK(vd,0); ANNOUNCE(0, vm, VM_FREE, data, vm->disc); CLRINUSE(vd, inuse); return rv; } /* Resizing an existing block */ #if __STD_C static Void_t* dbresize(Vmalloc_t* vm, Void_t* addr, reg size_t size, int type) #else static Void_t* dbresize(vm,addr,size,type) Vmalloc_t* vm; /* region allocating from */ Void_t* addr; /* old block of data */ reg size_t size; /* new size */ int type; /* !=0 for movable, >0 for copy */ #endif { reg Vmuchar_t* data; reg size_t s, oldsize; reg long offset; char *file, *oldfile; int line, oldline; Void_t* func; reg Vmdata_t* vd = vm->data; reg int inuse; SETINUSE(vd, inuse); if(!addr) { oldsize = 0; data = (Vmuchar_t*)dballoc(vm,size); goto done; } if(size == 0) { (void)dbfree(vm,addr); CLRINUSE(vd, inuse); return NIL(Void_t*); } VMFLF(vm,file,line,func); if(ISLOCK(vd,0) ) { dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_RESIZE); CLRINUSE(vd, inuse); return NIL(Void_t*); } SETLOCK(vd,0); if(vd->mode&VM_DBCHECK) vmdbcheck(vm); if((offset = KPVADDR(vm,addr,dbaddr)) != 0) { if(vm->disc->exceptf) (void)(*vm->disc->exceptf)(vm,VM_BADADDR,addr,vm->disc); dbwarn(vm,(Vmuchar_t*)addr,offset == -1L ? 0 : 1,file,line,func,DB_RESIZE); CLRLOCK(vd,0); CLRINUSE(vd, inuse); return NIL(Void_t*); } if(Dbnwatch > 0) dbwatch(vm,addr,file,line,func,DB_RESIZE); /* Vmbest data block */ data = DB2BEST(addr); oldsize = DBSIZE(addr); oldfile = DBFILE(addr); oldline = DBLINE(addr); /* do the resize */ s = ROUND(size,ALIGN) + DB_EXTRA; if(s < sizeof(Body_t)) s = sizeof(Body_t); data = (Vmuchar_t*)KPVRESIZE(vm,(Void_t*)data,s, (type&~VM_RSZERO),(*(Vmbest->resizef)) ); if(!data) /* failed, reset data for old block */ { dbwarn(vm,NIL(Vmuchar_t*),DB_ALLOC,file,line,func,DB_RESIZE); dbsetinfo((Vmuchar_t*)addr,oldsize,oldfile,oldline); } else { data = DB2DEBUG(data); dbsetinfo(data,size,file,line); if((vd->mode&VM_TRACE) && _Vmtrace) { vm->file = file; vm->line = line; (*_Vmtrace)(vm,(Vmuchar_t*)addr,data,size,0); } if(Dbnwatch > 0) dbwatch(vm,data,file,line,func,DB_RESIZED); } CLRLOCK(vd,0); ANNOUNCE(0, vm, VM_RESIZE, (Void_t*)data, vm->disc); done: if(data && (type&VM_RSZERO) && size > oldsize) { reg Vmuchar_t *d = data+oldsize, *ed = data+size; do { *d++ = 0; } while(d < ed); } CLRINUSE(vd, inuse); return (Void_t*)data; } /* compact any residual free space */ #if __STD_C static int dbcompact(Vmalloc_t* vm) #else static int dbcompact(vm) Vmalloc_t* vm; #endif { return (*(Vmbest->compactf))(vm); } /* check for memory overwrites over all live blocks */ #if __STD_C int vmdbcheck(Vmalloc_t* vm) #else int vmdbcheck(vm) Vmalloc_t* vm; #endif { reg Block_t *b, *endb; reg Seg_t* seg; int rv; reg Vmdata_t* vd = vm->data; /* check the meta-data of this region */ if(vd->mode & (VM_MTDEBUG|VM_MTBEST|VM_MTPROFILE)) { if(_vmbestcheck(vd, NIL(Block_t*)) < 0) return -1; if(!(vd->mode&VM_MTDEBUG)) return 0; } else return -1; rv = 0; for(seg = vd->seg; seg; seg = seg->next) { b = SEGBLOCK(seg); endb = (Block_t*)(seg->baddr - sizeof(Head_t)); while(b < endb) { reg Vmuchar_t *data, *begp, *endp; if(ISJUNK(SIZE(b)) || !ISBUSY(SIZE(b))) goto next; data = DB2DEBUG(DATA(b)); if(DBISBAD(data)) /* seen this before */ { rv += 1; goto next; } DBHEAD(data,begp,endp); for(; begp < endp; ++begp) if(*begp != DB_MAGIC) goto set_bad; DBTAIL(data,begp,endp); for(; begp < endp; ++begp) { if(*begp == DB_MAGIC) continue; set_bad: dbwarn(vm,data,begp-data,NIL(char*),0,0,DB_CHECK); DBSETBAD(data); rv += 1; goto next; } next: b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS)); } } return rv; } /* set/delete an address to watch */ #if __STD_C Void_t* vmdbwatch(Void_t* addr) #else Void_t* vmdbwatch(addr) Void_t* addr; /* address to insert */ #endif { reg int n; reg Void_t* out; out = NIL(Void_t*); if(!addr) Dbnwatch = 0; else { for(n = Dbnwatch - 1; n >= 0; --n) if(Dbwatch[n] == addr) break; if(n < 0) /* insert */ { if(Dbnwatch == S_WATCH) { /* delete left-most */ out = Dbwatch[0]; Dbnwatch -= 1; for(n = 0; n < Dbnwatch; ++n) Dbwatch[n] = Dbwatch[n+1]; } Dbwatch[Dbnwatch] = addr; Dbnwatch += 1; } } return out; } #if __STD_C static Void_t* dbalign(Vmalloc_t* vm, size_t size, size_t align) #else static Void_t* dbalign(vm, size, align) Vmalloc_t* vm; size_t size; size_t align; #endif { reg Vmuchar_t* data; reg size_t s; reg char* file; reg int line; reg Void_t* func; reg Vmdata_t* vd = vm->data; reg int inuse; SETINUSE(vd, inuse); VMFLF(vm,file,line,func); if(size <= 0 || align <= 0) { CLRINUSE(vd, inuse); return NIL(Void_t*); } if(ISLOCK(vd,0) ) { CLRINUSE(vd, inuse); return NIL(Void_t*); } SETLOCK(vd,0); if((s = ROUND(size,ALIGN) + DB_EXTRA) < sizeof(Body_t)) s = sizeof(Body_t); if(!(data = (Vmuchar_t*)KPVALIGN(vm,s,align,(*(Vmbest->alignf)))) ) goto done; data += DB_HEAD; dbsetinfo(data,size,file,line); if((vd->mode&VM_TRACE) && _Vmtrace) { vm->file = file; vm->line = line; vm->func = func; (*_Vmtrace)(vm,NIL(Vmuchar_t*),data,size,align); } done: CLRLOCK(vd,0); ANNOUNCE(0, vm, VM_ALLOC, (Void_t*)data, vm->disc); CLRINUSE(vd, inuse); return (Void_t*)data; } static Vmethod_t _Vmdebug = { dballoc, dbresize, dbfree, dbaddr, dbsize, dbcompact, dbalign, VM_MTDEBUG }; __DEFINE__(Vmethod_t*,Vmdebug,&_Vmdebug); #ifdef NoF NoF(vmdebug) #endif #endif