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