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