vmdebug.c revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2007 AT&T Knowledge Ventures *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Knowledge Ventures *
* *
* 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_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;
GETLOCAL(vd,local);
if(ISLOCK(vd,local) || !addr)
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);
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;
if(ISLOCK(vd,0))
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);
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;
VMFLF(vm,file,line,func);
if(ISLOCK(vd,0) )
{ dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_ALLOC);
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);
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;
VMFLF(vm,file,line,func);
if(!data)
return 0;
if(ISLOCK(vd,0) )
{ dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_FREE);
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);
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);
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;
if(!addr)
{ oldsize = 0;
data = (Vmuchar_t*)dballoc(vm,size);
goto done;
}
if(size == 0)
{ (void)dbfree(vm,addr);
return NIL(Void_t*);
}
VMFLF(vm,file,line,func);
if(ISLOCK(vd,0) )
{ dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_RESIZE);
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);
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);
}
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;
VMFLF(vm,file,line,func);
if(size <= 0 || align <= 0)
return NIL(Void_t*);
if(ISLOCK(vd,0) )
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);
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