1
2 /*******************************************************************
3 *
4 * DESCRIPTION: mem3dc.c
5 *
6 * AUTHOR: Rob Rodger
7 *
8 * HISTORY: 23/12/96
9 *
10 *******************************************************************/
11
12
13 /* mem3dc.c simply keeps a record of all memory allocations called by */
14 /* AllocateMem() and checks if DeallocateMem() calls are legitimate */
15 /* i.e. the address freed has been allocated. This record */
16 /* includes the address, size, filename and line number for the associated */
17 /* AllocateMem() call. */
18 /* */
19 /* Also, fills in MALLOC_FILL_VALUE in memory malloced and FREE_FILL_VALUE */
20 /* in memory freed. */
21 /* */
22 /* A record is kept of the total amount of allocated memory outstanding in */
23 /* global TotalMemAllocated as well as the total number of outstanding mallocs */
24 /* in the global TotalMallocNum. Finally, a call to DumpMallocInfo(DUMPTOSCREEN) */
25 /* will give a textprint of each outstanding malloc record while */
26 /* while DumpMallocInfo(DUMPTOFILE) writes malloc records to a file with */
27 /* filename defined by MALLOCDUMPFILE define. Set APPEND_TO_DUMPFILE to 1 */
28 /* in define below if you wish to append malloc info to MALLOCDUMPFILE rather */
29 /* than over writing file each time. */
30 /* */
31 /* To use, define DBGMALLOC as 1 in "mem3dc.h". Obviously for final code */
32 /* set DBGMALLOC to 0. */
33 /* */
34 /* Note, must use AllocateMem() and DeallocateMem() in code - Do not */
35 /* make direct calls to AllocMem()/DeallocMem() contained in platform */
36 /* file our_mem.c */
37
38 #include "3dc.h"
39
40 #include <string.h>
41 #include "mem3dc.h" /* contains extern declarations for platform
42 specific memory allocation/deallocation.
43 Also contains DBGMALLOC define */
44
45 #if DBGMALLOC
46
47 #include "ourasert.h"
48
49 #define MAXMALLOCS 2500000
50 /* assertion fires if max exceeded */
51 /* changed to 1000000 by DHM 7/4/98; was 70001 */
52
53 #define MALLOC_FILL_VALUE 0x21
54 #define FREE_FILL_VALUE 0x89
55 #define MALLOCDUMPFILE "dbgdump.txt"
56 #define APPEND_TO_DUMPFILE 0 /* define as 0 if overwrite dbgdump.txt rather than append */
57 #define FREEING_MEMORY -1
58
59 #define AllowedToDeleteNULL Yes
60 /*
61 Option added 7/4/98 by DHM:
62 ---------------------------
63 This option checks for NULL in record_free() and doesn't update the records.
64
65 According to 2nd and 3rd edition Stroustrup, it's legal to "delete NULL", and it
66 has no effect (see e.g. 3rd Edition Stroustrup, p128 paragraph 2).
67 According to Appendix B of K&R, free NULL has no effect.
68 */
69
70 #if APPEND_TO_DUMPFILE
71 #define FILEPERM "a"
72 #else
73 #define FILEPERM "w"
74 #endif
75
76 #if !defined(_MSC_VER) /* not required for MS C since MS C has CRT debugging available */
77 #define OVERRUN_SIZEMIN 2
78 #define OVERRUN_SIZEMAX 128
79 #define OVERRUN_SIZEFACTOR 2 /* this is a shift down */
80
81 #define OVERRUN_SIZE(sz) ((sz)>OVERRUN_SIZEMAX<<OVERRUN_SIZEFACTOR ? OVERRUN_SIZEMAX : (sz)<OVERRUN_SIZEMIN<<OVERRUN_SIZEFACTOR ? OVERRUN_SIZEMIN : (sz)>>OVERRUN_SIZEFACTOR)
82 /* I just selected these at random - the 0 term is necessary for wrap around */
83 static unsigned char const overrun_code [] = { 0xef, 0x94, 0x56, 0x27, 0xf6, 0x76, 0x23, 0x43, 0 };
84
85 #else
86 #undef OVERRUN_SIZE
87 #endif
88
89
90
91 typedef struct{
92 unsigned long addr;
93 unsigned long size;
94 #if COPY_FILENAME
95 char filename[40];
96 #else
97 char const * filename; /* JH 30/5/97 - since __FILE__ generates a string in the executable, to which we get passed a pointer, we don't need to make another copy unnecessarily wasting 720K of space on PC !! */
98 #endif
99 unsigned long linenum;
100 }MALLOC_RECORD;
101
102 /* globals */
103 MALLOC_RECORD MallocRecord[MAXMALLOCS];
104 unsigned long TotalMemAllocated = 0;
105 unsigned long TotalMallocNum = 0;
106
107 /* extern function declarations */
108 extern int textprint(const char* string, ...);
109 extern void ExitSystem(void);
110 extern void WaitForReturn(void);
111
112 #define textprint2 textprint
113
114 /* function declarations */
115 #if COPY_FILENAME
116 void record_free(void *ptr, char string[], unsigned long lineno);
117 void *record_malloc(long size, char string[], unsigned long lineno);
118 static int AdjustMallocRecord(unsigned long addr, long size, char string[], unsigned long lineno);
119 #else /* new prototypes to take just pointers - dunno if it's really necessary */
120 void record_free(void *ptr, char const * string, unsigned long lineno);
121 void *record_malloc(long size, char const * string, unsigned long lineno);
122 static int AdjustMallocRecord(unsigned long addr, long size, char const * string, unsigned long lineno);
123 #endif
124 void DumpMallocInfo(int type);
125 static void InitMallocRecords(void);
126
127 /* function definitions */
128
129 #if COPY_FILENAME
record_malloc(long size,char string[],unsigned long lineno)130 void *record_malloc(long size, char string[], unsigned long lineno)
131 #else
132 void *record_malloc(long size, char const * string, unsigned long lineno)
133 #endif
134 {
135 #ifdef OVERRUN_SIZE
136 void *ptr = (void *)AllocMem((size_t) size + OVERRUN_SIZE(size));
137 #else
138 void *ptr = (void *)AllocMem((size_t) size);
139 #endif
140 if(ptr==NULL)
141 {
142 textprint2("\nMalloc Error! %d bytes attempted\n", size);
143 return((void *)NULL);
144 }
145
146 GLOBALASSERT(size>0); /* should be redundant cos picked up by above malloc attempt */
147
148 AdjustMallocRecord((long)ptr,size, string, lineno);
149
150 return(ptr);
151 }
152
153 #if COPY_FILENAME
record_free(void * ptr,char string[],unsigned long lineno)154 void record_free(void *ptr, char string[], unsigned long lineno)
155 #else
156 void record_free(void *ptr, char const * string, unsigned long lineno)
157 #endif
158 {
159 if(AdjustMallocRecord((long)ptr,FREEING_MEMORY, string, lineno))
160 DeallocMem((void *)ptr); /* free previously malloced ptr */
161 return;
162 }
163
164 #if COPY_FILENAME
AdjustMallocRecord(unsigned long addr,long size,char string[],unsigned long lineno)165 static int AdjustMallocRecord(unsigned long addr, long size, char string[], unsigned long lineno)
166 #else
167 static int AdjustMallocRecord(unsigned long addr, long size, char const * string, unsigned long lineno)
168 #endif
169 {
170
171 int Starti=addr % MAXMALLOCS;
172 int i=Starti;
173 char *ptr = (char *)addr;
174 static int no_record_init = 1;
175 MALLOC_RECORD *recordPtr,*StartRecordPtr;
176 recordPtr=StartRecordPtr=&MallocRecord[Starti];
177
178 if(no_record_init)
179 {
180 InitMallocRecords();
181 no_record_init = 0;
182 }
183
184
185 if(size==FREEING_MEMORY) /* must be freeing memory */
186 {
187 #if AllowedToDeleteNULL
188 if (NULL == addr)
189 {
190 return 1;
191 /*
192 ...so the record_free() function calls
193 DeallocMem(NULL), which ought to do nothing
194 (refer to citations in comment near the top of
195 this file)
196 */
197 }
198 #endif
199
200 GLOBALASSERT(addr); /* ensure not null addr */
201
202 while(i<MAXMALLOCS)
203 {
204 if(recordPtr->addr==addr)
205 {
206 TotalMallocNum--;
207
208 size = recordPtr->size;
209
210 while (size--)
211 {
212 *ptr++ = FREE_FILL_VALUE;
213 }
214 #ifdef OVERRUN_SIZE
215 {
216 char const * overrun_ptr = overrun_code;
217 int ov_cnt = OVERRUN_SIZE(recordPtr->size);
218 do /* a memcmp - dunno if its supported on all platforms */
219 {
220 if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */
221 if (*overrun_ptr++ != *ptr++)
222 {
223 textprint2("\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum);
224 GLOBALASSERT(!"OUT OF BOUNDS detected in FREE");
225 }
226 }
227 while (--ov_cnt);
228 }
229 #endif
230
231 recordPtr->addr = 0;
232 TotalMemAllocated -= recordPtr->size;
233 recordPtr->size = 0;
234 #if COPY_FILENAME
235 recordPtr->filename[0] = 0;
236 #else
237 recordPtr->filename = "";
238 #endif
239 recordPtr->linenum = 0;
240 break; /* exit while loop */
241 }
242 i++;
243 recordPtr++;
244 }
245 if(i==MAXMALLOCS)
246 {
247 i=0;
248 recordPtr=&MallocRecord[0];
249
250 while(i<Starti)
251 {
252 if(recordPtr->addr==addr)
253 {
254 TotalMallocNum--;
255
256 size = recordPtr->size;
257
258 while (size--)
259 {
260 *ptr++ = FREE_FILL_VALUE;
261 }
262 #ifdef OVERRUN_SIZE
263 {
264 char const * overrun_ptr = overrun_code;
265 int ov_cnt = OVERRUN_SIZE(recordPtr->size);
266 do /* a memcmp - dunno if its supported on all platforms */
267 {
268 if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */
269 if (*overrun_ptr++ != *ptr++)
270 {
271 textprint2("\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum);
272 GLOBALASSERT(!"OUT OF BOUNDS detected in FREE");
273 }
274 }
275 while (--ov_cnt);
276 }
277 #endif
278
279 recordPtr->addr = 0;
280 TotalMemAllocated -= recordPtr->size;
281 recordPtr->size = 0;
282 #if COPY_FILENAME
283 recordPtr->filename[0] = 0;
284 #else
285 recordPtr->filename = "";
286 #endif
287 recordPtr->linenum = 0;
288 break; /* exit while loop */
289 }
290 i++;
291 recordPtr++;
292 }
293 if(i>=Starti)
294 {
295 textprint2("\n\n\n\nFree Error! %s, line %d\n", string, (int)lineno);
296 GLOBALASSERT(0);
297 return(0);
298 }
299 }
300 }
301
302 else /* must be mallocing memory */
303 {
304 TotalMallocNum++;
305 GLOBALASSERT(TotalMallocNum<MAXMALLOCS); /* just increase MAXMALLOCS define above if this fires */
306
307
308 // RWH chack to see that this address isn't already in use
309 while(i<MAXMALLOCS)
310 {
311 if(recordPtr->addr==0)
312 {
313 recordPtr->addr = addr;
314 recordPtr->size = size;
315 TotalMemAllocated += size;
316 #if COPY_FILENAME
317 strcpy(recordPtr->filename, string);
318 #else
319 recordPtr->filename = string;
320 #endif
321 recordPtr->linenum = lineno;
322 while (size--)
323 {
324 *ptr++ = MALLOC_FILL_VALUE;
325 }
326 #ifdef OVERRUN_SIZE
327 {
328 char const * overrun_ptr = overrun_code;
329 int ov_cnt = OVERRUN_SIZE(recordPtr->size);
330 do /* a memcpy */
331 {
332 if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */
333 *ptr++ = *overrun_ptr++;
334 }
335 while (--ov_cnt);
336 }
337 #endif
338 break; /* exit while loop */
339 }
340 i++;
341 recordPtr++;
342 }
343 if(i>=MAXMALLOCS)
344 {
345 i=0;
346 recordPtr=&MallocRecord[0];
347 while(i<Starti)
348 {
349 if(recordPtr->addr==0)
350 {
351 recordPtr->addr = addr;
352 recordPtr->size = size;
353 TotalMemAllocated += size;
354 #if COPY_FILENAME
355 strcpy(recordPtr->filename, string);
356 #else
357 recordPtr->filename = string;
358 #endif
359 recordPtr->linenum = lineno;
360 while (size--)
361 {
362 *ptr++ = MALLOC_FILL_VALUE;
363 }
364 #ifdef OVERRUN_SIZE
365 {
366 char const * overrun_ptr = overrun_code;
367 int ov_cnt = OVERRUN_SIZE(recordPtr->size);
368 do /* a memcpy */
369 {
370 if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */
371 *ptr++ = *overrun_ptr++;
372 }
373 while (--ov_cnt);
374 }
375 #endif
376 break; /* exit while loop */
377 }
378 i++;
379 recordPtr++;
380 }
381 GLOBALASSERT(i<Starti); /* This should never fire - oh well */
382 }
383 }
384 return(1);
385
386 }
387
DumpMallocInfo(int type)388 void DumpMallocInfo(int type)
389 {
390 int i;
391
392 if(type==DUMPTOSCREEN)
393 {
394 for (i=0;i<MAXMALLOCS;i++ )
395 {
396 if(MallocRecord[i].addr!=0)
397 {
398 if (!(MallocRecord[i].linenum & CPPGLOBAL))
399 {
400 textprint2("\nmalloc: %d bytes,line %d in %s",
401 (int)MallocRecord[i].size, (int)MallocRecord[i].linenum, MallocRecord[i].filename);
402 }
403 else
404 {
405 textprint2("\nC++ global construct malloc: %d bytes,line %d in %s",
406 (int)MallocRecord[i].size, (int)(MallocRecord[i].linenum &~CPPGLOBAL), MallocRecord[i].filename);
407 }
408 }
409 }
410 textprint2("\n\n\n\nTotalMemAllocated: %d\nTotalMallocNum: %d",
411 (int)TotalMemAllocated, (int)TotalMallocNum);
412 WaitForReturn();
413 }
414 else if (type==DUMPTOFILE)
415 {
416 FILE *fp;
417
418 if( (fp = fopen(MALLOCDUMPFILE,FILEPERM))== (FILE *)NULL)
419 {
420 textprint2("\n\n\nfile open error %s", MALLOCDUMPFILE);
421 }
422 else
423 {
424 fprintf(fp,"\n\n\n\nOUTSTANDING MEMORY ALLOCATIONS\n\n");
425 for (i=0;i<MAXMALLOCS;i++ )
426 {
427 if(MallocRecord[i].addr!=0)
428 {
429 if (!(MallocRecord[i].linenum & CPPGLOBAL))
430 {
431 fprintf(fp,"\nmalloc: %d bytes,line %d in %s",
432 (int)MallocRecord[i].size, (int)MallocRecord[i].linenum, MallocRecord[i].filename);
433 }
434 else
435 {
436 fprintf(fp,"\nC++ global construct malloc: %d bytes,line %d in %s",
437 (int)MallocRecord[i].size, (int)(MallocRecord[i].linenum &~CPPGLOBAL), MallocRecord[i].filename);
438 }
439 }
440 }
441 fprintf(fp,"\n\nTotalMemAllocated: %d\nTotalMallocNum: %d\n",
442 (int)TotalMemAllocated, (int)TotalMallocNum);
443
444 fclose(fp);
445 }
446
447 }
448
449 }
450
DumpBoundsCheckInfo(int type)451 void DumpBoundsCheckInfo(int type)
452 {
453 #ifdef OVERRUN_SIZE
454 int i;
455
456 if(type==DUMPTOSCREEN)
457 {
458 int bc_errcnt = 0;
459 for (i=0;i<MAXMALLOCS;i++ )
460 {
461 if(MallocRecord[i].addr!=0)
462 {
463 MALLOC_RECORD *recordPtr = &MallocRecord[i];
464
465 unsigned char const * overrun_ptr = overrun_code;
466 unsigned char const * ptr = (unsigned char const *)recordPtr->addr + recordPtr->size;
467 int ov_cnt = OVERRUN_SIZE(recordPtr->size);
468 do /* a memcmp - dunno if its supported on all platforms */
469 {
470 if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */
471 if (*overrun_ptr++ != *ptr++)
472 {
473 textprint2("\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum);
474 ++bc_errcnt;
475 break;
476 }
477 }
478 while (--ov_cnt);
479 }
480 }
481 if (bc_errcnt)
482 WaitForReturn();
483 else
484 textprint2("No bounds errors detected\n");
485
486 }
487 else if (type==DUMPTOFILE)
488 {
489 FILE *fp;
490
491 if( (fp = fopen(MALLOCDUMPFILE,FILEPERM))== (FILE *)NULL)
492 {
493 textprint2("\n\n\nfile open error %s", MALLOCDUMPFILE);
494 }
495 else
496 {
497 fprintf(fp,"\n\n\n\nBOUNDS CHECK PROBLEMS\n\n");
498 for (i=0;i<MAXMALLOCS;i++ )
499 {
500 if(MallocRecord[i].addr!=0)
501 {
502 MALLOC_RECORD *recordPtr = &MallocRecord[i];
503
504 unsigned char const * overrun_ptr = overrun_code;
505 unsigned char const * ptr = (unsigned char const *)recordPtr->addr + recordPtr->size;
506 int ov_cnt = OVERRUN_SIZE(recordPtr->size);
507 do /* a memcmp - dunno if its supported on all platforms */
508 {
509 if (!*overrun_ptr) overrun_ptr = overrun_code; /* repeat */
510 if (*overrun_ptr++ != *ptr++)
511 {
512 fprintf(fp,"\nOut of Bounds!\n%lu bytes allocated to %p\nat %s, line %lu\n", recordPtr->size,(void *)recordPtr->addr,recordPtr->filename,recordPtr->linenum);
513 break;
514 }
515 }
516 while (--ov_cnt);
517 }
518 }
519 fprintf(fp,"\n\nTotalMemAllocated: %d\nTotalMallocNum: %d\n",
520 (int)TotalMemAllocated, (int)TotalMallocNum);
521
522 fclose(fp);
523 }
524
525 }
526
527 #endif
528 }
529
InitMallocRecords(void)530 void InitMallocRecords(void)
531 {
532 int i;
533 MALLOC_RECORD *recordPtr = MallocRecord;
534
535 for (i=0;i<MAXMALLOCS;i++,recordPtr++)
536 {
537 recordPtr->addr = 0;
538 recordPtr->size = 0;
539 #if COPY_FILENAME
540 recordPtr->filename[0] = 0;
541 #else
542 recordPtr->filename = "";
543 #endif
544 recordPtr->linenum = 0;
545 }
546
547 return;
548 }
549
550 #else
551 void DumpMallocInfo(int type);
DumpMallocInfo(int type)552 void DumpMallocInfo(int type)
553 {
554 /* empty if not debugging */
555 }
DumpBoundsCheckInfo(int type)556 void DumpBoundsCheckInfo(int type)
557 {}
558 #endif
559