• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

src/Debug.cpp

00001 /*
00002  *   This file is part of the Standard Portable Library (SPL).
00003  *
00004  *   SPL is free software: you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation, either version 3 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   SPL is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License
00015  *   along with SPL.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 #if defined(DEBUG) || defined(_DEBUG)
00018 
00019 #include <stdio.h>
00020 #include <stdlib.h>
00021 
00022 #if defined(_WIN32) || defined(_WIN64)
00023 #include <spl/configwin32.h>
00024 #else
00025 #include <spl/autoconf/config.h>
00026 #endif
00027 
00028 #ifdef HAVE_SYS_ERRNO_H
00029 #include <sys/errno.h>
00030 #endif
00031 #ifdef HAVE_ERRNO_H
00032 #include <errno.h>
00033 #endif
00034 #ifdef HAVE_ASSERT_H
00035 #include <assert.h>
00036 #endif
00037 #ifdef HAVE_MEMORY_H
00038 #include <memory.h>
00039 #endif
00040 #include <spl/Debug.h>
00041 
00042 #if defined(_WIN32) || defined(_WIN64)
00043 #include <crtdbg.h>
00044 #else
00045 #define _CrtDumpMemoryLeaks() false
00046 #define _CrtCheckMemory() true
00047 #define _CrtIsValidHeapPointer() true
00048 #define _CrtIsValidPointer(a,b,c) true
00049 #endif
00050 
00051 #undef malloc
00052 #undef free
00053 
00054 #include <string.h>
00055 
00056 #if defined(_WIN32) || defined(_WIN64)
00057 #include <crtdbg.h>
00058 #endif
00059 
00060 #define DEBUG_MEM_FILL (char)0xCA
00061 
00062 static volatile int mallocsize = 0;
00063 static volatile int midnum = 0;
00064 static volatile bool isshutdown = false;
00065 static volatile bool mutexinit = false;
00066 static bool _usemutex = false;
00067 
00068 #if defined(_WIN32) || defined(_WIN64)
00069 #       define _WINSOCKAPI_
00070 #       include <spl/cleanwindows.h>
00071 #endif
00072 #if defined(HAVE_SPTHREAD_H) || defined(_TANDEM)
00073 #       define _tal
00074 #       define _extensible 
00075 #       include <spthread.h>
00076 #endif
00077 #if defined(HAVE_PTHREAD_H) && !defined(_TANDEM)
00078 #       include <pthread.h>
00079 #endif
00080 #if defined(_WIN32) || defined(_WIN64)
00081         HANDLE mutex;
00082 #else 
00083         pthread_mutex_t mutex;
00084         int mutex_recur_hack = 0;
00085 #endif 
00086 
00087 #include <spl/BigInteger.h>
00088 
00089 struct allocblock
00090 {
00091         int16 majic;
00092         int id;
00093         char checkbit;
00094         char checkbit2;
00095         bool isarray;   /* new something[] reservse the first 4 bytes */
00096         int size;
00097         const char *filename;
00098         int lineno;
00099         struct allocblock *next;
00100         struct allocblock *prev;
00101         char data[4];
00102 };
00103 
00104 struct memcheckpoint
00105 {
00106         int count;
00107         int size;
00108 };
00109 
00110 #define ALLOCBLOC_MAJIC 21845
00111 
00112 static struct allocblock m_abhead = { ALLOCBLOC_MAJIC, -1, 0, DEBUG_MEM_FILL, 0, 0, NULL, 0, NULL, NULL, DEBUG_MEM_FILL, DEBUG_MEM_FILL, DEBUG_MEM_FILL, DEBUG_MEM_FILL };
00113 static struct allocblock m_abtail = { ALLOCBLOC_MAJIC, -1, 0, DEBUG_MEM_FILL, 0, 0, NULL, 0, NULL, NULL, DEBUG_MEM_FILL, DEBUG_MEM_FILL, DEBUG_MEM_FILL, DEBUG_MEM_FILL };
00114 
00115 static struct memcheckpoint m_memcheckpoint;
00116 
00117 #define validateBlock(a) (a != NULL && (a->checkbit2 == DEBUG_MEM_FILL &&       \
00118                 a->majic == ALLOCBLOC_MAJIC &&                                                                          \
00119                 a->data[a->size] == DEBUG_MEM_FILL &&                                                           \
00120                 a->data[a->size+1] == DEBUG_MEM_FILL &&                                                         \
00121                 a->data[a->size+2] == DEBUG_MEM_FILL &&                                                         \
00122                 a->data[a->size+3] == DEBUG_MEM_FILL &&                                                         \
00123                 (a->checkbit == 0 || a->checkbit == 1)))
00124 
00125 /*
00126 inline int validateBlock( const struct allocblock *bp )
00127 {
00128         ASSERT( bp != NULL );
00129         // Fails on WINE
00130 #ifndef WINE
00131         ASSERT( _CrtIsValidPointer( bp, sizeof(struct allocblock) + bp->size, 1 ) );
00132         ASSERT( _CrtIsValidPointer( bp->data, 1, 1 ) );
00133 #endif
00134         return bp->checkbit2 == DEBUG_MEM_FILL &&
00135                 bp->data[bp->size] == DEBUG_MEM_FILL &&
00136                 bp->data[bp->size+1] == DEBUG_MEM_FILL &&
00137                 bp->data[bp->size+2] == DEBUG_MEM_FILL &&
00138                 bp->data[bp->size+3] == DEBUG_MEM_FILL &&
00139                 (bp->checkbit == 0 || bp->checkbit == 1);
00140 }*/
00141 
00142 void _debugEnableHeapLocking()
00143 {
00144         _usemutex = true;
00145 }
00146 
00147 void _debugLockHeap()
00148 {
00149         if ( mutexinit )
00150         {
00151 #ifdef _WINDOWS
00152                 WaitForSingleObject(mutex, INFINITE);
00153 #else
00154                 int ret = pthread_mutex_lock( &mutex );
00155                 if ( ret == EDEADLK )
00156                 {
00157                         // recursive lock not supported
00158                         mutex_recur_hack++;
00159                 }
00160                 else
00161                 {
00162                         assert(ret == 0);
00163                 }
00164 #endif
00165         }
00166 }
00167 
00168 void _debugUnlockHeap()
00169 {
00170         if ( mutexinit )
00171         {
00172 #ifdef _WIN32
00173                 ReleaseMutex(mutex);
00174 #else
00175                 if ( 0 != mutex_recur_hack )
00176                 {
00177                         assert( mutex_recur_hack > 0 );
00178                         mutex_recur_hack--;
00179                 }
00180                 else
00181                 {
00182                         assert( 0 == pthread_mutex_unlock( &mutex ) );
00183                 }
00184 #endif
00185         }
00186 }
00187 
00188 void _debugFreeHeap()
00189 {
00190         BigInteger::DebugClearStatics();
00191 
00192         struct allocblock *bp = m_abhead.next;
00193         if ( NULL == bp )
00194         {
00195                 return;
00196         }
00197         while ( &m_abtail != bp )
00198         {
00199                 assert(bp->next->prev == bp);
00200                 validateBlock( bp );
00201                 bp = bp->next;
00202                 free( bp->prev );
00203         }
00204 
00205         assert(m_abhead.next == &m_abtail);
00206         assert(m_abhead.prev == NULL);
00207         assert(m_abtail.prev == &m_abhead);
00208         assert(m_abtail.next == NULL);
00209 }
00210 
00211 void _debugTearDown(bool reallyfree)
00212 {
00213         BigInteger::DebugClearStatics();
00214 
00215         struct allocblock *bp = m_abhead.next;
00216 
00217         while( bp != NULL && &m_abtail != bp )
00218         {
00219                 struct allocblock *next;
00220                 validateBlock( bp );
00221                 next = bp->next;
00222                 if ( reallyfree )
00223                 {
00224                         free( bp );
00225                 }
00226                 bp = next;
00227         }
00228 
00229         if ( mutexinit )
00230         {
00231 #ifdef _WIN32
00232                 CloseHandle(mutex);
00233 #else
00234                 pthread_mutex_destroy( &mutex );
00235 #endif
00236                 mutexinit = false;
00237         }
00238         if ( reallyfree )
00239         {
00240                 assert(m_abhead.next == &m_abtail);
00241                 assert(m_abhead.prev == NULL);
00242                 assert(m_abtail.prev == &m_abhead);
00243                 assert(m_abtail.next == NULL);
00244         }
00245         isshutdown = true;
00246 }
00247 
00248 void *_debugMalloc( const int len, const char *filename, const int lineno, const bool isarray  )
00249 {
00250         struct allocblock *ab;
00251 
00252         assert( ! isshutdown );
00253         assert( len > 0 );
00254 
00255         if ( NULL == m_abhead.next )
00256         {
00257                 m_abhead.next = &m_abtail;
00258                 m_abtail.prev = &m_abhead;
00259                 if ( _usemutex && !mutexinit )
00260                 {
00261 #               ifdef _WINDOWS
00262                         mutex = CreateMutex(NULL, FALSE, NULL);
00263 #               else
00264                         assert( 0 == pthread_mutex_init( &mutex, NULL ) );
00265 #               endif
00266                         mutexinit = true;
00267                 }
00268         }
00269         _debugLockHeap( );
00270 
00271         mallocsize += sizeof( struct allocblock ) + len;
00272 
00273         ab = (struct allocblock *)malloc( sizeof( struct allocblock ) + len );
00274         if( NULL == ab )
00275         {
00276                 assert(false);
00277         }
00278 
00279         ab->majic = ALLOCBLOC_MAJIC;
00280         ab->id = midnum++;
00281         //if ( ab->id == 1937 )
00282         //{
00283         //      printf("hi\n");
00284         //}
00285         ab->data[len] = DEBUG_MEM_FILL;
00286         ab->data[len+1] = DEBUG_MEM_FILL;
00287         ab->data[len+2] = DEBUG_MEM_FILL;
00288         ab->data[len+3] = DEBUG_MEM_FILL;
00289         ab->filename = filename;
00290         ab->lineno = lineno;
00291         ab->size = len;
00292         ab->checkbit = 0;
00293         ab->checkbit2 = DEBUG_MEM_FILL;
00294         ab->isarray = isarray;
00295         ab->next = m_abhead.next;
00296         ab->next->prev = ab;
00297         ab->prev = &m_abhead;
00298         m_abhead.next = ab;
00299 
00300         validateBlock( ab );
00301 
00302         _debugUnlockHeap( );
00303         return ab->data;
00304 }
00305 
00306 static struct allocblock *_debugScanForBlock( const void *vp )
00307 {
00308         struct allocblock *bp;
00309         if ( 0 == vp || NULL == vp )
00310         {
00311                 return NULL;
00312         }
00313 
00314         bp = (struct allocblock *)(((byte *)vp) - (sizeof(struct allocblock)-4));
00315         if ( bp->majic == ALLOCBLOC_MAJIC )
00316         {
00317                 if(!validateBlock(bp))
00318                 {
00319                         assert(false);
00320                 }
00321                 return bp;
00322         }
00323 
00324         bp = m_abhead.next;
00325         while ( bp != &m_abtail )
00326         {
00327                 if ( vp == bp->data )
00328                 {
00329                         return bp;
00330                 }
00331                 if ( vp > bp->data )
00332                 {
00333                         if (vp < (void *)(bp->data + bp->size))
00334                         {
00335                                 return bp;
00336                         }
00337                 }
00338                 bp = bp->next;
00339         }
00340         return NULL;
00341 }
00342 
00343 struct allocblock *findBlock( const void *vp )
00344 {
00345         struct allocblock *bp = _debugScanForBlock( vp );
00346 
00347         if ( bp == NULL )
00348         {
00349                 bp = m_abhead.next;
00350                 while ( NULL != bp )
00351                 {
00352                         assert( bp->checkbit2 == DEBUG_MEM_FILL );
00353                         if ( vp >= (void *)bp->data && vp < (void *)(bp->data + bp->size) )
00354                         {
00355                                 //*offsetSize = bp->size - (int)((char *)vp - (char*)bp->data);
00356                                 return bp;
00357                         }
00358                         bp = bp->next;
00359                 }
00360         }
00361         else
00362         {
00363                 return bp;
00364         }
00365         return NULL;
00366 }
00367 
00368 void _debugFree( void *vp )
00369 {
00370         struct allocblock *bp = NULL;
00371 
00372         if ( isshutdown )
00373         {
00374                 /*free( (byte *)vp - sizeof(struct allocblock) );*/
00375                 return;
00376         }
00377 
00378         assert(NULL != vp);
00379 
00380         DEBUG_VALIDATE();
00381         _debugLockHeap(  );
00382 
00383         bp = _debugScanForBlock( vp );
00384         if ( NULL == bp )
00385         {
00386                 if ( NULL != (bp = _debugScanForBlock( ((byte *)vp)-4 )) )
00387                 {
00388                         //ASSERT( bp->isarray );
00389 #ifndef WINE
00390                         assert( _CrtIsValidPointer( ((byte *)vp)-4, 1, 1 ) );
00391 #endif
00392                 }
00393                 if ( NULL != (bp = _debugScanForBlock( ((byte *)vp)-8 )) )
00394                 {
00395                         //assert( bp->isarray );
00396 #ifndef WINE
00397                         assert( _CrtIsValidPointer( ((byte *)vp)-8, 1, 1 ) );
00398 #endif
00399                 }
00400         }
00401         else
00402         {
00403                 // Fails on WINE
00404 #ifndef WINE
00405                 assert( _CrtIsValidPointer( vp, 1, 1 ) );
00406 #endif
00407         }
00408         assert( -1 == m_abhead.id && 0 == m_abhead.checkbit );
00409         assert( NULL != bp );
00410         // fails on WINE
00411 #ifndef WINE
00412         assert( _CrtIsValidPointer( bp, sizeof(struct allocblock), 1 ) );
00413 #endif
00414         assert( bp->prev->next == bp );
00415         bp->next->prev = bp->prev;
00416         bp->prev->next = bp->next;
00417         
00418         assert( validateBlock( bp ) );
00419         assert ( bp->prev != NULL );
00420         assert( (&m_abhead == bp->prev)?1:validateBlock( bp->prev ) );
00421         assert( validateBlock( bp->next ) );
00422 
00423         assert( validateBlock( bp ) );
00424         assert( &m_abhead != bp );
00425 
00426         memset( bp, DEBUG_MEM_FILL, sizeof(struct allocblock) + bp->size );
00427         // Fails on WINE
00428 #ifndef WINE
00429         assert( _CrtIsValidPointer( bp, sizeof(struct allocblock), 1 ) );
00430 #endif
00431         mallocsize -= bp->size;
00432 
00433         _debugUnlockHeap(  );
00434         DEBUG_VALIDATE();
00435         free( bp );
00436         DEBUG_VALIDATE();
00437 }
00438 
00439 void _debugAssert( const int cond, const char *filename, const int lineno )
00440 {
00441         if ( cond == 0 )
00442         {
00443 #ifndef DEBUG
00444                 assert( cond );
00445 #else
00446                 printf( "assertion failed in %s on line %d.\n", filename, lineno );
00447                 exit(20);
00448 #endif
00449         }
00450 }
00451 
00452 int _debugCheckPtr( const void *ptr )
00453 {
00454         struct allocblock *bp;
00455         bool ret;
00456         if ( isshutdown )
00457         {
00458                 return 1;
00459         }
00460         _debugLockHeap(  );
00461 
00462         bp = findBlock( ptr );
00463         if ( NULL == bp )
00464         {
00465                 if ( NULL != (bp = findBlock( ((byte *)ptr)-4 )) )
00466                 {
00467                         if ( ! bp->isarray )
00468                         {
00469                                 _debugUnlockHeap(  );
00470                                 /*printf("pointer found, but should be array for padding?\n");*/
00471                                 return 0;
00472                         }
00473                 }
00474                 if ( NULL != (bp = findBlock( ((byte *)ptr)-8 )) )
00475                 {
00476                         // Also happens on sub classes with abstract (virtual) base class
00477                         //if ( ! bp->isarray )
00478                         //{
00479                         //      debugUnlockHeap(  );
00480                                 /*printf("pointer found, but should be array for padding?\n");*/
00481                         //      return 0;
00482                         //}
00483                 }
00484                 else
00485                 {
00486                         _debugUnlockHeap(  );
00487                         /*printf("pointer not found.\n");*/
00488                         return 0;
00489                 }
00490                 // Fails on WINE
00491 #ifndef WINE
00492                 assert( _CrtIsValidPointer( ((byte *)ptr)-4, 1, 1 ) );
00493 #endif
00494         }
00495         else
00496         {
00497                 // Fails on WINE
00498 #ifndef WINE
00499                 assert( _CrtIsValidPointer( ptr, 1, 1 ) );
00500 #endif
00501         }
00502         assert( NULL != bp );
00503         ret = validateBlock( bp );
00504         _debugUnlockHeap(  );
00505         return ret;
00506 }
00507 
00508 int _debugCheckBlock( const void *ptr, const int size )
00509 {
00510         struct allocblock *bp;
00511         if ( isshutdown )
00512         {
00513                 return 1;
00514         }
00515         assert( NULL != ptr );
00516 
00517         _debugLockHeap(  );
00518 
00519         bp = _debugScanForBlock( ptr );
00520         if( bp == NULL )
00521         {
00522                 const int vpsize = sizeof(void *);
00523                 
00524                 /* check for compiler padding, typical for arrays */
00525                 if ( NULL == (bp = _debugScanForBlock( ((byte *)ptr)-vpsize ) ) ) 
00526                 {
00527                         _debugUnlockHeap(  );
00528                         return 0;
00529                 }
00530                 /* This also happens for abstract base classes w/virtual members */
00531                 /*if ( ! bp->isarray )
00532                 {
00533                         debugUnlockHeap(  );
00534                         return 0;
00535                 }*/
00536 
00537                 // Fails on WINE
00538 #ifndef WINE
00539                 assert( _CrtIsValidPointer( ((byte *)ptr)-vpsize, 1, 1 ) );
00540 #endif
00541         }
00542         else
00543         {
00544                 // Fails on WINE
00545 #ifndef WINE
00546                 assert( _CrtIsValidPointer( ptr, 1, 1 ) );
00547 #endif
00548         }
00549         if ( NULL == bp )
00550         {
00551                 _debugUnlockHeap(  );
00552                 return 0;
00553         }
00554         if ( bp->size != size )
00555         {
00556                 if ( bp->size < size )
00557                 {
00558                         _debugUnlockHeap(  );
00559                         return 0;
00560                 }
00561         }
00562         assert( validateBlock( bp ));
00563         _debugUnlockHeap(  );
00564         return bp->size >= size;
00565 }
00566 
00567 void _debugClearMemCheckPoints()
00568 {
00569         struct allocblock *bp;
00570 
00571         if ( isshutdown )
00572         {
00573                 return;
00574         }
00575 
00576         _debugLockHeap(  );
00577 
00578         bp = m_abhead.next;
00579         while ( NULL != bp )
00580         {
00581                 assert( validateBlock( bp ) );
00582                 if ( bp != NULL )
00583                 {
00584                         bp->checkbit = 0;
00585                 }
00586                 bp = bp->next;
00587         }
00588 
00589         BigInteger::CheckMemStatics();
00590 
00591         _debugUnlockHeap(  );
00592 }
00593 
00594 void _debugNoteMemBlock( const void *vp )
00595 {
00596         struct allocblock *bp;
00597         
00598         if ( isshutdown )
00599         {
00600                 return;
00601         }       
00602         _debugLockHeap(  );
00603 
00604         bp = findBlock( vp );
00605         // Fails on WINE
00606 #ifndef WINE
00607         assert( _CrtIsValidPointer( vp, 1, 1 ) );
00608 #endif
00609         if ( NULL == bp )
00610         {
00611                 if ( NULL != (bp = findBlock( ((byte *)vp)-4 )) )
00612                 {
00613                         assert ( bp->isarray );
00614                 }
00615         }
00616         assert( NULL != bp );
00617         assert(validateBlock( bp ));
00618         bp->checkbit = 1;
00619         _debugUnlockHeap(  );
00620 }
00621 
00622 int _debugCheckMem()
00623 {
00624         struct allocblock *bp;
00625 
00626         assert(_CrtCheckMemory());
00627         if ( isshutdown )
00628         {
00629                 return 1;
00630         }
00631 
00632         _debugLockHeap(  );
00633 
00634         bp = m_abhead.next;
00635         while ( NULL != bp && &m_abtail != bp )
00636         {
00637                 if ( bp != NULL )
00638                 {
00639                         assert(validateBlock( bp ));
00640                         if( bp->checkbit != 1 )
00641                         {
00642                                 return 0;
00643                         }
00644                 }
00645                 bp = bp->next;
00646         }
00647         _debugUnlockHeap(  );
00648         return 1;
00649 }
00650 
00651 void _debugValidateHeap()
00652 {
00653         struct allocblock *bp = m_abhead.next;
00654 
00655         _debugLockHeap(  );
00656 
00657         while ( NULL != bp && bp != &m_abtail )
00658         {
00659                 assert ( validateBlock( bp ) );
00660                 assert( bp->next->prev == bp );
00661                 assert( bp->prev->next == bp );
00662                 bp = bp->next;
00663         }
00664         assert(_CrtCheckMemory());
00665         if ( isshutdown )
00666         {
00667                 return;
00668         }       
00669         _debugUnlockHeap(  );
00670 }
00671 
00672 void _debugDumpMemLeaks()
00673 {
00674         struct allocblock *bp = m_abhead.next;
00675         if ( isshutdown || NULL == bp )
00676         {
00677                 return;
00678         }       
00679         _debugLockHeap(  );
00680 
00681         while ( bp != &m_abtail )
00682         {
00683                 assert(validateBlock( bp ));
00684                 if( bp->checkbit != 1 )
00685                 {
00686                         printf( "Memory leak %d: %d bytes allocated in %s on line %d\n", bp->id, bp->size, bp->filename, bp->lineno );
00687                 }
00688                 bp = bp->next;
00689         }
00690         _debugUnlockHeap(  );
00691 }
00692 
00693 int _debugAssertMemFree()
00694 {
00695         return m_abhead.next == &m_abtail;
00696 }
00697 
00698 void fillCheckPointData( struct memcheckpoint *mp )
00699 {
00700         struct allocblock *bp;
00701 
00702         mp->count = 0;
00703         mp->size = 0;
00704 
00705         _debugLockHeap(  );
00706         bp = m_abhead.next;
00707 
00708         while( NULL != bp && &m_abtail != bp )
00709         {
00710                 if( bp != NULL )
00711                 {
00712                         mp->count++;
00713                         mp->size += bp->size;
00714                 }
00715                 bp = bp->next;
00716         }
00717         _debugUnlockHeap(  );
00718 }
00719 
00720 void _debugCheckpointHeap()
00721 {
00722         fillCheckPointData( &m_memcheckpoint );
00723 }
00724 
00725 int _debugAssertCheckPoint()
00726 {
00727         struct memcheckpoint mp;
00728         fillCheckPointData( &mp );
00729         return mp.size == m_memcheckpoint.size && mp.count == m_memcheckpoint.count;
00730 }
00731 
00732 #ifdef DEBUG
00733 void *rpl_malloc( const int size, char *filename, const int lineno, const bool isarray )
00734 {
00735         return _debugMalloc(size, filename, lineno, isarray);
00736 }
00737 #else
00738 void *rpl_malloc( const int size )
00739 {
00740         return malloc( size );
00741 }
00742 #endif
00743 
00744 #endif