xref: /titanic_50/usr/src/lib/libast/common/vmalloc/vmdebug.c (revision 4445fffbbb1ea25fd0e9ea68b9380dd7a6709025)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
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 - 1) = '\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, inuse;
278 
279 	SETINUSE(vd, inuse);
280 	GETLOCAL(vd,local);
281 	if(ISLOCK(vd,local) || !addr)
282 	{	CLRINUSE(vd, inuse);
283 		return -1L;
284 	}
285 	SETLOCK(vd,local);
286 
287 	b = endb = NIL(Block_t*);
288 	for(seg = vd->seg; seg; seg = seg->next)
289 	{	b = SEGBLOCK(seg);
290 		endb = (Block_t*)(seg->baddr - sizeof(Head_t));
291 		if((Vmuchar_t*)addr > (Vmuchar_t*)b &&
292 		   (Vmuchar_t*)addr < (Vmuchar_t*)endb)
293 			break;
294 	}
295 	if(!seg)
296 		goto done;
297 
298 	if(local)	/* must be vmfree or vmresize checking address */
299 	{	if(DBSEG(addr) == seg)
300 		{	b = DBBLOCK(addr);
301 			if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) )
302 				offset = 0;
303 			else	offset = -2L;
304 		}
305 		goto done;
306 	}
307 
308 	while(b < endb)
309 	{	data = (Vmuchar_t*)DATA(b);
310 		if((Vmuchar_t*)addr >= data && (Vmuchar_t*)addr < data+SIZE(b))
311 		{	if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) )
312 			{	data = DB2DEBUG(data);
313 				if((Vmuchar_t*)addr >= data &&
314 				   (Vmuchar_t*)addr < data+DBSIZE(data))
315 					offset =  (Vmuchar_t*)addr - data;
316 			}
317 			goto done;
318 		}
319 
320 		b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS) );
321 	}
322 
323 done:
324 	CLRLOCK(vd,local);
325 	CLRINUSE(vd, inuse);
326 	return offset;
327 }
328 
329 
330 #if __STD_C
331 static long dbsize(Vmalloc_t* vm, Void_t* addr)
332 #else
333 static long dbsize(vm, addr)
334 Vmalloc_t*	vm;
335 Void_t*		addr;
336 #endif
337 {
338 	reg Block_t	*b, *endb;
339 	reg Seg_t*	seg;
340 	reg long	size;
341 	reg Vmdata_t*	vd = vm->data;
342 	reg int		inuse;
343 
344 	SETINUSE(vd, inuse);
345 	if(ISLOCK(vd,0))
346 	{	CLRINUSE(vd, inuse);
347 		return -1L;
348 	}
349 	SETLOCK(vd,0);
350 
351 	size = -1L;
352 	for(seg = vd->seg; seg; seg = seg->next)
353 	{	b = SEGBLOCK(seg);
354 		endb = (Block_t*)(seg->baddr - sizeof(Head_t));
355 		if((Vmuchar_t*)addr <= (Vmuchar_t*)b ||
356 		   (Vmuchar_t*)addr >= (Vmuchar_t*)endb)
357 			continue;
358 		while(b < endb)
359 		{	if(addr == (Void_t*)DB2DEBUG(DATA(b)))
360 			{	if(ISBUSY(SIZE(b)) && !ISJUNK(SIZE(b)) )
361 					size = (long)DBSIZE(addr);
362 				goto done;
363 			}
364 
365 			b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS) );
366 		}
367 	}
368 done:
369 	CLRLOCK(vd,0);
370 	CLRINUSE(vd, inuse);
371 	return size;
372 }
373 
374 #if __STD_C
375 static Void_t* dballoc(Vmalloc_t* vm, size_t size)
376 #else
377 static Void_t* dballoc(vm, size)
378 Vmalloc_t*	vm;
379 size_t		size;
380 #endif
381 {
382 	reg size_t		s;
383 	reg Vmuchar_t*		data;
384 	reg char*		file;
385 	reg int			line;
386 	reg Void_t*		func;
387 	reg Vmdata_t*		vd = vm->data;
388 	reg int			inuse;
389 
390 	SETINUSE(vd, inuse);
391 	VMFLF(vm,file,line,func);
392 
393 	if(ISLOCK(vd,0) )
394 	{	dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_ALLOC);
395 		CLRINUSE(vd, inuse);
396 		return NIL(Void_t*);
397 	}
398 	SETLOCK(vd,0);
399 
400 	if(vd->mode&VM_DBCHECK)
401 		vmdbcheck(vm);
402 
403 	s = ROUND(size,ALIGN) + DB_EXTRA;
404 	if(s < sizeof(Body_t))	/* no tiny blocks during Vmdebug */
405 		s = sizeof(Body_t);
406 
407 	if(!(data = (Vmuchar_t*)KPVALLOC(vm,s,(*(Vmbest->allocf))) ) )
408 	{	dbwarn(vm,NIL(Vmuchar_t*),DB_ALLOC,file,line,func,DB_ALLOC);
409 		goto done;
410 	}
411 
412 	data = DB2DEBUG(data);
413 	dbsetinfo(data,size,file,line);
414 
415 	if((vd->mode&VM_TRACE) && _Vmtrace)
416 	{	vm->file = file; vm->line = line; vm->func = func;
417 		(*_Vmtrace)(vm,NIL(Vmuchar_t*),data,size,0);
418 	}
419 
420 	if(Dbnwatch > 0 )
421 		dbwatch(vm,data,file,line,func,DB_ALLOC);
422 
423 done:
424 	CLRLOCK(vd,0);
425 	ANNOUNCE(0, vm, VM_ALLOC, (Void_t*)data, vm->disc);
426 	CLRINUSE(vd, inuse);
427 	return (Void_t*)data;
428 }
429 
430 
431 #if __STD_C
432 static int dbfree(Vmalloc_t* vm, Void_t* data )
433 #else
434 static int dbfree(vm, data )
435 Vmalloc_t*	vm;
436 Void_t*		data;
437 #endif
438 {
439 	char*		file;
440 	int		line;
441 	Void_t*		func;
442 	reg long	offset;
443 	reg int		rv, *ip, *endip;
444 	reg Vmdata_t*	vd = vm->data;
445 	reg int		inuse;
446 
447 	SETINUSE(vd, inuse);
448 	VMFLF(vm,file,line,func);
449 
450 	if(!data)
451 	{	CLRINUSE(vd, inuse);
452 		return 0;
453 	}
454 
455 	if(ISLOCK(vd,0) )
456 	{	dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_FREE);
457 		CLRINUSE(vd, inuse);
458 		return -1;
459 	}
460 	SETLOCK(vd,0);
461 
462 	if(vd->mode&VM_DBCHECK)
463 		vmdbcheck(vm);
464 
465 	if((offset = KPVADDR(vm,data,dbaddr)) != 0)
466 	{	dbwarn(vm,(Vmuchar_t*)data,offset == -1L ? 0 : 1,file,line,func,DB_FREE);
467 		if(vm->disc->exceptf)
468 			(void)(*vm->disc->exceptf)(vm,VM_BADADDR,data,vm->disc);
469 		CLRLOCK(vd,0);
470 		CLRINUSE(vd, inuse);
471 		return -1;
472 	}
473 
474 	if(Dbnwatch > 0)
475 		dbwatch(vm,data,file,line,func,DB_FREE);
476 
477 	if((vd->mode&VM_TRACE) && _Vmtrace)
478 	{	vm->file = file; vm->line = line; vm->func = func;
479 		(*_Vmtrace)(vm,(Vmuchar_t*)data,NIL(Vmuchar_t*),DBSIZE(data),0);
480 	}
481 
482 	/* clear free space */
483 	ip = (int*)data;
484 	endip = ip + (DBSIZE(data)+sizeof(int)-1)/sizeof(int);
485 	while(ip < endip)
486 		*ip++ = 0;
487 
488 	rv = KPVFREE((vm), (Void_t*)DB2BEST(data), (*Vmbest->freef));
489 	CLRLOCK(vd,0);
490 	ANNOUNCE(0, vm, VM_FREE, data, vm->disc);
491 	CLRINUSE(vd, inuse);
492 	return rv;
493 }
494 
495 /*	Resizing an existing block */
496 #if __STD_C
497 static Void_t* dbresize(Vmalloc_t* vm, Void_t* addr, reg size_t size, int type)
498 #else
499 static Void_t* dbresize(vm,addr,size,type)
500 Vmalloc_t*	vm;		/* region allocating from	*/
501 Void_t*		addr;		/* old block of data		*/
502 reg size_t	size;		/* new size			*/
503 int		type;		/* !=0 for movable, >0 for copy	*/
504 #endif
505 {
506 	reg Vmuchar_t*	data;
507 	reg size_t	s, oldsize;
508 	reg long	offset;
509 	char		*file, *oldfile;
510 	int		line, oldline;
511 	Void_t*		func;
512 	reg Vmdata_t*	vd = vm->data;
513 	reg int		inuse;
514 
515 	SETINUSE(vd, inuse);
516 	if(!addr)
517 	{	oldsize = 0;
518 		data = (Vmuchar_t*)dballoc(vm,size);
519 		goto done;
520 	}
521 	if(size == 0)
522 	{	(void)dbfree(vm,addr);
523 		CLRINUSE(vd, inuse);
524 		return NIL(Void_t*);
525 	}
526 
527 	VMFLF(vm,file,line,func);
528 
529 	if(ISLOCK(vd,0) )
530 	{	dbwarn(vm,NIL(Vmuchar_t*),0,file,line,func,DB_RESIZE);
531 		CLRINUSE(vd, inuse);
532 		return NIL(Void_t*);
533 	}
534 	SETLOCK(vd,0);
535 
536 	if(vd->mode&VM_DBCHECK)
537 		vmdbcheck(vm);
538 
539 	if((offset = KPVADDR(vm,addr,dbaddr)) != 0)
540 	{	dbwarn(vm,(Vmuchar_t*)addr,offset == -1L ? 0 : 1,file,line,func,DB_RESIZE);
541 		if(vm->disc->exceptf)
542 			(void)(*vm->disc->exceptf)(vm,VM_BADADDR,addr,vm->disc);
543 		CLRLOCK(vd,0);
544 		CLRINUSE(vd, inuse);
545 		return NIL(Void_t*);
546 	}
547 
548 	if(Dbnwatch > 0)
549 		dbwatch(vm,addr,file,line,func,DB_RESIZE);
550 
551 	/* Vmbest data block */
552 	data = DB2BEST(addr);
553 	oldsize = DBSIZE(addr);
554 	oldfile = DBFILE(addr);
555 	oldline = DBLINE(addr);
556 
557 	/* do the resize */
558 	s = ROUND(size,ALIGN) + DB_EXTRA;
559 	if(s < sizeof(Body_t))
560 		s = sizeof(Body_t);
561 	data = (Vmuchar_t*)KPVRESIZE(vm,(Void_t*)data,s,
562 				 (type&~VM_RSZERO),(*(Vmbest->resizef)) );
563 	if(!data) /* failed, reset data for old block */
564 	{	dbwarn(vm,NIL(Vmuchar_t*),DB_ALLOC,file,line,func,DB_RESIZE);
565 		dbsetinfo((Vmuchar_t*)addr,oldsize,oldfile,oldline);
566 	}
567 	else
568 	{	data = DB2DEBUG(data);
569 		dbsetinfo(data,size,file,line);
570 
571 		if((vd->mode&VM_TRACE) && _Vmtrace)
572 		{	vm->file = file; vm->line = line;
573 			(*_Vmtrace)(vm,(Vmuchar_t*)addr,data,size,0);
574 		}
575 		if(Dbnwatch > 0)
576 			dbwatch(vm,data,file,line,func,DB_RESIZED);
577 	}
578 
579 	CLRLOCK(vd,0);
580 	ANNOUNCE(0, vm, VM_RESIZE, (Void_t*)data, vm->disc);
581 
582 done:	if(data && (type&VM_RSZERO) && size > oldsize)
583 	{	reg Vmuchar_t *d = data+oldsize, *ed = data+size;
584 		do { *d++ = 0; } while(d < ed);
585 	}
586 	CLRINUSE(vd, inuse);
587 	return (Void_t*)data;
588 }
589 
590 /* compact any residual free space */
591 #if __STD_C
592 static int dbcompact(Vmalloc_t* vm)
593 #else
594 static int dbcompact(vm)
595 Vmalloc_t*	vm;
596 #endif
597 {
598 	return (*(Vmbest->compactf))(vm);
599 }
600 
601 /* check for memory overwrites over all live blocks */
602 #if __STD_C
603 int vmdbcheck(Vmalloc_t* vm)
604 #else
605 int vmdbcheck(vm)
606 Vmalloc_t*	vm;
607 #endif
608 {
609 	reg Block_t	*b, *endb;
610 	reg Seg_t*	seg;
611 	int		rv;
612 	reg Vmdata_t*	vd = vm->data;
613 
614 	/* check the meta-data of this region */
615 	if(vd->mode & (VM_MTDEBUG|VM_MTBEST|VM_MTPROFILE))
616 	{	if(_vmbestcheck(vd, NIL(Block_t*)) < 0)
617 			return -1;
618 		if(!(vd->mode&VM_MTDEBUG))
619 			return 0;
620 	}
621 	else	return -1;
622 
623 	rv = 0;
624 	for(seg = vd->seg; seg; seg = seg->next)
625 	{	b = SEGBLOCK(seg);
626 		endb = (Block_t*)(seg->baddr - sizeof(Head_t));
627 		while(b < endb)
628 		{	reg Vmuchar_t	*data, *begp, *endp;
629 
630 			if(ISJUNK(SIZE(b)) || !ISBUSY(SIZE(b)))
631 				goto next;
632 
633 			data = DB2DEBUG(DATA(b));
634 			if(DBISBAD(data))	/* seen this before */
635 			{	rv += 1;
636 				goto next;
637 			}
638 
639 			DBHEAD(data,begp,endp);
640 			for(; begp < endp; ++begp)
641 				if(*begp != DB_MAGIC)
642 					goto set_bad;
643 
644 			DBTAIL(data,begp,endp);
645 			for(; begp < endp; ++begp)
646 			{	if(*begp == DB_MAGIC)
647 					continue;
648 			set_bad:
649 				dbwarn(vm,data,begp-data,NIL(char*),0,0,DB_CHECK);
650 				DBSETBAD(data);
651 				rv += 1;
652 				goto next;
653 			}
654 
655 		next:	b = (Block_t*)((Vmuchar_t*)DATA(b) + (SIZE(b)&~BITS));
656 		}
657 	}
658 
659 	return rv;
660 }
661 
662 /* set/delete an address to watch */
663 #if __STD_C
664 Void_t* vmdbwatch(Void_t* addr)
665 #else
666 Void_t* vmdbwatch(addr)
667 Void_t*		addr;	/* address to insert			*/
668 #endif
669 {
670 	reg int		n;
671 	reg Void_t*	out;
672 
673 	out = NIL(Void_t*);
674 	if(!addr)
675 		Dbnwatch = 0;
676 	else
677 	{	for(n = Dbnwatch - 1; n >= 0; --n)
678 			if(Dbwatch[n] == addr)
679 				break;
680 		if(n < 0)	/* insert */
681 		{	if(Dbnwatch == S_WATCH)
682 			{	/* delete left-most */
683 				out = Dbwatch[0];
684 				Dbnwatch -= 1;
685 				for(n = 0; n < Dbnwatch; ++n)
686 					Dbwatch[n] = Dbwatch[n+1];
687 			}
688 			Dbwatch[Dbnwatch] = addr;
689 			Dbnwatch += 1;
690 		}
691 	}
692 	return out;
693 }
694 
695 #if __STD_C
696 static Void_t* dbalign(Vmalloc_t* vm, size_t size, size_t align)
697 #else
698 static Void_t* dbalign(vm, size, align)
699 Vmalloc_t*	vm;
700 size_t		size;
701 size_t		align;
702 #endif
703 {
704 	reg Vmuchar_t*		data;
705 	reg size_t		s;
706 	reg char*		file;
707 	reg int			line;
708 	reg Void_t*		func;
709 	reg Vmdata_t*		vd = vm->data;
710 	reg int			inuse;
711 
712 	SETINUSE(vd, inuse);
713 	VMFLF(vm,file,line,func);
714 
715 	if(size <= 0 || align <= 0)
716 	{	CLRINUSE(vd, inuse);
717 		return NIL(Void_t*);
718 	}
719 
720 	if(ISLOCK(vd,0) )
721 	{	CLRINUSE(vd, inuse);
722 		return NIL(Void_t*);
723 	}
724 	SETLOCK(vd,0);
725 
726 	if((s = ROUND(size,ALIGN) + DB_EXTRA) < sizeof(Body_t))
727 		s = sizeof(Body_t);
728 
729 	if(!(data = (Vmuchar_t*)KPVALIGN(vm,s,align,(*(Vmbest->alignf)))) )
730 		goto done;
731 
732 	data += DB_HEAD;
733 	dbsetinfo(data,size,file,line);
734 
735 	if((vd->mode&VM_TRACE) && _Vmtrace)
736 	{	vm->file = file; vm->line = line; vm->func = func;
737 		(*_Vmtrace)(vm,NIL(Vmuchar_t*),data,size,align);
738 	}
739 
740 done:
741 	CLRLOCK(vd,0);
742 	ANNOUNCE(0, vm, VM_ALLOC, (Void_t*)data, vm->disc);
743 	CLRINUSE(vd, inuse);
744 	return (Void_t*)data;
745 }
746 
747 /* print statistics of region vm. If vm is NULL, use Vmregion */
748 #if __STD_C
749 ssize_t vmdbstat(Vmalloc_t* vm)
750 #else
751 ssize_t vmdbstat(vm)
752 Vmalloc_t*	vm;
753 #endif
754 {	Vmstat_t	st;
755 	char		buf[1024], *bufp;
756 
757 	vmstat(vm ? vm : Vmregion, &st);
758 	bufp = buf;
759 	bufp = (*_Vmstrcpy)(bufp, "n_busy", '=');
760 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)((Vmulong_t)st.n_busy,-1), ',');
761 	bufp = (*_Vmstrcpy)(bufp, " s_busy", '=');
762 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(st.s_busy),-1), '\n');
763 	bufp = (*_Vmstrcpy)(bufp, "n_free", '=');
764 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)((Vmulong_t)st.n_free,-1), ',');
765 	bufp = (*_Vmstrcpy)(bufp, " s_free", '=');
766 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(st.s_free),-1), '\n');
767 	bufp = (*_Vmstrcpy)(bufp, "m_busy", '=');
768 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(st.m_busy),-1), ',');
769 	bufp = (*_Vmstrcpy)(bufp, " m_free", '=');
770 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(st.m_free),-1), '\n');
771 	bufp = (*_Vmstrcpy)(bufp, "n_segment", '=');
772 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)((Vmulong_t)st.n_seg,-1), ',');
773 	bufp = (*_Vmstrcpy)(bufp, " extent", '=');
774 	bufp = (*_Vmstrcpy)(bufp, (*_Vmitoa)(VLONG(st.extent),-1), '\n');
775 	*bufp = 0;
776 	write(Dbfd, buf, strlen(buf));
777 	return strlen(buf);
778 }
779 
780 static Vmethod_t _Vmdebug =
781 {
782 	dballoc,
783 	dbresize,
784 	dbfree,
785 	dbaddr,
786 	dbsize,
787 	dbcompact,
788 	dbalign,
789 	VM_MTDEBUG
790 };
791 
792 __DEFINE__(Vmethod_t*,Vmdebug,&_Vmdebug);
793 
794 #ifdef NoF
795 NoF(vmdebug)
796 #endif
797 
798 #endif
799