summaryrefslogtreecommitdiff
path: root/usr/src/lib/libast/common/vmalloc/vmdebug.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libast/common/vmalloc/vmdebug.c')
-rw-r--r--usr/src/lib/libast/common/vmalloc/vmdebug.c732
1 files changed, 732 insertions, 0 deletions
diff --git a/usr/src/lib/libast/common/vmalloc/vmdebug.c b/usr/src/lib/libast/common/vmalloc/vmdebug.c
new file mode 100644
index 0000000000..61eb8fedb0
--- /dev/null
+++ b/usr/src/lib/libast/common/vmalloc/vmdebug.c
@@ -0,0 +1,732 @@
+/***********************************************************************
+* *
+* 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