1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2011 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
24void _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 */
37typedef struct _dbfile_s	Dbfile_t;
38struct _dbfile_s
39{	Dbfile_t*	next;
40	char		file[1];
41};
42static Dbfile_t*	Dbfile;
43
44/* global watch list */
45#define S_WATCH	32
46static int	Dbnwatch;
47static 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
59static int Dbinit = 0;
60#define DBINIT()	(Dbinit ? 0 : (dbinit(), Dbinit=1) )
61static void dbinit()
62{	int	fd;
63	if((fd = vmtrace(-1)) >= 0)
64		vmtrace(fd);
65}
66
67static int	Dbfd = 2;	/* default warning file descriptor */
68#if __STD_C
69int vmdebug(int fd)
70#else
71int vmdebug(fd)
72int	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
82static void vmdbwarn(Vmalloc_t* vm, char* mesg, int n)
83#else
84static void vmdbwarn(vm, mesg, n)
85Vmalloc_t*	vm;
86char*		mesg;
87int		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
99static void dbwarn(Vmalloc_t* vm, Void_t* data, int where,
100		   const char* file, int line, const Void_t* func, int type)
101#else
102static void dbwarn(vm, data, where, file, line, func, type)
103Vmalloc_t*	vm;	/* region holding the block	*/
104Void_t*		data;	/* data block			*/
105int		where;	/* byte that was corrupted	*/
106const char*	file;	/* file where call originates	*/
107int		line;	/* line number of call		*/
108const Void_t*	func;	/* function called from		*/
109int		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
188static void dbwatch(Vmalloc_t* vm, Void_t* data,
189		    const char* file, int line, const Void_t* func, int type)
190#else
191static void dbwatch(vm, data, file, line, func, type)
192Vmalloc_t*	vm;
193Void_t*		data;
194const char*	file;
195int		line;
196const Void_t*	func;
197int		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
212static void dbsetinfo(Vmuchar_t* data, size_t size, const char* file, int line)
213#else
214static void dbsetinfo(data, size, file, line)
215Vmuchar_t*	data;	/* real address not the one from Vmbest	*/
216size_t		size;	/* the actual requested size		*/
217const char*	file;	/* file where the request came from	*/
218int		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
265static long dbaddr(Vmalloc_t* vm, Void_t* addr)
266#else
267static long dbaddr(vm, addr)
268Vmalloc_t*	vm;
269Void_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
323done:
324	CLRLOCK(vd,local);
325	CLRINUSE(vd, inuse);
326	return offset;
327}
328
329
330#if __STD_C
331static long dbsize(Vmalloc_t* vm, Void_t* addr)
332#else
333static long dbsize(vm, addr)
334Vmalloc_t*	vm;
335Void_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	}
368done:
369	CLRLOCK(vd,0);
370	CLRINUSE(vd, inuse);
371	return size;
372}
373
374#if __STD_C
375static Void_t* dballoc(Vmalloc_t* vm, size_t size)
376#else
377static Void_t* dballoc(vm, size)
378Vmalloc_t*	vm;
379size_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
423done:
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
432static int dbfree(Vmalloc_t* vm, Void_t* data )
433#else
434static int dbfree(vm, data )
435Vmalloc_t*	vm;
436Void_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
497static Void_t* dbresize(Vmalloc_t* vm, Void_t* addr, reg size_t size, int type)
498#else
499static Void_t* dbresize(vm,addr,size,type)
500Vmalloc_t*	vm;		/* region allocating from	*/
501Void_t*		addr;		/* old block of data		*/
502reg size_t	size;		/* new size			*/
503int		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
582done:	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
592static int dbcompact(Vmalloc_t* vm)
593#else
594static int dbcompact(vm)
595Vmalloc_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
603int vmdbcheck(Vmalloc_t* vm)
604#else
605int vmdbcheck(vm)
606Vmalloc_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
664Void_t* vmdbwatch(Void_t* addr)
665#else
666Void_t* vmdbwatch(addr)
667Void_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
696static Void_t* dbalign(Vmalloc_t* vm, size_t size, size_t align)
697#else
698static Void_t* dbalign(vm, size, align)
699Vmalloc_t*	vm;
700size_t		size;
701size_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
740done:
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
749ssize_t vmdbstat(Vmalloc_t* vm)
750#else
751ssize_t vmdbstat(vm)
752Vmalloc_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
780static 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
795NoF(vmdebug)
796#endif
797
798#endif
799