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