xref: /titanic_41/usr/src/lib/libast/common/vmalloc/vmdebug.c (revision 71269a2275bf5a143dad6461eee2710a344e7261)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #if defined(_UWIN) && defined(_BLD_ast)
23 
24 void _STUB_vmdebug(){}
25 
26 #else
27 
28 #include	"vmhdr.h"
29 
30 /*	Method to help with debugging. This does rigorous checks on
31 **	addresses and arena integrity.
32 **
33 **	Written by Kiem-Phong Vo, kpv@research.att.com, 01/16/94.
34 */
35 
36 /* structure to keep track of file names */
37 typedef struct _dbfile_s	Dbfile_t;
38 struct _dbfile_s
39 {	Dbfile_t*	next;
40 	char		file[1];
41 };
42 static Dbfile_t*	Dbfile;
43 
44 /* global watch list */
45 #define S_WATCH	32
46 static int	Dbnwatch;
47 static Void_t*	Dbwatch[S_WATCH];
48 
49 /* types of warnings reported by dbwarn() */
50 #define	DB_CHECK	0
51 #define DB_ALLOC	1
52 #define DB_FREE		2
53 #define DB_RESIZE	3
54 #define DB_WATCH	4
55 #define DB_RESIZED	5
56 
57 #define LONGV(x)	((Vmulong_t)(x))
58 
59 static int Dbinit = 0;
60 #define DBINIT()	(Dbinit ? 0 : (dbinit(), Dbinit=1) )
61 static void dbinit()
62 {	int	fd;
63 	if((fd = vmtrace(-1)) >= 0)
64 		vmtrace(fd);
65 }
66 
67 static int	Dbfd = 2;	/* default warning file descriptor */
68 #if __STD_C
69 int vmdebug(int fd)
70 #else
71 int vmdebug(fd)
72 int	fd;
73 #endif
74 {
75 	int	old = Dbfd;
76 	Dbfd = fd;
77 	return old;
78 }
79 
80 /* just an entry point to make it easy to set break point */
81 #if __STD_C
82 static void vmdbwarn(Vmalloc_t* vm, char* mesg, int n)
83 #else
84 static void vmdbwarn(vm, mesg, n)
85 Vmalloc_t*	vm;
86 char*		mesg;
87 int		n;
88 #endif
89 {
90 	reg Vmdata_t*	vd = vm->data;
91 
92 	write(Dbfd,mesg,n);
93 	if(vd->mode&VM_DBABORT)
94 		abort();
95 }
96 
97 /* issue a warning of some type */
98 #if __STD_C
99 static void dbwarn(Vmalloc_t* vm, Void_t* data, int where,
100 		   const char* file, int line, const Void_t* func, int type)
101 #else
102 static void dbwarn(vm, data, where, file, line, func, type)
103 Vmalloc_t*	vm;	/* region holding the block	*/
104 Void_t*		data;	/* data block			*/
105 int		where;	/* byte that was corrupted	*/
106 const char*	file;	/* file where call originates	*/
107 int		line;	/* line number of call		*/
108 const Void_t*	func;	/* function called from		*/
109 int		type;	/* operation being done		*/
110 #endif
111 {
112 	char	buf[1024], *bufp, *endbuf, *s;
113 #define SLOP	64	/* enough for a message and an int */
114 
115 	DBINIT();
116 
117 	bufp = buf;
118 	endbuf = buf + sizeof(buf);
119 
120 	if(type == DB_ALLOC)
121 		bufp = (*_Vmstrcpy)(bufp, "alloc error", ':');
122 	else if(type == DB_FREE)
123 		bufp = (*_Vmstrcpy)(bufp, "free error", ':');
124 	else if(type == DB_RESIZE)
125 		bufp = (*_Vmstrcpy)(bufp, "resize error", ':');
126 	else if(type == DB_CHECK)
127 		bufp = (*_Vmstrcpy)(bufp, "corrupted data", ':');
128 	else if(type == DB_WATCH)
129 		bufp = (*_Vmstrcpy)(bufp, "alert", ':');
130 
131 	/* region info */
132 	bufp = (*_Vmstrcpy)(bufp, "region", '=');
133 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(vm), 0), ':');
134 
135 	if(data)
136 	{	bufp = (*_Vmstrcpy)(bufp,"block",'=');
137 		bufp = (*_Vmstrcpy)(bufp,(*_Vmitoa)(VLONG(data),0),':');
138 	}
139 
140 	if(!data)
141 	{	if(where == DB_ALLOC)
142 			bufp = (*_Vmstrcpy)(bufp, "can't get memory", ':');
143 		else	bufp = (*_Vmstrcpy)(bufp, "region is locked", ':');
144 	}
145 	else if(type == DB_FREE || type == DB_RESIZE)
146 	{	if(where == 0)
147 			bufp = (*_Vmstrcpy)(bufp, "unallocated block", ':');
148 		else	bufp = (*_Vmstrcpy)(bufp, "already freed", ':');
149 	}
150 	else if(type == DB_WATCH)
151 	{	bufp = (*_Vmstrcpy)(bufp, "size", '=');
152 		bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(DBSIZE(data),-1), ':');
153 		if(where == DB_ALLOC)
154 			bufp = (*_Vmstrcpy)(bufp,"just allocated", ':');
155 		else if(where == DB_FREE)
156 			bufp = (*_Vmstrcpy)(bufp,"being freed", ':');
157 		else if(where == DB_RESIZE)
158 			bufp = (*_Vmstrcpy)(bufp,"being resized", ':');
159 		else if(where == DB_RESIZED)
160 			bufp = (*_Vmstrcpy)(bufp,"just resized", ':');
161 	}
162 	else if(type == DB_CHECK)
163 	{	bufp = (*_Vmstrcpy)(bufp, "bad byte at", '=');
164 		bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(LONGV(where),-1), ':');
165 		if((s = DBFILE(data)) && (bufp + strlen(s) + SLOP) < endbuf)
166 		{	bufp = (*_Vmstrcpy)(bufp,"allocated at", '=');
167 			bufp = (*_Vmstrcpy)(bufp, s, ',');
168 			bufp = (*_Vmstrcpy)(bufp,(*_Vmitoa)(LONGV(DBLINE(data)),-1),':');
169 		}
170 	}
171 
172 	/* location where offending call originates from */
173 	if(file && file[0] && line > 0 && (bufp + strlen(file) + SLOP) < endbuf)
174 	{	bufp = (*_Vmstrcpy)(bufp, "detected at", '=');
175 		bufp = (*_Vmstrcpy)(bufp, file, ',');
176 		bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(LONGV(line),-1), ',');
177 		bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(func),-1), ':');
178 	}
179 
180 	*bufp++ = '\n';
181 	*bufp = '\0';
182 
183 	vmdbwarn(vm,buf,(bufp-buf));
184 }
185 
186 /* check for watched address and issue warnings */
187 #if __STD_C
188 static void dbwatch(Vmalloc_t* vm, Void_t* data,
189 		    const char* file, int line, const Void_t* func, int type)
190 #else
191 static void dbwatch(vm, data, file, line, func, type)
192 Vmalloc_t*	vm;
193 Void_t*		data;
194 const char*	file;
195 int		line;
196 const Void_t*	func;
197 int		type;
198 #endif
199 {
200 	reg int		n;
201 
202 	for(n = Dbnwatch; n >= 0; --n)
203 	{	if(Dbwatch[n] == data)
204 		{	dbwarn(vm,data,type,file,line,func,DB_WATCH);
205 			return;
206 		}
207 	}
208 }
209 
210 /* record information about the block */
211 #if __STD_C
212 static void dbsetinfo(Vmuchar_t* data, size_t size, const char* file, int line)
213 #else
214 static void dbsetinfo(data, size, file, line)
215 Vmuchar_t*	data;	/* real address not the one from Vmbest	*/
216 size_t		size;	/* the actual requested size		*/
217 const char*	file;	/* file where the request came from	*/
218 int		line;	/* and line number			*/
219 #endif
220 {
221 	reg Vmuchar_t	*begp, *endp;
222 	reg Dbfile_t	*last, *db;
223 
224 	DBINIT();
225 
226 	/* find the file structure */
227 	if(!file || !file[0])
228 		db = NIL(Dbfile_t*);
229 	else
230 	{	for(last = NIL(Dbfile_t*), db = Dbfile; db; last = db, db = db->next)
231 			if(strcmp(db->file,file) == 0)
232 				break;
233 		if(!db)
234 		{	db = (Dbfile_t*)vmalloc(Vmheap,sizeof(Dbfile_t)+strlen(file));
235 			if(db)
236 			{	(*_Vmstrcpy)(db->file,file,0);
237 				db->next = Dbfile;
238 				Dbfile = db->next;
239 			}
240 		}
241 		else if(last) /* move-to-front heuristic */
242 		{	last->next = db->next;
243 			db->next = Dbfile;
244 			Dbfile = db->next;
245 		}
246 	}
247 
248 	DBSETFL(data,(db ? db->file : NIL(char*)),line);
249 	DBSIZE(data) = size;
250 	DBSEG(data)  = SEG(DBBLOCK(data));
251 
252 	DBHEAD(data,begp,endp);
253 	while(begp < endp)
254 		*begp++ = DB_MAGIC;
255 	DBTAIL(data,begp,endp);
256 	while(begp < endp)
257 		*begp++ = DB_MAGIC;
258 }
259 
260 /* Check to see if an address is in some data block of a region.
261 ** This returns -(offset+1) if block is already freed, +(offset+1)
262 ** if block is live, 0 if no match.
263 */
264 #if __STD_C
265 static long dbaddr(Vmalloc_t* vm, Void_t* addr)
266 #else
267 static long dbaddr(vm, addr)
268 Vmalloc_t*	vm;
269 Void_t*		addr;
270 #endif
271 {
272 	reg Block_t	*b, *endb;
273 	reg Seg_t*	seg;
274 	reg Vmuchar_t*	data;
275 	reg long	offset = -1L;
276 	reg Vmdata_t*	vd = vm->data;
277 	reg int		local;
278 
279 	GETLOCAL(vd,local);
280 	if(ISLOCK(vd,local) || !addr)
281 		return -1L;
282 	SETLOCK(vd,local);
283 
284 	b = endb = NIL(Block_t*);
285 	for(seg = vd->seg; seg; seg = seg->next)
286 	{	b = SEGBLOCK(seg);
287 		endb = (Block_t*)(seg->baddr - sizeof(Head_t));
288 		if((Vmuchar_t*)addr > (Vmuchar_t*)b &&
289 		   (Vmuchar_t*)addr < (Vmuchar_t*)endb)
290 			break;
291 	}
292 	if(!seg)
293 		goto done;
294 
295 	if(local)	/* must be vmfree or vmresize checking address */
296 	{	if(DBSEG(addr) == seg)
297 		{	b = DBBLOCK(addr);
298 			if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) )
299 				offset = 0;
300 			else	offset = -2L;
301 		}
302 		goto done;
303 	}
304 
305 	while(b < endb)
306 	{	data = (Vmuchar_t*)DATA(b);
307 		if((Vmuchar_t*)addr >= data && (Vmuchar_t*)addr < data+SIZE(b))
308 		{	if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) )
309 			{	data = DB2DEBUG(data);
310 				if((Vmuchar_t*)addr >= data &&
311 				   (Vmuchar_t*)addr < data+DBSIZE(data))
312 					offset =  (Vmuchar_t*)addr - data;
313 			}
314 			goto done;
315 		}
316 
317 		b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS) );
318 	}
319 
320 done:
321 	CLRLOCK(vd,local);
322 	return offset;
323 }
324 
325 
326 #if __STD_C
327 static long dbsize(Vmalloc_t* vm, Void_t* addr)
328 #else
329 static long dbsize(vm, addr)
330 Vmalloc_t*	vm;
331 Void_t*		addr;
332 #endif
333 {
334 	reg Block_t	*b, *endb;
335 	reg Seg_t*	seg;
336 	reg long	size;
337 	reg Vmdata_t*	vd = vm->data;
338 
339 	if(ISLOCK(vd,0))
340 		return -1L;
341 	SETLOCK(vd,0);
342 
343 	size = -1L;
344 	for(seg = vd->seg; seg; seg = seg->next)
345 	{	b = SEGBLOCK(seg);
346 		endb = (Block_t*)(seg->baddr - sizeof(Head_t));
347 		if((Vmuchar_t*)addr <= (Vmuchar_t*)b ||
348 		   (Vmuchar_t*)addr >= (Vmuchar_t*)endb)
349 			continue;
350 		while(b < endb)
351 		{	if(addr == (Void_t*)DB2DEBUG(DATA(b)))
352 			{	if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) )
353 					size = (long)DBSIZE(addr);
354 				goto done;
355 			}
356 
357 			b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS) );
358 		}
359 	}
360 done:
361 	CLRLOCK(vd,0);
362 	return size;
363 }
364 
365 #if __STD_C
366 static Void_t* dballoc(Vmalloc_t* vm, size_t size)
367 #else
368 static Void_t* dballoc(vm, size)
369 Vmalloc_t*	vm;
370 size_t		size;
371 #endif
372 {
373 	reg size_t		s;
374 	reg Vmuchar_t*		data;
375 	reg char*		file;
376 	reg int			line;
377 	reg Void_t*		func;
378 	reg Vmdata_t*		vd = vm->data;
379 
380 	VMFLF(vm,file,line,func);
381 
382 	if(ISLOCK(vd,0) )
383 	{	dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_ALLOC);
384 		return NIL(Void_t*);
385 	}
386 	SETLOCK(vd,0);
387 
388 	if(vd->mode&VM_DBCHECK)
389 		vmdbcheck(vm);
390 
391 	s = ROUND(size,ALIGN) + DB_EXTRA;
392 	if(s < sizeof(Body_t))	/* no tiny blocks during Vmdebug */
393 		s = sizeof(Body_t);
394 
395 	if(!(data = (Vmuchar_t*)KPVALLOC(vm,s,(*(Vmbest->allocf))) ) )
396 	{	dbwarn(vm,NIL(Vmuchar_t*),DB_ALLOC,file,line,func,DB_ALLOC);
397 		goto done;
398 	}
399 
400 	data = DB2DEBUG(data);
401 	dbsetinfo(data,size,file,line);
402 
403 	if((vd->mode&VM_TRACE) && _Vmtrace)
404 	{	vm->file = file; vm->line = line; vm->func = func;
405 		(*_Vmtrace)(vm,NIL(Vmuchar_t*),data,size,0);
406 	}
407 
408 	if(Dbnwatch > 0 )
409 		dbwatch(vm,data,file,line,func,DB_ALLOC);
410 
411 done:
412 	CLRLOCK(vd,0);
413 	ANNOUNCE(0, vm, VM_ALLOC, (Void_t*)data, vm->disc);
414 	return (Void_t*)data;
415 }
416 
417 
418 #if __STD_C
419 static int dbfree(Vmalloc_t* vm, Void_t* data )
420 #else
421 static int dbfree(vm, data )
422 Vmalloc_t*	vm;
423 Void_t*		data;
424 #endif
425 {
426 	char*		file;
427 	int		line;
428 	Void_t*		func;
429 	reg long	offset;
430 	reg int		rv, *ip, *endip;
431 	reg Vmdata_t*	vd = vm->data;
432 
433 	VMFLF(vm,file,line,func);
434 
435 	if(!data)
436 		return 0;
437 
438 	if(ISLOCK(vd,0) )
439 	{	dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_FREE);
440 		return -1;
441 	}
442 	SETLOCK(vd,0);
443 
444 	if(vd->mode&VM_DBCHECK)
445 		vmdbcheck(vm);
446 
447 	if((offset = KPVADDR(vm,data,dbaddr)) != 0)
448 	{	if(vm->disc->exceptf)
449 			(void)(*vm->disc->exceptf)(vm,VM_BADADDR,data,vm->disc);
450 		dbwarn(vm,(Vmuchar_t*)data,offset == -1L ? 0 : 1,file,line,func,DB_FREE);
451 		CLRLOCK(vd,0);
452 		return -1;
453 	}
454 
455 	if(Dbnwatch > 0)
456 		dbwatch(vm,data,file,line,func,DB_FREE);
457 
458 	if((vd->mode&VM_TRACE) && _Vmtrace)
459 	{	vm->file = file; vm->line = line; vm->func = func;
460 		(*_Vmtrace)(vm,(Vmuchar_t*)data,NIL(Vmuchar_t*),DBSIZE(data),0);
461 	}
462 
463 	/* clear free space */
464 	ip = (int*)data;
465 	endip = ip + (DBSIZE(data)+sizeof(int)-1)/sizeof(int);
466 	while(ip < endip)
467 		*ip++ = 0;
468 
469 	rv = KPVFREE((vm), (Void_t*)DB2BEST(data), (*Vmbest->freef));
470 	CLRLOCK(vd,0);
471 	ANNOUNCE(0, vm, VM_FREE, data, vm->disc);
472 	return rv;
473 }
474 
475 /*	Resizing an existing block */
476 #if __STD_C
477 static Void_t* dbresize(Vmalloc_t* vm, Void_t* addr, reg size_t size, int type)
478 #else
479 static Void_t* dbresize(vm,addr,size,type)
480 Vmalloc_t*	vm;		/* region allocating from	*/
481 Void_t*		addr;		/* old block of data		*/
482 reg size_t	size;		/* new size			*/
483 int		type;		/* !=0 for movable, >0 for copy	*/
484 #endif
485 {
486 	reg Vmuchar_t*	data;
487 	reg size_t	s, oldsize;
488 	reg long	offset;
489 	char		*file, *oldfile;
490 	int		line, oldline;
491 	Void_t*		func;
492 	reg Vmdata_t*	vd = vm->data;
493 
494 	if(!addr)
495 	{	oldsize = 0;
496 		data = (Vmuchar_t*)dballoc(vm,size);
497 		goto done;
498 	}
499 	if(size == 0)
500 	{	(void)dbfree(vm,addr);
501 		return NIL(Void_t*);
502 	}
503 
504 	VMFLF(vm,file,line,func);
505 
506 	if(ISLOCK(vd,0) )
507 	{	dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_RESIZE);
508 		return NIL(Void_t*);
509 	}
510 	SETLOCK(vd,0);
511 
512 	if(vd->mode&VM_DBCHECK)
513 		vmdbcheck(vm);
514 
515 	if((offset = KPVADDR(vm,addr,dbaddr)) != 0)
516 	{	if(vm->disc->exceptf)
517 			(void)(*vm->disc->exceptf)(vm,VM_BADADDR,addr,vm->disc);
518 		dbwarn(vm,(Vmuchar_t*)addr,offset == -1L ? 0 : 1,file,line,func,DB_RESIZE);
519 		CLRLOCK(vd,0);
520 		return NIL(Void_t*);
521 	}
522 
523 	if(Dbnwatch > 0)
524 		dbwatch(vm,addr,file,line,func,DB_RESIZE);
525 
526 	/* Vmbest data block */
527 	data = DB2BEST(addr);
528 	oldsize = DBSIZE(addr);
529 	oldfile = DBFILE(addr);
530 	oldline = DBLINE(addr);
531 
532 	/* do the resize */
533 	s = ROUND(size,ALIGN) + DB_EXTRA;
534 	if(s < sizeof(Body_t))
535 		s = sizeof(Body_t);
536 	data = (Vmuchar_t*)KPVRESIZE(vm,(Void_t*)data,s,
537 				 (type&~VM_RSZERO),(*(Vmbest->resizef)) );
538 	if(!data) /* failed, reset data for old block */
539 	{	dbwarn(vm,NIL(Vmuchar_t*),DB_ALLOC,file,line,func,DB_RESIZE);
540 		dbsetinfo((Vmuchar_t*)addr,oldsize,oldfile,oldline);
541 	}
542 	else
543 	{	data = DB2DEBUG(data);
544 		dbsetinfo(data,size,file,line);
545 
546 		if((vd->mode&VM_TRACE) && _Vmtrace)
547 		{	vm->file = file; vm->line = line;
548 			(*_Vmtrace)(vm,(Vmuchar_t*)addr,data,size,0);
549 		}
550 		if(Dbnwatch > 0)
551 			dbwatch(vm,data,file,line,func,DB_RESIZED);
552 	}
553 
554 	CLRLOCK(vd,0);
555 	ANNOUNCE(0, vm, VM_RESIZE, (Void_t*)data, vm->disc);
556 
557 done:	if(data && (type&VM_RSZERO) && size > oldsize)
558 	{	reg Vmuchar_t *d = data+oldsize, *ed = data+size;
559 		do { *d++ = 0; } while(d < ed);
560 	}
561 	return (Void_t*)data;
562 }
563 
564 /* compact any residual free space */
565 #if __STD_C
566 static int dbcompact(Vmalloc_t* vm)
567 #else
568 static int dbcompact(vm)
569 Vmalloc_t*	vm;
570 #endif
571 {
572 	return (*(Vmbest->compactf))(vm);
573 }
574 
575 /* check for memory overwrites over all live blocks */
576 #if __STD_C
577 int vmdbcheck(Vmalloc_t* vm)
578 #else
579 int vmdbcheck(vm)
580 Vmalloc_t*	vm;
581 #endif
582 {
583 	reg Block_t	*b, *endb;
584 	reg Seg_t*	seg;
585 	int		rv;
586 	reg Vmdata_t*	vd = vm->data;
587 
588 	/* check the meta-data of this region */
589 	if(vd->mode & (VM_MTDEBUG|VM_MTBEST|VM_MTPROFILE))
590 	{	if(_vmbestcheck(vd, NIL(Block_t*)) < 0)
591 			return -1;
592 		if(!(vd->mode&VM_MTDEBUG))
593 			return 0;
594 	}
595 	else	return -1;
596 
597 	rv = 0;
598 	for(seg = vd->seg; seg; seg = seg->next)
599 	{	b = SEGBLOCK(seg);
600 		endb = (Block_t*)(seg->baddr - sizeof(Head_t));
601 		while(b < endb)
602 		{	reg Vmuchar_t	*data, *begp, *endp;
603 
604 			if(ISJUNK(SIZE(b)) || !ISBUSY(SIZE(b)))
605 				goto next;
606 
607 			data = DB2DEBUG(DATA(b));
608 			if(DBISBAD(data))	/* seen this before */
609 			{	rv += 1;
610 				goto next;
611 			}
612 
613 			DBHEAD(data,begp,endp);
614 			for(; begp < endp; ++begp)
615 				if(*begp != DB_MAGIC)
616 					goto set_bad;
617 
618 			DBTAIL(data,begp,endp);
619 			for(; begp < endp; ++begp)
620 			{	if(*begp == DB_MAGIC)
621 					continue;
622 			set_bad:
623 				dbwarn(vm,data,begp-data,NIL(char*),0,0,DB_CHECK);
624 				DBSETBAD(data);
625 				rv += 1;
626 				goto next;
627 			}
628 
629 		next:	b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS));
630 		}
631 	}
632 
633 	return rv;
634 }
635 
636 /* set/delete an address to watch */
637 #if __STD_C
638 Void_t* vmdbwatch(Void_t* addr)
639 #else
640 Void_t* vmdbwatch(addr)
641 Void_t*		addr;	/* address to insert			*/
642 #endif
643 {
644 	reg int		n;
645 	reg Void_t*	out;
646 
647 	out = NIL(Void_t*);
648 	if(!addr)
649 		Dbnwatch = 0;
650 	else
651 	{	for(n = Dbnwatch - 1; n >= 0; --n)
652 			if(Dbwatch[n] == addr)
653 				break;
654 		if(n < 0)	/* insert */
655 		{	if(Dbnwatch == S_WATCH)
656 			{	/* delete left-most */
657 				out = Dbwatch[0];
658 				Dbnwatch -= 1;
659 				for(n = 0; n < Dbnwatch; ++n)
660 					Dbwatch[n] = Dbwatch[n+1];
661 			}
662 			Dbwatch[Dbnwatch] = addr;
663 			Dbnwatch += 1;
664 		}
665 	}
666 	return out;
667 }
668 
669 #if __STD_C
670 static Void_t* dbalign(Vmalloc_t* vm, size_t size, size_t align)
671 #else
672 static Void_t* dbalign(vm, size, align)
673 Vmalloc_t*	vm;
674 size_t		size;
675 size_t		align;
676 #endif
677 {
678 	reg Vmuchar_t*		data;
679 	reg size_t		s;
680 	reg char*		file;
681 	reg int			line;
682 	reg Void_t*		func;
683 	reg Vmdata_t*		vd = vm->data;
684 
685 	VMFLF(vm,file,line,func);
686 
687 	if(size <= 0 || align <= 0)
688 		return NIL(Void_t*);
689 
690 	if(ISLOCK(vd,0) )
691 		return NIL(Void_t*);
692 	SETLOCK(vd,0);
693 
694 	if((s = ROUND(size,ALIGN) + DB_EXTRA) < sizeof(Body_t))
695 		s = sizeof(Body_t);
696 
697 	if(!(data = (Vmuchar_t*)KPVALIGN(vm,s,align,(*(Vmbest->alignf)))) )
698 		goto done;
699 
700 	data += DB_HEAD;
701 	dbsetinfo(data,size,file,line);
702 
703 	if((vd->mode&VM_TRACE) && _Vmtrace)
704 	{	vm->file = file; vm->line = line; vm->func = func;
705 		(*_Vmtrace)(vm,NIL(Vmuchar_t*),data,size,align);
706 	}
707 
708 done:
709 	CLRLOCK(vd,0);
710 	ANNOUNCE(0, vm, VM_ALLOC, (Void_t*)data, vm->disc);
711 	return (Void_t*)data;
712 }
713 
714 static Vmethod_t _Vmdebug =
715 {
716 	dballoc,
717 	dbresize,
718 	dbfree,
719 	dbaddr,
720 	dbsize,
721 	dbcompact,
722 	dbalign,
723 	VM_MTDEBUG
724 };
725 
726 __DEFINE__(Vmethod_t*,Vmdebug,&_Vmdebug);
727 
728 #ifdef NoF
729 NoF(vmdebug)
730 #endif
731 
732 #endif
733