popped all patches
[ckermit.git] / ckcmdb.c
1 /*
2   C K C M D B . C  --  malloc debugger.
3 */
4
5 /*
6   Author: Howie Kaye, Columbia University Center for Computing Activities.
7
8   Copyright (C) 1985, 1999,
9     Trustees of Columbia University in the City of New York.
10     All rights reserved.  See the C-Kermit COPYING.TXT file or the
11     copyright text in the ckcmai.c module for disclaimer and permissions.
12 */
13 /* Use the real ones in this module! */
14 #ifdef malloc
15 #undef malloc
16 #endif /* malloc */
17 #ifdef calloc
18 #undef calloc
19 #endif /* calloc */
20 #ifdef realloc
21 #undef realloc
22 #endif /* realloc */
23 #ifdef free
24 #undef free
25 #endif /* free */
26
27 #include "ckcsym.h"
28 #include <stdio.h>
29 #include "ckcdeb.h"
30
31 #ifdef COHERENT
32 _PROTOTYP ( FILE * fdopen, (int, char *) );
33 #endif /* COHERENT */
34
35 /*
36   memdebug:
37   variable to control memory debugging.
38   if memdebug ==  1, then action is always taken.
39   if memdebug ==  0, then no action is taken.
40   if memdebug == -1, then the user is asked (works well with gdb).
41 */
42 int memdebug = -1;
43 int disabled = 0;
44 int inited = 0;
45 /*
46   To use this package, compile your program with:
47   -Dmalloc=dmalloc -Dfree=dfree =Dcalloc=dcalloc ... -DMDEBUG
48   and then link it with ckcmdb.c.
49 */
50 #ifdef MDEBUG
51
52 #ifndef M_SIZE_T
53 #ifdef NEXT
54 #define M_SIZE_T size_t
55 #else
56 #ifdef SUNOS41
57 #define M_SIZE_T unsigned
58 #else
59 #define M_SIZE_T int
60 #endif /* SUNOS41 */
61 #endif /* NEXT */
62 #endif /* M_SIZE_T */
63
64 #ifdef CK_ANSIC
65 _PROTOTYP( void free, (void *) );
66 _PROTOTYP( void * malloc, (size_t) );
67 _PROTOTYP( void * realloc, (void *, size_t) );
68 #else
69 _PROTOTYP( VOID free, (char *) );
70 _PROTOTYP( char * malloc, (M_SIZE_T) );
71 _PROTOTYP( char * realloc, (char *, M_SIZE_T) );
72 #endif /* NEXT */
73
74 _PROTOTYP( VOID m_insert, (char *) );
75 _PROTOTYP( int m_delete, (char *) );
76
77 _PROTOTYP( char * dmalloc, (int) );
78 _PROTOTYP( char * dcalloc, (int, int) );
79 _PROTOTYP( char * drealloc, (char *, int) );
80
81 _PROTOTYP( char *set_range_check, (char *, int) );
82 _PROTOTYP( char *check_range, (char *) );
83 _PROTOTYP( static char *maybe_check_range, (char *) );
84
85 _PROTOTYP( static VOID maybe_quit, (char *) );
86 _PROTOTYP( static int ask, (char *) );
87
88 #ifndef min
89 #define min(x,y) ((x) < (y) ? (x) : (y))
90 #endif /* min */
91 #define RANGE "ABCDEFGHIJKLMNOP"
92 #define INTSIZE  sizeof(int)
93 #define LONGSIZE sizeof(long)
94 #define RSIZE    sizeof(RANGE)
95 #define RFRONT   min((RSIZE/2),LONGSIZE)
96 #define RBACK    min((RSIZE-RFRONT),LONGSIZE)
97
98 char *
99 dmalloc(size) int size; {
100     char *cp;
101
102     cp = malloc(size + RSIZE + INTSIZE);
103     if (cp) {
104         cp = set_range_check(cp, size);
105         m_insert(cp);
106     }
107     return(cp);
108 }
109
110 char *
111 dcalloc(nelem, elsize) int nelem, elsize; {
112     char *cp;
113
114     cp = dmalloc(nelem * elsize);
115     if (cp)
116         memset(cp, 0, nelem * elsize);
117     return(cp);
118 }
119
120 char *
121 drealloc(bp,size) char *bp; int size; {
122     char *cp;
123
124     if (bp == NULL) {
125         maybe_quit("Freeing NULL pointer");
126     } else {
127         m_delete(bp);
128         cp = check_range(bp);
129     }
130     cp = realloc(cp, size + RSIZE + INTSIZE);
131     if (cp) {
132         cp = set_range_check(cp, size);
133         m_insert(cp);
134     }
135     return(cp);
136 }
137
138 VOID
139 dfree(cp) char *cp; {
140     if (cp == NULL)
141         maybe_quit("Freeing NULL pointer");
142     else {
143         switch(m_delete(cp)) {
144         case 0:
145             cp = maybe_check_range(cp);
146             break;
147         case 1:
148             cp = check_range(cp);
149             break;
150         case 2:
151             break;
152         }
153     }
154 #ifndef CK_ANSIC
155     return(free(cp));
156 #endif /* CK_ANSIC */
157 }
158
159 char *
160 set_range_check(cp,size) char *cp; int size; {
161     register int i;
162     int tmp = size;
163
164     for(i = 0; i < INTSIZE; i++) {      /* set the size in the string */
165         cp[i] = tmp & 0xff;
166         tmp >>= 8;
167     }
168     cp += INTSIZE;                      /* skip the size */
169
170     for(i = 0; i < RFRONT; i++)         /* set the front of the range check */
171         cp[i] = RANGE[i];               /* string */
172
173     cp += RFRONT;                       /* skip the front range check */
174
175     for(i = 0; i < RBACK; i++)          /* set the back odf the range check */
176         cp[i+size] = RANGE[i+RFRONT];
177
178     return(cp);
179 }
180
181 /*
182   Put calls to this routine in your code any place where you want to
183   check whether you've copied too many characters into a malloc'd space.
184 */
185 char *
186 check_range(cp) char *cp; {
187     register char *bp = cp - RFRONT - INTSIZE;
188     char *xp = bp;
189     register int i;
190     int size = 0;
191
192     for(i = 0 ; i < INTSIZE; i++) {     /* get the size out of the string */
193         size <<= 8;
194         size |= bp[INTSIZE-i-1] & 0xff;
195     }
196     bp += INTSIZE;
197
198     for(i = 0; i < RFRONT; i++)         /* check front range check */
199         if (bp[i] != RANGE[i]) {
200             maybe_quit("leftside malloc buffer overrun");
201             break;
202         }
203     bp += RFRONT;                       /* skip front range check */
204
205     for(i = 0; i < RBACK; i++)          /* check back range check */
206         if (bp[i+size] != RANGE[i+RFRONT]) {
207             maybe_quit("rightside malloc buffer overrun");
208             break;
209         }
210     return(xp);
211 }
212
213 static char *
214 maybe_check_range(cp) char *cp; {
215     register char *bp = cp - RFRONT - INTSIZE;
216     char *xp = bp;
217     register int i;
218     int size = 0;
219
220     for(i = 0 ; i < INTSIZE; i++) {     /* get the size out of the string */
221         size <<= 8;
222         size |= bp[INTSIZE-i-1] & 0xff;
223     }
224     bp += INTSIZE;
225
226     for(i = 0; i < RFRONT; i++)         /* check front range check */
227         if (bp[i] != RANGE[i]) {
228             return(cp);
229         }
230     bp += RFRONT;                       /* skip front range check */
231
232     for(i = 0; i < RBACK; i++)          /* check back range check */
233         if (bp[i+size] != RANGE[i+RFRONT]) {
234             fprintf(stderr,"rightside malloc buffer overrun\n");
235             abort();
236             break;
237         }
238     return(xp);
239 }
240
241 #define BUCKETS 10000
242 char *m_used[BUCKETS];
243 char *m_used2[BUCKETS];
244
245 VOID
246 m_insert(cp) register char *cp; {
247     register int i;
248
249     if (disabled)
250         return;
251
252     for(i = 0; i < BUCKETS; i++)
253         if (m_used[i] == 0) {
254             m_used[i] = cp;
255             return;
256         }
257     disabled ++;
258 }
259
260 static VOID
261 m_insert2(cp) register char *cp; {
262     register int i;
263
264     if (disabled)
265         return;
266     for(i = 0; i < BUCKETS; i++)
267         if (m_used2[i] == 0) {
268             m_used2[i] = cp;
269             return;
270         }
271     disabled ++;
272 }
273
274 int
275 m_delete(cp) register char *cp; {
276     register int i;
277
278     for(i = 0; i < BUCKETS; i++)
279         if (m_used[i] == cp) {
280             m_used[i] = 0;
281             return(1);
282         }
283     for(i = 0; i < BUCKETS; i++)
284         if (m_used2[i] == cp) {
285             m_used2[i] = 0;
286             return(2);
287         }
288     if (disabled)
289         return(0);
290
291     maybe_quit("Freeing unmalloc'ed pointer");
292     return(0);
293 }
294
295 VOID
296 m_init() {
297     register int i;
298
299     inited = 1;
300     disabled = 0;
301 #ifdef NEXT
302     malloc_debug(2+4+8+16);
303 #endif /* NEXT */
304
305     for(i = 0; i < BUCKETS; i++)
306       m_used[i] = 0;
307 }
308
309 VOID
310 m_done() {
311     register int i,j=0;
312
313     if (disabled)
314         return;
315     for(i = 0; i < BUCKETS; i++)
316         if (m_used[i] != 0) {
317             if (memdebug) {
318                 if (j == 0)
319                     fprintf(stderr,"unfree'ed buffers, indices: ");
320                 fprintf(stderr,"%d, ", i);
321                 j++;
322             }
323         }
324     if (j)
325         fprintf(stderr,"\n");
326     for(i = 0; i < BUCKETS; i++)
327         if (m_used2[i] != 0) {
328             if (memdebug) {
329                 if (j == 0)
330                     fprintf(stderr,"unfree'ed registered buffers, indices: ");
331                 fprintf(stderr,"%d, ", i);
332                 j++;
333             }
334         }
335     if (j)
336         fprintf(stderr,"\n");
337     if (j)
338         maybe_quit("Unfree'ed malloc buffers");
339 }
340
341 VOID
342 m_checkranges() {
343     int i;
344
345     for ( i = 0; i < BUCKETS; i++)
346         if (m_used[i])
347             check_range(m_used[i]);
348 }
349
350 static VOID
351 maybe_quit(str) char *str; {
352     debug(F100,"mdebug maybe_quit","",0);
353     if (memdebug == 0)
354         return;
355     fprintf(stderr,"%s\n",str);
356     if (memdebug == 1)
357         abort();
358     if (memdebug == -1)
359         if (ask("Quit? "))
360             abort();
361 }
362
363 static int
364 ask(str) char *str; {
365     char buf[100];
366     FILE *in;
367     int fd;
368
369     fd = dup(fileno(stdin));
370     in = fdopen(fd, "r");
371     while(1) {
372         fprintf(stderr,str);
373         fflush(stderr);
374         if (fgets(buf, 99, in) == NULL) /* EOF? */
375             return(0);
376         if (buf[0] == 'n' || buf[0] == 'N') {
377             fclose(in);
378             return(0);
379         }
380         if (buf[0] == 'y' || buf[0] == 'Y') {
381             fclose(in);
382             return(1);
383         }
384         fprintf(stderr,"please answer y/n.\n");
385     }
386 }
387 #endif /* MDEBUG */