1 /**
2  * @file debug.h
3  * @author Joe Wingbermuehle
4  * @date 2003-2006
5  *
6  * @brief Debug functions.
7  *
8  */
9 
10 #include "debug.h"
11 #include <stdarg.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 /** Emit a message (if compiled with -DDEBUG). */
Debug(const char * str,...)16 void Debug(const char *str, ...)
17 {
18 #ifdef DEBUG
19 
20    va_list ap;
21    va_start(ap, str);
22    Assert(str);
23    fprintf(stderr, "DEBUG: ");
24    vfprintf(stderr, str, ap);
25    fprintf(stderr, "\n");
26    va_end(ap);
27 
28 #endif /* DEBUG */
29 }
30 
31 #ifdef DEBUG
32 
33 #define CHECKPOINT_LIST_SIZE 8
34 
35 typedef struct MemoryType {
36    const char *file;
37    unsigned int line;
38    size_t size;
39    char *pointer;
40    struct MemoryType *next;
41 } MemoryType;
42 
43 static MemoryType *allocations = NULL;
44 
45 static const char *checkpointFile[CHECKPOINT_LIST_SIZE];
46 static unsigned int checkpointLine[CHECKPOINT_LIST_SIZE];
47 static unsigned int checkpointOffset;
48 
49 /** Start the debugger. */
DEBUG_StartDebug(const char * file,unsigned int line)50 void DEBUG_StartDebug(const char *file, unsigned int line)
51 {
52    unsigned int x;
53    Debug("%s[%u]: debug mode started", file, line);
54    checkpointOffset = 0;
55    for(x = 0; x < CHECKPOINT_LIST_SIZE; x++) {
56       checkpointFile[x] = NULL;
57       checkpointLine[x] = 0;
58    }
59 }
60 
61 /** Stop the debugger. */
DEBUG_StopDebug(const char * file,unsigned int line)62 void DEBUG_StopDebug(const char *file, unsigned int line)
63 {
64    Debug("%s[%u]: debug mode stopped", file, line);
65    if(allocations) {
66       MemoryType *mp;
67       unsigned int count = 0;
68       Debug("MEMORY: memory leaks follow");
69       for(mp = allocations; mp; mp = mp->next) {
70          Debug("        %u bytes in %s at line %u",
71             mp->size, mp->file, mp->line);
72          ++count;
73       }
74       if(count == 1) {
75          Debug("MEMORY: 1 memory leak");
76       } else {
77          Debug("MEMORY: %u memory leaks", count);
78       }
79    } else {
80       Debug("MEMORY: no memory leaks");
81    }
82 }
83 
84 /** Set a checkpoint. */
DEBUG_SetCheckpoint(const char * file,unsigned int line)85 void DEBUG_SetCheckpoint(const char *file, unsigned int line)
86 {
87    checkpointFile[checkpointOffset] = file;
88    checkpointLine[checkpointOffset] = line;
89    checkpointOffset = (checkpointOffset + 1) % CHECKPOINT_LIST_SIZE;
90 }
91 
92 /** Display the location of the last checkpoint. */
DEBUG_ShowCheckpoint(void)93 void DEBUG_ShowCheckpoint(void)
94 {
95    unsigned int x, offset;
96    Debug("CHECKPOINT LIST (oldest)");
97    offset = checkpointOffset;
98    for(x = 0; x < CHECKPOINT_LIST_SIZE; x++) {
99       if(checkpointFile[offset]) {
100          Debug("   %s[%u]", checkpointFile[offset], checkpointLine[offset]);
101       }
102       offset = (offset + 1) % CHECKPOINT_LIST_SIZE;
103    }
104    Debug("END OF CHECKPOINT LIST (most recent)");
105 }
106 
107 /** Allocate memory and log. */
DEBUG_Allocate(size_t size,const char * file,unsigned int line)108 void *DEBUG_Allocate(size_t size, const char *file, unsigned int line)
109 {
110    MemoryType *mp;
111    mp = (MemoryType*)malloc(sizeof(MemoryType));
112    Assert(mp);
113    mp->file = file;
114    mp->line = line;
115    mp->size = size;
116    mp->pointer = malloc(size + sizeof(char) + 8);
117    if(!mp->pointer) {
118       Debug("MEMORY: %s[%u]: Memory allocation failed (%d bytes)",
119             file, line, (int)size);
120       Assert(0);
121    }
122 
123    /* Make uninitialized accesses easy to find. */
124    memset(mp->pointer, 85, size);
125 
126    /* Canary value for buffer underflow/overflow checking. */
127    mp->pointer[7] = 42;
128    mp->pointer[size + 8] = 42;
129 
130    mp->next = allocations;
131    allocations = mp;
132    return mp->pointer + 8;
133 }
134 
135 /** Reallocate memory and log. */
DEBUG_Reallocate(void * ptr,size_t size,const char * file,unsigned int line)136 void *DEBUG_Reallocate(void *ptr, size_t size,
137                        const char *file,
138                        unsigned int line)
139 {
140    MemoryType *mp;
141    if(!ptr) {
142       Debug("MEMORY: %s[%u]: Attempt to reallocate NULL pointer. "
143             "Calling Allocate...", file, line);
144       return DEBUG_Allocate(size, file, line);
145    } else {
146       char *cptr = (char*)ptr - 8;
147       for(mp = allocations; mp; mp = mp->next) {
148          if(mp->pointer == cptr) {
149             if(cptr[mp->size + 8] != 42) {
150                Debug("MEMORY: %s[%u]: The canary is dead (overflow).",
151                      file, line);
152             }
153             if(cptr[7] != 42) {
154                Debug("MEMORY: %s[%u]: The canary is dead (underflow).",
155                      file, line);
156             }
157             mp->file = file;
158             mp->line = line;
159             mp->size = size;
160             mp->pointer = realloc(cptr, size + sizeof(char) + 8);
161             if(!mp->pointer) {
162                Debug("MEMORY: %s[%u]: Failed to reallocate %d bytes.",
163                      file, line, (int)size);
164                Assert(0);
165             }
166             mp->pointer[7] = 42;
167             mp->pointer[size + 8] = 42;
168             return mp->pointer + 8;
169          }
170       }
171 
172       Debug("MEMORY: %s[%u]: Attempt to reallocate unallocated pointer",
173             file, line);
174       mp = malloc(sizeof(MemoryType));
175       Assert(mp);
176       mp->file = file;
177       mp->line = line;
178       mp->size = size;
179       mp->pointer = malloc(size + sizeof(char) + 8);
180       if(!mp->pointer) {
181          Debug("MEMORY: %s[%u]: Failed to reallocate %d bytes.",
182                file, line, (int)size);
183          Assert(0);
184       }
185       memset(mp->pointer, 85, size);
186       mp->pointer[7] = 42;
187       mp->pointer[size + 8] = 42;
188       mp->next = allocations;
189       allocations = mp;
190       return mp->pointer + 8;
191    }
192 }
193 
194 /** Release memory and log. */
DEBUG_Release(void ** ptr,const char * file,unsigned int line)195 void DEBUG_Release(void **ptr, const char *file, unsigned int line)
196 {
197    MemoryType *mp, *last;
198    if(!ptr) {
199       Debug("MEMORY: %s[%u]: Invalid attempt to release", file, line);
200    } else if(!*ptr) {
201       Debug("MEMORY: %s[%u]: Attempt to delete NULL pointer",
202             file, line);
203    } else {
204       char *cptr = (char*)*ptr - 8;
205       last = NULL;
206       for(mp = allocations; mp; mp = mp->next) {
207          if(mp->pointer == cptr) {
208             if(last) {
209                last->next = mp->next;
210             } else {
211                allocations = mp->next;
212             }
213 
214             if(cptr[mp->size + 8] != 42) {
215                Debug("MEMORY: %s[%u]: The canary is dead (overflow).",
216                      file, line);
217             }
218             if(cptr[7] != 42) {
219                Debug("MEMORY: %s[%u]: The canary is dead (underflow).",
220                      file, line);
221             }
222 
223             memset(cptr, 0xFF, mp->size + 8 + sizeof(char));
224             free(mp);
225             free(cptr);
226             *ptr = NULL;
227             return;
228          }
229          last = mp;
230       }
231       Debug("MEMORY: %s[%u]: Attempt to delete unallocated pointer",
232             file, line);
233       free(*ptr);
234 
235       /* This address should cause a segfault or bus error. */
236       *ptr = (void*)1;
237 
238    }
239 }
240 
241 #undef CHECKPOINT_LIST_SIZE
242 
243 #endif
244 
245