1 /* $Id: dbg.c,v 1.3 2002/10/19 08:30:57 tommy Exp $ */
2 
3 /*
4  * Copyright (c) 2002 Tom Marshall <tommy@tig-grr.com>
5  *
6  * This program is free software.  It may be distributed under the terms
7  * in the file LICENSE, found in the top level of the distribution.
8  *
9  * dbg.c: debug functions for bmf.
10  */
11 
12 #include "config.h"
13 #include "dbg.h"
14 #include <stdarg.h>
15 
16 
17 uint g_verbose = 0;
18 
verbose(int level,const char * fmt,...)19 void verbose( int level, const char* fmt, ... )
20 {
21     if( g_verbose >= level )
22     {
23         char str[4096];
24         va_list v;
25         va_start( v, fmt );
26         vsnprintf( str, sizeof(str)-1, fmt, v );
27         str[sizeof(str)-1] = '\0';
28 #ifdef _UNIX
29         fputs( str, stderr );
30 #endif
31 #ifdef _WIN32
32         ::OutputDebugString( str );
33 #endif
34     }
35 }
36 
37 #ifndef NDEBUG
38 
dbgout(const char * fmt,...)39 void dbgout( const char* fmt, ... )
40 {
41     char str[4096];
42     va_list v;
43     va_start( v, fmt );
44     vsnprintf( str, sizeof(str)-1, fmt, v );
45     str[sizeof(str)-1] = '\0';
46 #ifdef _UNIX
47     fputs( str, stderr );
48 #endif
49 #ifdef _WIN32
50     ::OutputDebugString( str );
51 #endif
52 }
53 
54 /*
55  * Heap management routines.  These routines use unbalanced binary trees to
56  * keep track of allocations in an attempt to make them fast yet simple.
57  *
58  * Each block of memory consists of an alloc_node header, the requested
59  * memory block, and guard bytes before and after the requested memory
60  * block.  The requested memory block is filled with a semi-random byte
61  * value to ensure that the caller does not rely on any particular initial
62  * bit pattern (eg. a block of zeros or NULLs).  It is refilled with a
63  * (possibly different) byte value after deallocation to ensure that the
64  * caller doesn't attempt to use the freed memory.
65  */
66 
67 /* we need to use the real malloc and free */
68 #undef malloc
69 #undef free
70 
71 typedef struct _alloc_node
72 {
73     struct _alloc_node* lptr;
74     struct _alloc_node* rptr;
75     size_t              len;
76     cpchar              file;
77     uint                line;
78 } alloc_node;
79 
80 static alloc_node* g_heap = NULL;
81 
82 /* Our magic guard bytes */
83 static byte g_guard[] =
84 {
85     0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF,
86     0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF
87 };
88 
debug_malloc(cpchar file,uint line,size_t n,int fill)89 void* debug_malloc( cpchar file, uint line, size_t n, int fill )
90 {
91     byte* pmem = NULL;
92     alloc_node* pnode;
93 
94     pmem = NULL;
95     if( n == 0 )
96     {
97         n = 1;
98     }
99     pnode = (alloc_node*)malloc( n + 2*sizeof(g_guard) + sizeof(alloc_node) );
100     if( pnode != NULL )
101     {
102         alloc_node** ppuplink;
103         alloc_node* pcur;
104 
105         pmem = (byte*)pnode + sizeof(alloc_node) + sizeof(g_guard);
106         memcpy( pmem - sizeof(g_guard), g_guard, sizeof(g_guard) );
107         memset( pmem, fill, n );
108         memcpy( pmem + n, g_guard, sizeof(g_guard) );
109 
110         pnode->lptr = pnode->rptr = NULL;
111         pnode->len = n;
112         pnode->file = file;
113         pnode->line = line;
114         ppuplink = &g_heap;
115         pcur = g_heap;
116         while( pcur != NULL )
117         {
118             if( pnode == pcur )
119             {
120                 dbgout( "%s(%u): *** FATAL: duplicate memory allocated ***\n", file, line );
121                 assert( false );
122                 exit( -1 );
123             }
124             if( pnode < pcur )
125             {
126                 ppuplink = &pcur->lptr;
127                 pcur = pcur->lptr;
128             }
129             else
130             {
131                 ppuplink = &pcur->rptr;
132                 pcur = pcur->rptr;
133             }
134         }
135         *ppuplink = pnode;
136     }
137 
138     return pmem;
139 }
140 
debug_free(cpchar file,uint line,void * p)141 void debug_free( cpchar file, uint line, void* p )
142 {
143     alloc_node** ppuplink;
144     alloc_node* pcur;
145 
146     if( p == NULL )
147     {
148         return;
149     }
150     if( g_heap == NULL )
151     {
152         dbgout( "%s(%u): *** FATAL: delete with empty heap ***\n", file, line );
153         assert( false );
154         exit( -1 );
155     }
156 
157     ppuplink = &g_heap;
158     pcur = g_heap;
159     while( pcur != NULL )
160     {
161         void* pcurblk = (char*)pcur + sizeof(alloc_node) + sizeof(g_guard);
162         if( p == pcurblk )
163         {
164             byte* pmem = (byte*)p;
165             if( memcmp( pmem - sizeof(g_guard), g_guard, sizeof(g_guard) ) != 0 ||
166                 memcmp( pmem + pcur->len, g_guard, sizeof(g_guard) ) != 0 )
167             {
168                 dbgout( "%s(%u): *** FATAL: corrupted memory at %p\n", file, line, p );
169                 assert( false );
170                 exit( -1 );
171             }
172             memset( pmem, rand(), pcur->len );
173             if( pcur->lptr && pcur->rptr )
174             {
175                 /*
176                  * node has both ptrs so replace it with left child and move
177                  * right child to bottom right of left child's tree
178                  */
179                 alloc_node* pend = pcur->lptr;
180                 while( pend->rptr ) pend = pend->rptr;
181                 *ppuplink = pcur->lptr;
182                 pend->rptr = pcur->rptr;
183             }
184             else
185             {
186                 /* move child up */
187                 *ppuplink = (pcur->lptr) ? pcur->lptr : pcur->rptr;
188             }
189             free( pcur );
190             return;
191         }
192         if( p < pcurblk )
193         {
194             ppuplink = &pcur->lptr;
195             pcur = pcur->lptr;
196         }
197         else
198         {
199             ppuplink = &pcur->rptr;
200             pcur = pcur->rptr;
201         }
202     }
203 
204     dbgout( "%s(%u): *** FATAL: delete on unalloced memory ***\n", file, line );
205     assert( false );
206     exit( -1 );
207 }
208 
debug_realloc(cpchar file,uint line,void * p,size_t n)209 void* debug_realloc( cpchar file, uint line, void* p, size_t n )
210 {
211     void* pnew;
212 
213     if( p == NULL )
214     {
215         pnew = debug_malloc( file, line, n, rand() );
216     }
217     else if( n == 0 )
218     {
219         debug_free( file, line, p );
220         pnew = NULL;
221     }
222     else
223     {
224         alloc_node* pnode = (alloc_node*)((char*)p-sizeof(g_guard)-sizeof(alloc_node));
225         pnew = debug_malloc( file, line, n, rand() );
226         if( pnew != NULL )
227         {
228             memcpy( pnew, p, pnode->len );
229             debug_free( file, line, p );
230         }
231     }
232 
233     return pnew;
234 }
235 
debug_strdup(cpchar file,uint line,cpchar s)236 char* debug_strdup( cpchar file, uint line, cpchar s )
237 {
238     char* s2;
239     uint sl = strlen(s);
240 
241     s2 = (char*)debug_malloc( file, line, sl+1, 0 );
242     memcpy( s2, s, sl );
243     s2[sl] = '\0';
244 
245     return s2;
246 }
247 
debug_strndup(cpchar file,uint line,cpchar s,size_t n)248 char* debug_strndup( cpchar file, uint line, cpchar s, size_t n )
249 {
250     char* s2;
251     uint sl = strlen(s);
252 
253     sl = min( n-1, sl );
254     s2 = (char*)debug_malloc( file, line, sl+1, 0 );
255     memcpy( s2, s, sl );
256     s2[sl] = '\0';
257 
258     return s2;
259 }
260 
walk_alloc_tree(alloc_node * pcur,size_t * pttl)261 static void walk_alloc_tree( alloc_node* pcur, size_t* pttl )
262 {
263     if( pcur != NULL )
264     {
265         walk_alloc_tree( pcur->lptr, pttl );
266         dbgout( "%s(%u): %u bytes at %p\n", pcur->file, pcur->line,
267                 pcur->len, pcur+sizeof(alloc_node)+sizeof(g_guard) );
268         *pttl += pcur->len;
269         walk_alloc_tree( pcur->rptr, pttl );
270     }
271 }
272 
dump_alloc_heap(void)273 void dump_alloc_heap( void )
274 {
275     if( g_heap != NULL )
276     {
277         size_t ttl = 0;
278         dbgout( "\n" );
279         dbgout( "Memory leaks detected\n" );
280         dbgout( "=====================\n" );
281         dbgout( "\n" );
282         walk_alloc_tree( g_heap, &ttl );
283         dbgout( "\n" );
284         dbgout( "=====================\n" );
285         dbgout( "Total bytes: %u\n", ttl );
286         dbgout( "=====================\n" );
287     }
288 }
289 
290 #else /* ndef NDEBUG */
291 
dbgout(const char * fmt,...)292 void dbgout( const char* fmt, ... )
293 {
294     /* empty */
295 }
296 
dump_alloc_heap(void)297 void dump_alloc_heap( void )
298 {
299     /* empty */
300 }
301 
302 #endif /* ndef NDEBUG */
303