- 论坛徽章:
- 0
|
/*
** 981217 JLI: is it possible that ->next is not always set?
*/
void * mwMark( void *p, const char *desc, const char *file, unsigned line ) {
mwMarker *mrk;
unsigned n, isnew;
char *buf;
int tot, oflow = 0;
char wherebuf[128];
mwAutoInit();
TESTS(NULL,0);
if( desc == NULL ) desc = "unknown";
if( file == NULL ) file = "unknown";
tot = sprintf( wherebuf, "%.48s called from %s(%d)", desc, file, line );
if( tot >= (int)sizeof(wherebuf) ) { wherebuf[sizeof(wherebuf)-1] = 0; oflow = 1; }
if( p == NULL ) {
mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc );
return p;
}
if( mwFirstMark != NULL && !mwIsReadAddr( mwFirstMark, sizeof( mwMarker ) ) )
{
mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n",
file, line, mwFirstMark, desc );
return p;
}
for( mrk=mwFirstMark; mrk; mrk=mrk->next )
{
if( mrk->next != NULL && !mwIsReadAddr( mrk->next, sizeof( mwMarker ) ) )
{
mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n",
file, line, mrk, mrk->next, desc );
return p;
}
if( mrk->host == p ) break;
}
if( mrk == NULL ) {
isnew = 1;
mrk = (mwMarker*) malloc( sizeof( mwMarker ) );
if( mrk == NULL ) {
mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
return p;
}
mrk->next = NULL;
n = 0;
}
else {
isnew = 0;
n = strlen( mrk->text );
}
n += strlen( wherebuf );
buf = (char*) malloc( n+3 );
if( buf == NULL ) {
if( isnew ) free( mrk );
mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc );
return p;
}
if( isnew ) {
memcpy( buf, wherebuf, n+1 );
mrk->next = mwFirstMark;
mrk->host = p;
mrk->text = buf;
mrk->level = 1;
mwFirstMark = mrk;
}
else {
strcpy( buf, mrk->text );
strcat( buf, ", " );
strcat( buf, wherebuf );
free( mrk->text );
mrk->text = buf;
mrk->level ++;
}
if( oflow ) {
mwIncErr();
mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" );
}
return p;
}
void* mwUnmark( void *p, const char *file, unsigned line ) {
mwMarker *mrk, *prv;
mrk = mwFirstMark;
prv = NULL;
while( mrk ) {
if( mrk->host == p ) {
if( mrk->level < 2 ) {
if( prv ) prv->next = mrk->next;
else mwFirstMark = mrk->next;
free( mrk->text );
free( mrk );
return p;
}
mrk->level --;
return p;
}
prv = mrk;
mrk = mrk->next;
}
mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p );
return p;
}
/***********************************************************************
** Abort/Retry/Ignore handlers
***********************************************************************/
static int mwARI( const char *estr ) {
char inbuf[81];
int c;
fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr);
(void) fgets(inbuf,sizeof(inbuf),stdin);
for( c=0; inbuf[c] && inbuf[c] <= ' '; c++ ) ;
c = inbuf[c];
if( c == 'R' || c == 'r' ) {
mwBreakOut( estr );
return MW_ARI_RETRY;
}
if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE;
return MW_ARI_ABORT;
}
/* standard ARI handler (exported) */
int mwAriHandler( const char *estr ) {
mwAutoInit();
return mwARI( estr );
}
/* used to set the ARI function */
void mwSetAriFunc( int (*func)(const char *) ) {
mwAutoInit();
mwAriFunction = func;
}
/***********************************************************************
** Allocation handlers
***********************************************************************/
void* mwMalloc( size_t size, const char* file, int line) {
size_t needed;
mwData *mw;
char *ptr;
void *p;
mwAutoInit();
MW_MUTEX_LOCK();
TESTS(file,line);
mwCounter ++;
needed = mwDataSize + mwOverflowZoneSize*2 + size;
if( needed < size )
{
/* theoretical case: req size + mw overhead exceeded size_t limits */
return NULL;
}
/* if this allocation would violate the limit, fail it */
if( mwUseLimit && ((long)size + mwStatCurAlloc > mwAllocLimit) ) {
mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
mwCounter, file, line, (long)size, mwAllocLimit - mwStatCurAlloc );
mwIncErr();
FLUSH();
MW_MUTEX_UNLOCK();
return NULL;
}
mw = (mwData*) malloc( needed );
if( mw == NULL ) {
if( mwFreeUp(needed,0) >= needed ) {
mw = (mwData*) malloc(needed);
if( mw == NULL ) {
mwWrite( "internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed );
mwIncErr();
FLUSH();
}
}
if( mw == NULL ) {
mwWrite( "fail: <%ld> %s(%d), %ld wanted %ld allocated\n",
mwCounter, file, line, (long)size, mwStatCurAlloc );
mwIncErr();
FLUSH();
MW_MUTEX_UNLOCK();
return NULL;
}
}
mw->count = mwCounter;
mw->prev = NULL;
mw->next = mwHead;
mw->file = file;
mw->size = size;
mw->line = line;
mw->flag = 0;
mw->check = CHKVAL(mw);
if( mwHead ) mwHead->prev = mw;
mwHead = mw;
if( mwTail == NULL ) mwTail = mw;
ptr = ((char*)mw) + mwDataSize;
mwWriteOF( ptr ); /* '*(long*)ptr = PRECHK;' */
ptr += mwOverflowZoneSize;
p = ptr;
memset( ptr, MW_VAL_NEW, size );
ptr += size;
mwWriteOF( ptr ); /* '*(long*)ptr = POSTCHK;' */
mwNumCurAlloc ++;
mwStatCurAlloc += (long) size;
mwStatTotAlloc += (long) size;
if( mwStatCurAlloc > mwStatMaxAlloc )
mwStatMaxAlloc = mwStatCurAlloc;
mwStatNumAlloc ++;
if( mwStatLevel ) mwStatAlloc( size, file, line );
MW_MUTEX_UNLOCK();
return p;
}
void* mwRealloc( void *p, size_t size, const char* file, int line) {
int oldUseLimit, i;
mwData *mw;
char *ptr;
mwAutoInit();
if( p == NULL ) return mwMalloc( size, file, line );
if( size == 0 ) { mwFree( p, file, line ); return NULL; }
MW_MUTEX_LOCK();
/* do the quick ownership test */
mw = (mwData*) mwBUFFER_TO_MW( p );
if( mwIsOwned( mw, file, line ) ) {
/* if the buffer is an NML, treat this as a double-free */
if( mw->flag & MW_NML )
{
mwIncErr();
if( *((unsigned char*)(mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
{
mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
mwCounter, file, line, mw );
}
goto check_dbl_free;
}
/* if this allocation would violate the limit, fail it */
if( mwUseLimit && ((long)size + mwStatCurAlloc - (long)mw->size > mwAllocLimit) ) {
TESTS(file,line);
mwCounter ++;
mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n",
mwCounter, file, line, (unsigned long)size - mw->size, mwAllocLimit - mwStatCurAlloc );
mwIncErr();
FLUSH();
MW_MUTEX_UNLOCK();
return NULL;
}
/* fake realloc operation */
oldUseLimit = mwUseLimit;
mwUseLimit = 0;
ptr = (char*) mwMalloc( size, file, line );
if( ptr != NULL ) {
if( size < mw->size )
memcpy( ptr, p, size );
else
memcpy( ptr, p, mw->size );
mwFree( p, file, line );
}
mwUseLimit = oldUseLimit;
MW_MUTEX_UNLOCK();
return (void*) ptr;
}
/* Unknown pointer! */
/* using free'd pointer? */
check_dbl_free:
for(i=0;i<MW_FREE_LIST;i++) {
if( mwLastFree == p ) {
mwIncErr();
mwWrite( "realloc: <%ld> %s(%d), %p was"
" freed from %s(%d)\n",
mwCounter, file, line, p,
mwLFfile, mwLFline );
FLUSH();
MW_MUTEX_UNLOCK();
return NULL;
}
}
/* some weird pointer */
mwIncErr();
mwWrite( "realloc: <%ld> %s(%d), unknown pointer %p\n",
mwCounter, file, line, p );
FLUSH();
MW_MUTEX_UNLOCK();
return NULL;
}
char *mwStrdup( const char* str, const char* file, int line ) {
size_t len;
char *newstring;
MW_MUTEX_LOCK();
if( str == NULL ) {
mwIncErr();
mwWrite( "strdup: <%ld> %s(%d), strdup(NULL) called\n",
mwCounter, file, line );
FLUSH();
MW_MUTEX_UNLOCK();
return NULL;
}
len = strlen( str ) + 1;
newstring = (char*) mwMalloc( len, file, line );
if( newstring != NULL ) memcpy( newstring, str, len );
MW_MUTEX_UNLOCK();
return newstring;
}
void mwFree( void* p, const char* file, int line ) {
int i;
mwData* mw;
char buffer[ sizeof(mwData) + (mwROUNDALLOC*3) + 64 ];
/* this code is in support of C++ delete */
if( file == NULL ) {
mwFree_( p );
MW_MUTEX_UNLOCK();
return;
}
mwAutoInit();
MW_MUTEX_LOCK();
TESTS(file,line);
mwCounter ++;
/* on NULL free, write a warning and return */
if( p == NULL ) {
mwWrite( "NULL free: <%ld> %s(%d), NULL pointer free'd\n",
mwCounter, file, line );
FLUSH();
MW_MUTEX_UNLOCK();
return;
}
/* do the quick ownership test */
mw = (mwData*) mwBUFFER_TO_MW( p );
if( mwIsOwned( mw, file, line ) ) {
(void) mwTestBuf( mw, file, line );
/* if the buffer is an NML, treat this as a double-free */
if( mw->flag & MW_NML )
{
if( *(((unsigned char*)mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML )
{
mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n",
mwCounter, file, line, mw );
}
goto check_dbl_free;
}
/* update the statistics */
mwNumCurAlloc --;
mwStatCurAlloc -= (long) mw->size;
if( mwStatLevel ) mwStatFree( mw->size, mw->file, mw->line );
/* we should either free the allocation or keep it as NML */
if( mwNML ) {
mw->flag |= MW_NML;
mwNmlNumAlloc ++;
mwNmlCurAlloc += (long) mw->size;
memset( ((char*)mw)+mwDataSize+mwOverflowZoneSize, MW_VAL_NML, mw->size );
}
else {
/* unlink the allocation, and enter the post-free data */
mwUnlink( mw, file, line );
memset( mw, MW_VAL_DEL,
mw->size + mwDataSize+mwOverflowZoneSize+mwOverflowZoneSize );
if( mwFBI ) {
memset( mw, '.', mwDataSize + mwOverflowZoneSize );
sprintf( buffer, "FBI<%ld>%s(%d)", mwCounter, file, line );
strncpy( (char*)(void*)mw, buffer, mwDataSize + mwOverflowZoneSize );
}
free( mw );
}
/* add the pointer to the last-free track */
mwLFfile[ mwLFcur ] = file;
mwLFline[ mwLFcur ] = line;
mwLastFree[ mwLFcur++ ] = p;
if( mwLFcur == MW_FREE_LIST ) mwLFcur = 0;
MW_MUTEX_UNLOCK();
return;
}
/* check for double-freeing */
check_dbl_free:
for(i=0;i<MW_FREE_LIST;i++) {
if( mwLastFree == p ) {
mwIncErr();
mwWrite( "double-free: <%ld> %s(%d), %p was"
" freed from %s(%d)\n",
mwCounter, file, line, p,
mwLFfile, mwLFline );
FLUSH();
MW_MUTEX_UNLOCK();
return;
}
}
/* some weird pointer... block the free */
mwIncErr();
mwWrite( "WILD free: <%ld> %s(%d), unknown pointer %p\n",
mwCounter, file, line, p );
FLUSH();
MW_MUTEX_UNLOCK();
return;
}
void* mwCalloc( size_t a, size_t b, const char *file, int line ) {
void *p;
size_t size = a * b;
p = mwMalloc( size, file, line );
if( p == NULL ) return NULL;
memset( p, 0, size );
return p;
}
void mwFree_( void *p ) {
MW_MUTEX_LOCK();
TESTS(NULL,0);
MW_MUTEX_UNLOCK();
free(p);
}
void* mwMalloc_( size_t size ) {
MW_MUTEX_LOCK();
TESTS(NULL,0);
MW_MUTEX_UNLOCK();
return malloc( size );
}
void* mwRealloc_( void *p, size_t size ) {
MW_MUTEX_LOCK();
TESTS(NULL,0);
MW_MUTEX_UNLOCK();
return realloc( p, size );
}
void* mwCalloc_( size_t a, size_t b ) {
MW_MUTEX_LOCK();
TESTS(NULL,0);
MW_MUTEX_UNLOCK();
return calloc( a, b );
}
void mwFlushNow( void ) {
if( mwLogR() ) fflush( mwLogR() );
return;
}
void mwDoFlush( int onoff ) {
mwFlushW( onoff<1?0 noff );
if( onoff ) if( mwLogR() ) fflush( mwLogR() );
return;
}
void mwLimit( long lim ) {
TESTS(NULL,0);
mwWrite("limit: old limit = " ;
if( !mwAllocLimit ) mwWrite( "none" );
else mwWrite( "%ld bytes", mwAllocLimit );
mwWrite( ", new limit = " ;
if( !lim ) {
mwWrite( "none\n" );
mwUseLimit = 0;
}
else {
mwWrite( "%ld bytes\n", lim );
mwUseLimit = 1;
}
mwAllocLimit = lim;
FLUSH();
}
void mwSetAriAction( int action ) {
MW_MUTEX_LOCK();
TESTS(NULL,0);
mwAriAction = action;
MW_MUTEX_UNLOCK();
return;
}
int mwAssert( int exp, const char *exps, const char *fn, int ln ) {
int i;
char buffer[MW_TRACE_BUFFER+8];
if( exp ) {
return 0;
}
mwAutoInit();
MW_MUTEX_LOCK();
TESTS(fn,ln);
mwIncErr();
mwCounter++;
mwWrite( "assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
if( mwAriFunction != NULL ) {
sprintf( buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps );
i = (*mwAriFunction)(buffer);
switch( i ) {
case MW_ARI_IGNORE:
mwWrite( "assert trap: <%ld> IGNORED - execution continues\n", mwCounter );
MW_MUTEX_UNLOCK();
return 0;
case MW_ARI_RETRY:
mwWrite( "assert trap: <%ld> RETRY - executing again\n", mwCounter );
MW_MUTEX_UNLOCK();
return 1;
}
}
else {
if( mwAriAction & MW_ARI_IGNORE ) {
mwWrite( "assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
MW_MUTEX_UNLOCK();
return 0;
}
fprintf(mwSTDERR,"\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps );
}
FLUSH();
(void) mwTestNow( fn, ln, 1 );
FLUSH();
if( mwAriAction & MW_ARI_NULLREAD ) {
/* This is made in an attempt to kick in */
/* any debuggers or OS stack traces */
FLUSH();
/*lint -save -e413 */
i = *((int*)NULL);
mwDummy( (char)i );
/*lint -restore */
}
MW_MUTEX_UNLOCK();
exit(255);
/* NOT REACHED - the return statement is in to keep */
/* stupid compilers from squeaking about differing return modes. */
/* Smart compilers instead say 'code unreachable...' */
/*lint -save -e527 */
return 0;
/*lint -restore */
}
int mwVerify( int exp, const char *exps, const char *fn, int ln ) {
int i;
char buffer[MW_TRACE_BUFFER+8];
if( exp ) {
return 0;
}
mwAutoInit();
MW_MUTEX_LOCK();
TESTS(fn,ln);
mwIncErr();
mwCounter++;
mwWrite( "verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps );
if( mwAriFunction != NULL ) {
sprintf( buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps );
i = (*mwAriFunction)(buffer);
if( i == 0 ) {
mwWrite( "verify trap: <%ld> IGNORED - execution continues\n", mwCounter );
MW_MUTEX_UNLOCK();
return 0;
}
if( i == 1 ) {
mwWrite( "verify trap: <%ld> RETRY - executing again\n", mwCounter );
MW_MUTEX_UNLOCK();
return 1;
}
}
else {
if( mwAriAction & MW_ARI_NULLREAD ) {
/* This is made in an attempt to kick in */
/* any debuggers or OS stack traces */
FLUSH();
/*lint -save -e413 */
i = *((int*)NULL);
mwDummy( (char)i );
/*lint -restore */
}
if( mwAriAction & MW_ARI_IGNORE ) {
mwWrite( "verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter );
MW_MUTEX_UNLOCK();
return 0;
}
fprintf(mwSTDERR,"\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps );
}
FLUSH();
(void) mwTestNow( fn, ln, 1 );
FLUSH();
MW_MUTEX_UNLOCK();
exit(255);
/* NOT REACHED - the return statement is in to keep */
/* stupid compilers from squeaking about differing return modes. */
/* Smart compilers instead say 'code unreachable...' */
/*lint -save -e527 */
return 0;
/*lint -restore */
} |
|