1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
22 #if defined(_UWIN) && defined(_BLD_ast)
24 void _STUB_vmdebug(){}
30 /* Method to help with debugging. This does rigorous checks on
31 ** addresses and arena integrity.
33 ** Written by Kiem-Phong Vo, kpv@research.att.com, 01/16/94.
36 /* structure to keep track of file names */
37 typedef struct _dbfile_s Dbfile_t
;
42 static Dbfile_t
* Dbfile
;
44 /* global watch list */
47 static Void_t
* Dbwatch
[S_WATCH
];
49 /* types of warnings reported by dbwarn() */
57 #define LONGV(x) ((Vmulong_t)(x))
59 static int Dbinit
= 0;
60 #define DBINIT() (Dbinit ? 0 : (dbinit(), Dbinit=1) )
63 if((fd
= vmtrace(-1)) >= 0)
67 static int Dbfd
= 2; /* default warning file descriptor */
80 /* just an entry point to make it easy to set break point */
82 static void vmdbwarn(Vmalloc_t
* vm
, char* mesg
, int n
)
84 static void vmdbwarn(vm
, mesg
, n
)
90 reg Vmdata_t
* vd
= vm
->data
;
93 if(vd
->mode
&VM_DBABORT
)
97 /* issue a warning of some type */
99 static void dbwarn(Vmalloc_t
* vm
, Void_t
* data
, int where
,
100 const char* file
, int line
, const Void_t
* func
, int type
)
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 */
112 char buf
[1024], *bufp
, *endbuf
, *s
;
113 #define SLOP 64 /* enough for a message and an int */
118 endbuf
= buf
+ sizeof(buf
);
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", ':');
132 bufp
= (*_Vmstrcpy
)(bufp
, "region", '=');
133 bufp
= (*_Vmstrcpy
)(bufp
, (*_Vmitoa
)(VLONG(vm
), 0), ':');
136 { bufp
= (*_Vmstrcpy
)(bufp
,"block",'=');
137 bufp
= (*_Vmstrcpy
)(bufp
,(*_Vmitoa
)(VLONG(data
),0),':');
141 { if(where
== DB_ALLOC
)
142 bufp
= (*_Vmstrcpy
)(bufp
, "can't get memory", ':');
143 else bufp
= (*_Vmstrcpy
)(bufp
, "region is locked", ':');
145 else if(type
== DB_FREE
|| type
== DB_RESIZE
)
147 bufp
= (*_Vmstrcpy
)(bufp
, "unallocated block", ':');
148 else bufp
= (*_Vmstrcpy
)(bufp
, "already freed", ':');
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", ':');
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),':');
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), ':');
183 vmdbwarn(vm
,buf
,(bufp
-buf
));
186 /* check for watched address and issue warnings */
188 static void dbwatch(Vmalloc_t
* vm
, Void_t
* data
,
189 const char* file
, int line
, const Void_t
* func
, int type
)
191 static void dbwatch(vm
, data
, file
, line
, func
, type
)
202 for(n
= Dbnwatch
; n
>= 0; --n
)
203 { if(Dbwatch
[n
] == data
)
204 { dbwarn(vm
,data
,type
,file
,line
,func
,DB_WATCH
);
210 /* record information about the block */
212 static void dbsetinfo(Vmuchar_t
* data
, size_t size
, const char* file
, int line
)
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 */
221 reg Vmuchar_t
*begp
, *endp
;
222 reg Dbfile_t
*last
, *db
;
226 /* find the file structure */
227 if(!file
|| !file
[0])
230 { for(last
= NIL(Dbfile_t
*), db
= Dbfile
; db
; last
= db
, db
= db
->next
)
231 if(strcmp(db
->file
,file
) == 0)
234 { db
= (Dbfile_t
*)vmalloc(Vmheap
,sizeof(Dbfile_t
)+strlen(file
));
236 { (*_Vmstrcpy
)(db
->file
,file
,0);
241 else if(last
) /* move-to-front heuristic */
242 { last
->next
= db
->next
;
248 DBSETFL(data
,(db
? db
->file
: NIL(char*)),line
);
250 DBSEG(data
) = SEG(DBBLOCK(data
));
252 DBHEAD(data
,begp
,endp
);
255 DBTAIL(data
,begp
,endp
);
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.
265 static long dbaddr(Vmalloc_t
* vm
, Void_t
* addr
)
267 static long dbaddr(vm
, addr
)
272 reg Block_t
*b
, *endb
;
275 reg
long offset
= -1L;
276 reg Vmdata_t
* vd
= vm
->data
;
277 reg
int local
, inuse
;
281 if(ISLOCK(vd
,local
) || !addr
)
282 { CLRINUSE(vd
, inuse
);
287 b
= endb
= NIL(Block_t
*);
288 for(seg
= vd
->seg
; seg
; seg
= seg
->next
)
290 endb
= (Block_t
*)(seg
->baddr
- sizeof(Head_t
));
291 if((Vmuchar_t
*)addr
> (Vmuchar_t
*)b
&&
292 (Vmuchar_t
*)addr
< (Vmuchar_t
*)endb
)
298 if(local
) /* must be vmfree or vmresize checking address */
299 { if(DBSEG(addr
) == seg
)
301 if(ISBUSY(SIZE(b
)) && !ISJUNK(SIZE(b
)) )
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
;
320 b
= (Block_t
*)((Vmuchar_t
*)DATA(b
) + (SIZE(b
)&~BITS
) );
331 static long dbsize(Vmalloc_t
* vm
, Void_t
* addr
)
333 static long dbsize(vm
, addr
)
338 reg Block_t
*b
, *endb
;
341 reg Vmdata_t
* vd
= vm
->data
;
346 { CLRINUSE(vd
, inuse
);
352 for(seg
= vd
->seg
; seg
; seg
= seg
->next
)
354 endb
= (Block_t
*)(seg
->baddr
- sizeof(Head_t
));
355 if((Vmuchar_t
*)addr
<= (Vmuchar_t
*)b
||
356 (Vmuchar_t
*)addr
>= (Vmuchar_t
*)endb
)
359 { if(addr
== (Void_t
*)DB2DEBUG(DATA(b
)))
360 { if(ISBUSY(SIZE(b
)) && !ISJUNK(SIZE(b
)) )
361 size
= (long)DBSIZE(addr
);
365 b
= (Block_t
*)((Vmuchar_t
*)DATA(b
) + (SIZE(b
)&~BITS
) );
375 static Void_t
* dballoc(Vmalloc_t
* vm
, size_t size
)
377 static Void_t
* dballoc(vm
, size
)
387 reg Vmdata_t
* vd
= vm
->data
;
391 VMFLF(vm
,file
,line
,func
);
394 { dbwarn(vm
,NIL(Vmuchar_t
*),0,file
,line
,func
,DB_ALLOC
);
400 if(vd
->mode
&VM_DBCHECK
)
403 s
= ROUND(size
,ALIGN
) + DB_EXTRA
;
404 if(s
< sizeof(Body_t
)) /* no tiny blocks during Vmdebug */
407 if(!(data
= (Vmuchar_t
*)KPVALLOC(vm
,s
,(*(Vmbest
->allocf
))) ) )
408 { dbwarn(vm
,NIL(Vmuchar_t
*),DB_ALLOC
,file
,line
,func
,DB_ALLOC
);
412 data
= DB2DEBUG(data
);
413 dbsetinfo(data
,size
,file
,line
);
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);
421 dbwatch(vm
,data
,file
,line
,func
,DB_ALLOC
);
425 ANNOUNCE(0, vm
, VM_ALLOC
, (Void_t
*)data
, vm
->disc
);
427 return (Void_t
*)data
;
432 static int dbfree(Vmalloc_t
* vm
, Void_t
* data
)
434 static int dbfree(vm
, data
)
443 reg
int rv
, *ip
, *endip
;
444 reg Vmdata_t
* vd
= vm
->data
;
448 VMFLF(vm
,file
,line
,func
);
451 { CLRINUSE(vd
, inuse
);
456 { dbwarn(vm
,NIL(Vmuchar_t
*),0,file
,line
,func
,DB_FREE
);
462 if(vd
->mode
&VM_DBCHECK
)
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
);
475 dbwatch(vm
,data
,file
,line
,func
,DB_FREE
);
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);
482 /* clear free space */
484 endip
= ip
+ (DBSIZE(data
)+sizeof(int)-1)/sizeof(int);
488 rv
= KPVFREE((vm
), (Void_t
*)DB2BEST(data
), (*Vmbest
->freef
));
490 ANNOUNCE(0, vm
, VM_FREE
, data
, vm
->disc
);
495 /* Resizing an existing block */
497 static Void_t
* dbresize(Vmalloc_t
* vm
, Void_t
* addr
, reg
size_t size
, int type
)
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 */
507 reg
size_t s
, oldsize
;
509 char *file
, *oldfile
;
512 reg Vmdata_t
* vd
= vm
->data
;
518 data
= (Vmuchar_t
*)dballoc(vm
,size
);
522 { (void)dbfree(vm
,addr
);
527 VMFLF(vm
,file
,line
,func
);
530 { dbwarn(vm
,NIL(Vmuchar_t
*),0,file
,line
,func
,DB_RESIZE
);
536 if(vd
->mode
&VM_DBCHECK
)
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
);
549 dbwatch(vm
,addr
,file
,line
,func
,DB_RESIZE
);
551 /* Vmbest data block */
552 data
= DB2BEST(addr
);
553 oldsize
= DBSIZE(addr
);
554 oldfile
= DBFILE(addr
);
555 oldline
= DBLINE(addr
);
558 s
= ROUND(size
,ALIGN
) + DB_EXTRA
;
559 if(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
);
568 { data
= DB2DEBUG(data
);
569 dbsetinfo(data
,size
,file
,line
);
571 if((vd
->mode
&VM_TRACE
) && _Vmtrace
)
572 { vm
->file
= file
; vm
->line
= line
;
573 (*_Vmtrace
)(vm
,(Vmuchar_t
*)addr
,data
,size
,0);
576 dbwatch(vm
,data
,file
,line
,func
,DB_RESIZED
);
580 ANNOUNCE(0, vm
, VM_RESIZE
, (Void_t
*)data
, vm
->disc
);
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
);
587 return (Void_t
*)data
;
590 /* compact any residual free space */
592 static int dbcompact(Vmalloc_t
* vm
)
594 static int dbcompact(vm
)
598 return (*(Vmbest
->compactf
))(vm
);
601 /* check for memory overwrites over all live blocks */
603 int vmdbcheck(Vmalloc_t
* vm
)
609 reg Block_t
*b
, *endb
;
612 reg Vmdata_t
* vd
= vm
->data
;
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)
618 if(!(vd
->mode
&VM_MTDEBUG
))
624 for(seg
= vd
->seg
; seg
; seg
= seg
->next
)
626 endb
= (Block_t
*)(seg
->baddr
- sizeof(Head_t
));
628 { reg Vmuchar_t
*data
, *begp
, *endp
;
630 if(ISJUNK(SIZE(b
)) || !ISBUSY(SIZE(b
)))
633 data
= DB2DEBUG(DATA(b
));
634 if(DBISBAD(data
)) /* seen this before */
639 DBHEAD(data
,begp
,endp
);
640 for(; begp
< endp
; ++begp
)
641 if(*begp
!= DB_MAGIC
)
644 DBTAIL(data
,begp
,endp
);
645 for(; begp
< endp
; ++begp
)
646 { if(*begp
== DB_MAGIC
)
649 dbwarn(vm
,data
,begp
-data
,NIL(char*),0,0,DB_CHECK
);
655 next
: b
= (Block_t
*)((Vmuchar_t
*)DATA(b
) + (SIZE(b
)&~BITS
));
662 /* set/delete an address to watch */
664 Void_t
* vmdbwatch(Void_t
* addr
)
666 Void_t
* vmdbwatch(addr
)
667 Void_t
* addr
; /* address to insert */
677 { for(n
= Dbnwatch
- 1; n
>= 0; --n
)
678 if(Dbwatch
[n
] == addr
)
680 if(n
< 0) /* insert */
681 { if(Dbnwatch
== S_WATCH
)
682 { /* delete left-most */
685 for(n
= 0; n
< Dbnwatch
; ++n
)
686 Dbwatch
[n
] = Dbwatch
[n
+1];
688 Dbwatch
[Dbnwatch
] = addr
;
696 static Void_t
* dbalign(Vmalloc_t
* vm
, size_t size
, size_t align
)
698 static Void_t
* dbalign(vm
, size
, align
)
709 reg Vmdata_t
* vd
= vm
->data
;
713 VMFLF(vm
,file
,line
,func
);
715 if(size
<= 0 || align
<= 0)
716 { CLRINUSE(vd
, inuse
);
721 { CLRINUSE(vd
, inuse
);
726 if((s
= ROUND(size
,ALIGN
) + DB_EXTRA
) < sizeof(Body_t
))
729 if(!(data
= (Vmuchar_t
*)KPVALIGN(vm
,s
,align
,(*(Vmbest
->alignf
)))) )
733 dbsetinfo(data
,size
,file
,line
);
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
);
742 ANNOUNCE(0, vm
, VM_ALLOC
, (Void_t
*)data
, vm
->disc
);
744 return (Void_t
*)data
;
747 /* print statistics of region vm. If vm is NULL, use Vmregion */
749 ssize_t
vmdbstat(Vmalloc_t
* vm
)
755 char buf
[1024], *bufp
;
757 vmstat(vm
? vm
: Vmregion
, &st
);
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');
776 write(Dbfd
, buf
, strlen(buf
));
780 static Vmethod_t _Vmdebug
=
792 __DEFINE__(Vmethod_t
*,Vmdebug
,&_Vmdebug
);