G:/ScriptBasic/source/testalloc.c

Go to the documentation of this file.
00001 /* 
00002 FILE:   testalloc.c
00003 HEADER: testalloc.h
00004 
00005 --GNU LGPL
00006 This library is free software; you can redistribute it and/or
00007 modify it under the terms of the GNU Lesser General Public
00008 License as published by the Free Software Foundation; either
00009 version 2.1 of the License, or (at your option) any later version.
00010 
00011 This library is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014 Lesser General Public License for more details.
00015 
00016 You should have received a copy of the GNU Lesser General Public
00017 License along with this library; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 TO_HEADER:
00021 
00022 typedef struct _tAllocUnit {
00023   long n;                       // the size of the chunk
00024   long id;                      // unique id of the chunk
00025   struct _tAllocUnit *next;     // link to the next unit
00026   struct _tAllocUnit *prev;     // the previous unit
00027   unsigned char memory[1];      // one or more bytes reserved
00028   } tAllocUnit, *ptAllocUnit;
00029 // Note that the last member before 'memory' is a pointer which should
00030 // provide sufficient alignment for 'memory' on 32bit and on 64bit systems as well
00031 
00032 typedef struct _TAlloc {
00033   void * (*memory_allocating_function)(size_t);
00034   void (*memory_releasing_function)(void *);
00035 
00036   ptAllocUnit FirstUnit;
00037   } TAlloc, *pTAlloc;
00038 
00039 */
00040 #ifdef _DEBUG
00041 #include <stdio.h>
00042 #include <stdlib.h>
00043 
00044 /* for offsetof */
00045 #include <stddef.h>
00046 #include <memory.h>
00047 #include "myalloc.h"
00048 #include "testalloc.h"
00049 
00050 #define CHECKBYTEVALUE 0xFF
00051 
00052 /*POD
00053 =H Memory allocation test module
00054 
00055 This module contains two major functions T<testa_Alloc> and T<testa_Free>. These modules keep
00056 track of the allocated memory segments using a similar data structures as the functions
00057 implemented in T<myalloc.c>
00058 
00059 The functions in this module use a statically initialized memory segment and ALL memory
00060 allocation is assigned to this segment.
00061 
00062 The functions are to test an application so that it does not leak memory. These functions
00063 should NOT be used in a final released version. The functins should be used to track memory
00064 leak.
00065 
00066 CUT*/
00067 
00068 static pTAlloc pMemorySegment;
00069 static long cbAllocBytes;
00070 static long cbID;
00071 
00072 /*POD
00073 =section InitSegment
00074 =H Initialize THE segement
00075 
00076 Call this function before calling any other functions once in a process.
00077 /*FUNCTION*/
00078 void testa_InitSegment(
00079   ){
00080 /*noverbatim
00081 CUT*/
00082   pMemorySegment = (pTAlloc)malloc(sizeof(TAlloc));
00083   if( pMemorySegment == NULL )exit(1);
00084   pMemorySegment->memory_allocating_function = malloc;
00085   pMemorySegment->memory_releasing_function = free;
00086   pMemorySegment->FirstUnit = NULL;
00087   cbAllocBytes = 0L;
00088   cbID = 0L;
00089   return;
00090   }
00091 
00092 /*POD
00093 =section Assert0x80
00094 =H Assert that no segment overwrote the 0x80 at the last extra byte
00095 
00096 This function can be called from several point of the program to check if
00097 some code overwrote the allocated buffer at least one byte. The test allocation
00098 function allocates one extra byte at the end of the memory block allocated and
00099 fills it with the code 0x80. This extra byte should remain intact at all times
00100 during execution.
00101 
00102 The problem is that this is realized after the error. To localize the bug in the code
00103 try to have identical runs. The local variable 'counter' counts each call of the
00104 function and it prints out when the error is catched. Edit the line
00105 
00106 =verbatim
00107   if( counter == 123 ){
00108 =noverbatim
00109 
00110 to have the number printed. After that set a debugger breakpoint on the T<printf>
00111 statement on the next line and during the next identical run you can catch the
00112 last time the memory was NOT corrupted. Continue from there step by step.
00113 
00114 /*FUNCTION*/
00115 void testa_Assert0x80(
00116   ){
00117 /*noverbatim
00118 CUT*/
00119   long k;
00120   ptAllocUnit pAllU;
00121   static unsigned long counter=0;
00122 
00123   counter++;
00124   /* EDIT HERE TO DEBUG */
00125   if( counter == 123 ){
00126     printf("");
00127     }
00128   if( cbAllocBytes == 0L )return;
00129   k = 0;
00130   pAllU = pMemorySegment->FirstUnit;
00131   while( pAllU ){
00132     if( pAllU->memory[pAllU->n] != CHECKBYTEVALUE ){
00133       printf("Segment altered: %d(%d)",pAllU->n,pAllU->id);
00134       printf("Segment content: %s\n",pAllU->memory);
00135       printf("Counter to catch is: %d\n",counter-1);
00136       k = 1;
00137       }
00138     pAllU = pAllU->next;
00139     }
00140   if( k ){
00141     printf("\n");
00142 
00143     printf("Press any key to continue...\n");
00144     getchar();
00145     exit(1276);
00146     }
00147   }
00148 
00149 /*POD
00150 =section Alloc
00151 =H Allocate memory from a segment
00152 
00153 Use this memory to allocate a memory piece from THE segment.
00154 
00155 /*FUNCTION*/
00156 void *testa_Alloc(size_t n
00157   ){
00158 /*noverbatim
00159 
00160 The argument is the size to be allocated.
00161 
00162 If the memory allocation fails the function returns T<NULL>.
00163 
00164 During debug each allocated memory piece gets an ID. This ID is printed
00165 to the standard error when some error is identified by this module. Edit the
00166 line 
00167 
00168 =verbatim
00169   if( pAllU->id == 26 ){
00170 =noverbatim
00171 
00172 to compare the id to the number of the segment that was reported leaking or corrupted and
00173 set the breakpoint on the next line T<printf> during the next identical run. From there
00174 you can trace your code step by step identifying the allocation that was not correct.
00175 CUT*/
00176   ptAllocUnit pAllU;
00177 
00178   if( n == 0 )
00179     return NULL;
00180 
00181   testa_Assert0x80();
00182 
00183   cbAllocBytes += n;
00184 
00185   /* allocate one extra byte to detect buffer overflow. The extra one byte is set to 0x80 value and should not
00186      change when the memory is released */
00187   pAllU = (ptAllocUnit)pMemorySegment->memory_allocating_function( n + offsetof(struct _tAllocUnit,memory) +1 );
00188 
00189   if( pAllU == NULL )return NULL;
00190 
00191   memset(pAllU,CHECKBYTEVALUE,n + offsetof(struct _tAllocUnit,memory) +1);
00192 
00193   pAllU->n = n;
00194   pAllU->id = cbID++;
00195   pAllU->prev = NULL;
00196   pAllU->next = pMemorySegment->FirstUnit;
00197   if( pMemorySegment->FirstUnit )pMemorySegment->FirstUnit->prev = pAllU;
00198   pMemorySegment->FirstUnit = pAllU;
00199 
00200   /* EDIT HERE TO DEBUG */
00201   if( pAllU->id == 26 ){
00202     printf("");
00203     }
00204   /* return a void* pointer that points to the allocated memory after the header */
00205   return (void *)( pAllU->memory );
00206   }
00207 
00208 /*POD
00209 =section Free
00210 =H Release memory
00211 
00212 You should call this function whenever you want to release a single piece of memory
00213 allocated from THE segment.
00214 
00215 /*FUNCTION*/
00216 void testa_Free(void *pMem
00217   ){
00218 /*noverbatim
00219 CUT*/
00220   ptAllocUnit pAllU;
00221 
00222   if( pMemorySegment == NULL )return;
00223   pAllU = (ptAllocUnit)( ((unsigned char *)pMem) - offsetof(struct _tAllocUnit,memory) ); 
00224 
00225   /* check here that the extra 0x80 byte remained intact */
00226   if( pAllU->memory[pAllU->n] != CHECKBYTEVALUE ){
00227     printf("Segment altered: %d(%d)",pAllU->n,pAllU->id);
00228     printf("Segment content: %s\n",pAllU->memory);
00229     printf("Press any key to continue...\n");
00230     getchar();
00231     exit(1276);
00232     }
00233 
00234   if( pAllU->next )
00235     pAllU->next->prev = pAllU->prev;
00236   if( pAllU->prev )
00237     pAllU->prev->next = pAllU->next;
00238   else
00239     pMemorySegment->FirstUnit = pAllU->next;
00240   cbAllocBytes -= pAllU->n;
00241 
00242   pMemorySegment->memory_releasing_function(pAllU);
00243   }
00244 
00245 
00246 /*POD
00247 =section Check
00248 =H Check memory
00249 
00250 This function does nothing but checks that a memory segment is valid,
00251 in other words it checks that the memory segment was allocated by the
00252 program and the pointer is valid.
00253 
00254 /*FUNCTION*/
00255 void testa_Check(void *pMem
00256   ){
00257 /*noverbatim
00258 CUT*/
00259   ptAllocUnit pAllU,pAllUi;
00260 
00261   if( pMemorySegment == NULL ){
00262     printf("pMem %p can not be a valid segment as there are not segments\n",pMem);
00263     exit(1277);
00264     }
00265   pAllU = (ptAllocUnit)( ((unsigned char *)pMem) - offsetof(struct _tAllocUnit,memory) ); 
00266 
00267   pAllUi = pMemorySegment->FirstUnit;
00268   while( pAllUi ){
00269     if( pAllUi == pAllU ){
00270       if( pAllU->memory[pAllU->n] != CHECKBYTEVALUE ){
00271         printf("Segment altered: %d(%d)",pAllU->n,pAllU->id);
00272         printf("Segment content: %s\n",pAllU->memory);
00273         exit(1278);
00274         }
00275       return;
00276       }
00277     pAllUi = pAllUi->next;
00278     }
00279 
00280   printf("The segment %p is not on the list\n",pMem);
00281   exit(1279);
00282   }
00283 
00284 /*POD
00285 =section AssertLeak
00286 =H Assert that there is no memory leak
00287 
00288 Returns if all memory was relased. Otherwise it prints the information of non-released
00289 memory chunks, and then exits with code 1276.
00290 
00291 Then you can debug the application based on the reported memory chunk size and id to hunt
00292 where you allocate memory and not release.
00293 /*FUNCTION*/
00294 void testa_AssertLeak(
00295   ){
00296 /*noverbatim
00297 CUT*/
00298   long k;
00299   ptAllocUnit pAllU;
00300 
00301   if( cbAllocBytes == 0L )return;
00302 
00303   printf("There is a memory leak of %ld bytes\n",cbAllocBytes);
00304   k = 0;
00305   pAllU = pMemorySegment->FirstUnit;
00306   while( pAllU ){
00307     pAllU = pAllU->next;
00308     k++;
00309     }
00310   printf("This memory is lost in %d chunks\n",k);
00311   pAllU = pMemorySegment->FirstUnit;
00312   while( pAllU ){
00313     printf("%d(%d)",pAllU->n,pAllU->id);
00314     pAllU = pAllU->next;
00315     if( pAllU )printf(",");
00316     }
00317   printf("\n");
00318 
00319   printf("Press any key to continue...\n");
00320   getchar();
00321   exit(1276);
00322   }
00323 
00324 /*POD
00325 =section ReportLeak
00326 =H Reports memory leak
00327 
00328 
00329 /*FUNCTION*/
00330 unsigned long testa_ReportLeak(
00331   ){
00332 /*noverbatim
00333 CUT*/
00334   long k;
00335   ptAllocUnit pAllU;
00336 
00337   if( cbAllocBytes == 0L ){
00338     printf("No memory leak\n");
00339     return 0;
00340     }
00341 
00342   printf("There is a memory leak of %ld bytes\n",cbAllocBytes);
00343   k = 0;
00344   pAllU = pMemorySegment->FirstUnit;
00345   while( pAllU ){
00346     pAllU = pAllU->next;
00347     k++;
00348     }
00349   printf("This memory is lost in %d chunks\n",k);
00350   pAllU = pMemorySegment->FirstUnit;
00351 /*  while( pAllU ){
00352     printf("%d(%d)",pAllU->n,pAllU->id);
00353     pAllU = pAllU->next;
00354     if( pAllU )printf(",");
00355     }*/
00356   printf("\n");
00357   return cbAllocBytes;
00358   }
00359 #endif

Generated on Sun Mar 12 23:56:31 2006 for ScriptBasic by  doxygen 1.4.6-NO