/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Implementation of debug logging utilities
*
*/
#ifndef __WINS__
# ifdef CATALOGS_BUILD_CONFIG_HEAP_CHECKER
# undef CATALOGS_BUILD_CONFIG_HEAP_CHECKER
# warning "Automatically disabling heap checker in ARMV5 build!"
# endif
# ifdef CATALOGS_BUILD_CONFIG_DEBUG
# warning "Debug logging on in ARM5 build"!
# endif
#endif
#ifdef __WINS__
# ifdef CATALOGS_BUILD_CONFIG_HEAP_CHECKER
# warning "Heap checker available!"
# endif
#endif
// Enable this to get alternative XML output
//#define CATALOGS_DEBUG_XML
// Quick method to disable debug functions:
// #undef CATALOGS_BUILD_CONFIG_DEBUG
// #undef CATALOGS_BUILD_CONFIG_HEAP_CHECKER
// #undef CATALOGS_EXT_LOGGER
#include <flogger.h>
#include <e32msgqueue.h>
#include <e32property.h>
#include <hal.h>
#include "catalogsdebug.h"
#include "e32debug.h"
#ifndef __WINS__
extern "C" TUint32 GetStackPointer();
#endif
//#define _DDPRINT( x ) RDebug::Printf x
#define _DDPRINT( x )
// Logger includes from ../../debuglogger/inc/
#include "catalogsdebugdefs.h"
#include "catalogslogger.hrh"
#undef CATALOGS_FILEID
#define CATALOGS_FILEID "$Id: //depot/nf-catalogs/impl/catalogs/engine/src/catalogsdebug.cpp#43 $"
#ifdef CATALOGS_DEBUG_XML
_LIT8( KCatalogsError, "error" );
_LIT8( KCatalogsWarning, "warning" );
_LIT8( KCatalogsInfo, "info" );
_LIT8( KCatalogsTraceIn, "func" );
_LIT8( KCatalogsTraceOut, "out" );
_LIT8( KCatalogsTraceLeave, "leave" );
_LIT8( KCatalogsTrace, "trace" );
_LIT8( KLogDir, "Catalogs" );
_LIT8( KLogName, "debug.log" );
#else
_LIT8( KCatalogsError, "ERROR" );
_LIT8( KCatalogsWarning, "WARNING" );
_LIT8( KCatalogsInfo, "INFO" );
_LIT8( KCatalogsTraceIn, "-->" );
_LIT8( KCatalogsTraceOut, "<--" );
_LIT8( KCatalogsTraceLeave, "<--LEAVE" );
_LIT8( KCatalogsTrace, "@" );
_LIT8( KCatalogsSeparator, "\t" );
_LIT( KLogDir, "Catalogs" );
_LIT( KLogName, "debug.log" );
#endif
const TInt KLineLength = 120;
#ifdef CATALOGS_BUILD_CONFIG_DEBUG
static void Append( const char* aText, TDes16& aDestination )
{
TPtrC8 ptr( reinterpret_cast<const TUint8*>( aText ) );
for ( TInt i = 0; i < ptr.Length(); i++ )
{
aDestination.Append( ptr[i] );
}
}
#endif
#ifndef CATALOGS_DEBUG_XML
static void Append( TPtrC8 aText, TDes8& aDestination, TInt aLength = 60 )
{
for ( TInt i = 0; i < aLength; i++ )
{
if ( i < aText.Length() )
{
aDestination.Append( aText[i] );
}
else
{
aDestination.Append( '.' );
}
}
}
#else
static void Append( TPtrC8 aText, TDes16& aDestination )
{
TInt len = aText.Length();
for ( TInt i = 0; i < len; i++ )
{
aDestination.Append( aText[i] );
}
}
const TDesC8& TypeTag( TCatalogsDebug::TType aType )
{
switch ( aType )
{
case TCatalogsDebug::EInfo:
return KCatalogsInfo;
break;
case TCatalogsDebug::EError:
return KCatalogsError;
break;
case TCatalogsDebug::EWarning:
return KCatalogsWarning;
break;
case TCatalogsDebug::ETrace:
return KCatalogsTrace;
break;
case TCatalogsDebug::ETraceIn:
return KCatalogsTraceIn;
break;
case TCatalogsDebug::ETraceOut:
return KCatalogsTraceOut;
break;
case TCatalogsDebug::ETraceLeave:
return KDebugLoggerTraceLeave;
break;
default:
DASSERT( EFalse );
break;
}
return KNullDesC(); // for compiler
}
#endif
EXPORT_C TCatalogsDebug::TCatalogsDebug(
TType aType,
const char* aFunction,
TInt aLine,
const char* aFileId,
TUint aDeltaTime,
TInt aOutput )
: iType( aType ), iFunction( aFunction ), iLine( aLine ), iFileId( aFileId ),
iDeltaTime( aDeltaTime ), iOutput( aOutput )
{}
inline RCatalogsDebugHeap* DebugHeap()
{
return static_cast< RCatalogsDebugHeap* >( &User::Heap() );
}
void TCatalogsDebug::PrintGeneral( TPtr8 aPrintBuf )
{
#ifdef CATALOGS_DEBUG_XML
// Insert start tag beginning.
aPrintBuf.AppendFormat( "<%S name=\"", &TypeTag( iType ) );
// Process the function string. Assume __PRETTY_FUNCTION__ -style string
TPtrC8 func( reinterpret_cast<const TUint8*>( iFunction ) );
// drop params
func.Set( func.Left( func.Locate( '(' ) ) );
// drop return value (if specified)
TInt nameStart = func.LocateReverse( ' ' );
if ( nameStart != KErrNotFound )
{
func.Set( func.Mid( nameStart+1 ) );
}
// Insert method name
aPrintBuf.Append( func );
aPrintBuf.Append( "\" file=\"" );
// Insert file name
aPrintBuf.Append( TDesC8( iFileId ) );
// Insert the rest of the attributes
RThread thread;
TInt totalAllocSize = 0;
User::AllocSize( totalAllocSize );
aPrintBuf.AppendFormat( "\" line=\"%d\" thread=\"%x%x\" alloc=\"%d\">",
iLine, (TUint32)(thread.Id().Id()>>32), (TUint32)thread.Id().Id(), totalAllocSize>>10 );
#else // CATALOGS_DEBUG_XML
// Insert current thread id.
RThread thread;
aPrintBuf.AppendNum( thread.Id().Id(), EHex );
aPrintBuf.Append( ' ' );
// Process the function string. Assume __PRETTY_FUNCTION__ -style string
TPtrC8 ptr( reinterpret_cast<const TUint8*>( iFunction ) );
// drop params
ptr.Set( ptr.Left( ptr.Locate( '(' ) ) );
// drop return value (if specified)
TInt nameStart = ptr.LocateReverse( ' ' );
if ( nameStart != KErrNotFound )
{
// aPrintBuf.Append( ptr.Mid( nameStart+1 ) );
Append( ptr.Mid( nameStart+1 ), aPrintBuf );
}
else
{
// aPrintBuf.Append( ptr );
Append( ptr, aPrintBuf );
}
aPrintBuf.Append( KCatalogsSeparator );
aPrintBuf.AppendNum( iLine );
aPrintBuf.Append( KCatalogsSeparator );
TInt totalAllocSize = 0;
User::AllocSize( totalAllocSize );
aPrintBuf.Append( 'M' );
aPrintBuf.AppendNum( totalAllocSize>>10 );
TThreadStackInfo stackInfo;
TInt err = thread.StackInfo( stackInfo );
if( err == KErrNone )
{
#ifdef __WINS__
volatile TInt32 currentSp=0;
__asm { mov currentSp, esp }
#else
TInt32 currentSp = GetStackPointer();
#endif
// TInt remainingStack = currentSp - (TInt)stackInfo.iLimit;
TInt remainingStack = (TInt)stackInfo.iBase - currentSp;
aPrintBuf.Append( '/' );
if( remainingStack > 1023 )
{
aPrintBuf.AppendNum( remainingStack >> 10 );
aPrintBuf.Append( 'k' );
}
else
{
aPrintBuf.AppendNum( remainingStack );
}
}
aPrintBuf.Append( KCatalogsSeparator );
switch ( iType )
{
case EError:
aPrintBuf.Append( KCatalogsError );
break;
case EWarning:
aPrintBuf.Append( KCatalogsWarning );
break;
case ETrace:
aPrintBuf.Append( KCatalogsTrace );
break;
case ETraceIn:
aPrintBuf.Append( KCatalogsTraceIn );
break;
case ETraceOut:
aPrintBuf.Append( KCatalogsTraceOut );
break;
case ETraceLeave:
aPrintBuf.Append( KCatalogsTraceLeave );
break;
case EInfo:
aPrintBuf.Append( KCatalogsInfo );
break;
default:
break;
}
#endif // CATALOGS_DEBUG_XML
}
void TCatalogsDebug::FileWrite( const TDesC8& aPrintBuf )
{
TLex8 lex( aPrintBuf );
TInt length = KLineLength;
lex.Mark();
while ( ! lex.Eos() )
{
while ( length-- > 0 && ! lex.Eos() )
{
lex.Inc();
}
length = KLineLength;
TPtrC8 line( lex.MarkedToken() );
lex.Mark();
RFileLogger::Write( KLogDir, KLogName, EFileLoggingModeAppend, line );
}
}
EXPORT_C void TCatalogsDebug::Print( TRefByValue<const TDesC16> aFmt, ... )
{
VA_LIST list;
VA_START( list, aFmt );
if( iOutput & EOutputExtLogger )
if( !DebugHeap()->IsEnabled( iType ) ) return;
// Disable debug heap (if installed) to prevent infinite recursion.
TBool debugHeapActive = RCatalogsDebugHeap::Activate( EFalse );
HBufC8* buffer = HBufC8::New( KCatalogsDebugBufferSize );
if ( buffer == NULL ) goto exit1;
PrintGeneral( buffer->Des() );
HBufC* buffer16 = HBufC::New( KCatalogsDebugBufferSize );
if ( buffer16 == NULL ) goto exit2;
buffer16->Des().AppendFormatList( aFmt, list );
HBufC8* buffer16to8 = HBufC8::New( buffer16->Length() );
if ( buffer16to8 == NULL ) goto exit3;
buffer16to8->Des().Copy( *buffer16 );
#ifndef CATALOGS_DEBUG_XML
buffer->Des().Append( KCatalogsSeparator );
buffer->Des().Append( *buffer16to8 );
#else
TInt formatPos = buffer->Length();
buffer->Des().Append( *buffer16to8 );
// replace xml-evil chars with ¤
TPtr8 formattedText = buffer->Des().MidTPtr( formatPos );
TInt pos;
while( (pos = formattedText.Locate( '<' )) != KErrNotFound )
{
formattedText[pos] = '¤';
}
while( (pos = formattedText.Locate( '>' )) != KErrNotFound )
{
formattedText[pos] = '¤';
}
while( (pos = formattedText.Locate( '&' )) != KErrNotFound )
{
formattedText[pos] = '¤';
}
#endif
#ifdef CATALOGS_DEBUG_XML
// In tag is not closed until after out
if( iType != ETraceIn )
{
// close the tag
buffer->Des().AppendFormat( "</%S>", &TypeTag( iType ) );
if( iType == ETraceOut || iType == ETraceLeave )
{
buffer->Des().AppendFormat( "</%S>", &KCatalogsTraceIn() );
}
}
#endif
if( iOutput & EOutputRDebug )
RDebug::Printf( (char*)buffer->Des().PtrZ() );
if( iOutput & EOutputExtLogger )
DebugHeap()->ChunkOutput( *buffer, iDeltaTime );
if( iOutput & EOutputFileLogger )
FileWrite( *buffer );
delete buffer16to8;
exit3:
delete buffer16;
exit2:
delete buffer;
exit1:
RCatalogsDebugHeap::Activate( debugHeapActive );
}
EXPORT_C void TCatalogsDebug::Print( const char* aFmt, ... )
{
VA_LIST list;
VA_START( list, aFmt );
if( iOutput & EOutputExtLogger )
{
if( !DebugHeap()->IsEnabled( iType ) ) return;
}
// Disable debug heap (if installed) to prevent infinite recursion.
TBool debugHeapActive = RCatalogsDebugHeap::Activate( EFalse );
HBufC8* buffer = HBufC8::New( KCatalogsDebugBufferSize );
if ( buffer == NULL ) return;
PrintGeneral( buffer->Des() );
#ifndef CATALOGS_DEBUG_XML
buffer->Des().Append( KCatalogsSeparator );
buffer->Des().AppendFormatList( TPtrC8( ( const TUint8* )aFmt ), list );
#else
TInt formatPos = buffer->Length();
buffer->Des().AppendFormatList( TPtrC8( ( const TUint8* )aFmt ), list );
// replace xml-evil chars with ¤
TPtr8 formattedText = buffer->Des().MidTPtr( formatPos );
TInt pos;
while( (pos = formattedText.Locate( '<' )) != KErrNotFound )
{
formattedText[pos] = '¤';
}
while( (pos = formattedText.Locate( '>' )) != KErrNotFound )
{
formattedText[pos] = '¤';
}
while( (pos = formattedText.Locate( '&' )) != KErrNotFound )
{
formattedText[pos] = '¤';
}
// In tag is not closed until after out
if( iType != ETraceIn )
{
// close the tag
buffer->Des().AppendFormat( "</%S>", &TypeTag( iType ) );
if( iType == ETraceOut || iType == ETraceLeave )
{
buffer->Des().AppendFormat( "</%S>", &KCatalogsTraceIn() );
}
}
#endif
if( iOutput & EOutputRDebug )
RDebug::Printf( (char*)buffer->Des().PtrZ() );
if( iOutput & EOutputExtLogger )
DebugHeap()->ChunkOutput( *buffer, iDeltaTime );
if( iOutput & EOutputFileLogger )
FileWrite( *buffer );
delete buffer;
RCatalogsDebugHeap::Activate( debugHeapActive );
}
EXPORT_C void TCatalogsDebug::DumpData( const TAny* aData, TInt aSize, TInt aClipToSize )
{
TInt size = aSize;
Print( "dump: %d bytes total", aSize );
TUint8* data = ( TUint8* )aData;
while ( size > 0 )
{
// Cut dumps of > aClipToSize from the middle.
if ( ( aSize-size > aClipToSize/2 ) && ( size > aClipToSize/2 ) )
{
TInt clipAmount = ( ( aSize - aClipToSize ) / 8 + 1 ) * 8;
Print( "... %d bytes of data clipped ...", clipAmount );
size -= clipAmount;
data += clipAmount;
continue;
}
switch ( size )
{
case 1:
Print( "%04x: %02x", aSize-size,
( TInt )data[0] );
break;
case 2:
Print( "%04x: %02x %02x", aSize-size,
( TInt )data[0], ( TInt )data[1] );
break;
case 3:
Print( "%04x: %02x %02x %02x", aSize-size,
( TInt )data[0], ( TInt )data[1], ( TInt )data[2] );
break;
case 4:
Print( "%04x: %02x %02x %02x %02x", aSize-size,
( TInt )data[0], ( TInt )data[1], ( TInt )data[2],
( TInt )data[3] );
break;
case 5:
Print( "%04x: %02x %02x %02x %02x %02x", aSize-size,
( TInt )data[0], ( TInt )data[1], ( TInt )data[2],
( TInt )data[3], ( TInt )data[4] );
break;
case 6:
Print( "%04x: %02x %02x %02x %02x %02x %02x", aSize-size,
( TInt )data[0], ( TInt )data[1], ( TInt )data[2],
( TInt )data[3], ( TInt )data[4], ( TInt )data[5] );
break;
case 7:
Print( "%04x: %02x %02x %02x %02x %02x %02x %02x", aSize-size,
( TInt )data[0], ( TInt )data[1], ( TInt )data[2],
( TInt )data[3], ( TInt )data[4], ( TInt )data[5],
( TInt )data[6] );
break;
default: // 8 or more
Print( "%04x: %02x %02x %02x %02x %02x %02x %02x %02x", aSize-size,
( TInt )data[0], ( TInt )data[1], ( TInt )data[2],
( TInt )data[3], ( TInt )data[4], ( TInt )data[5],
( TInt )data[6], ( TInt )data[7] );
break;
}
size -= 8;
data += 8;
}
}
TBool RCatalogsDebugHeap::IsEnabled( TCatalogsDebug::TType aPrintType )
{
if( iChunkIndex == -1 ) return EFalse;
TCatalogsDebugChunkHeader* header = (TCatalogsDebugChunkHeader*)iChunk[iChunkIndex].Base();
switch( aPrintType )
{
case TCatalogsDebug::EError:
return header->iFlags & ECatalogsDebugFlagEnableError;
case TCatalogsDebug::EWarning:
return header->iFlags & ECatalogsDebugFlagEnableWarning;
case TCatalogsDebug::EInfo:
return header->iFlags & ECatalogsDebugFlagEnableInfo;
case TCatalogsDebug::ETrace:
case TCatalogsDebug::ETraceIn:
case TCatalogsDebug::ETraceOut:
case TCatalogsDebug::ETraceLeave:
return header->iFlags & ECatalogsDebugFlagEnableTrace;
}
return EFalse;
}
void RCatalogsDebugHeap::ChunkOutput( const TDesC8& aBuffer, TUint aDeltaTime )
{
if( iChunkIndex == -1 )
return; // not initialized ok; debuglogger app propably not running
// Get access to output.
iMutex.Wait();
// Read the output chunk header.
TCatalogsDebugChunkHeader* header = (TCatalogsDebugChunkHeader*)iChunk[iChunkIndex].Base();
if( header->iFlags & ECatalogsDebugFlagFlushChunk )
{
_DDPRINT(( "DPRN: Debug chunk %d -> %d, flushed by someone else", iChunkIndex, iChunkIndex ^ 1 ));
// Chunk flushed, need to switch.
iChunkIndex ^= 1; // toggle 0/1
header = (TCatalogsDebugChunkHeader*)iChunk[iChunkIndex].Base();
}
if( header->iFlags & ECatalogsDebugFlagFlushChunk )
{
// Horror error
DASSERT( EFalse );
}
// Now we have exclusive access to writable chunk.
// Generate time stamp.
TUint32 fastCounter = User::FastCounter();
/*
TBuf8< 14 > timeStamp; // HH:mm:ss.ss
TUint32 hours = fastCounter / (fastCounterFrequency*60*60);
fastCounter -= hours * (fastCounterFrequency*60*60);
TUint32 minutes = fastCounter / (fastCounterFrequency*60);
fastCounter -= minutes * (fastCounterFrequency*60);
TUint32 seconds = fastCounter / fastCounterFrequency;
fastCounter -= seconds * fastCounterFrequency;
TUint32 secondParts = fastCounter / (fastCounterFrequency / 100 );
timeStamp.Format( "%02d:%02d:%02d.%02d", hours, minutes, seconds, secondParts );
*/
TBuf8< 32 > timeStamp;
TUint32 seconds = fastCounter / iFastCounterFrequency;
TUint32 secondParts = (fastCounter % iFastCounterFrequency) * 1000 / iFastCounterFrequency;
if( aDeltaTime != 0 )
{
TUint32 dseconds = aDeltaTime / iFastCounterFrequency;
TUint32 dsecondParts = (aDeltaTime % iFastCounterFrequency) * 10000 / iFastCounterFrequency;
timeStamp.Format( _L8("%u.%03u %u.%04u\t"), seconds, secondParts, dseconds, dsecondParts );
}
else
{
timeStamp.Format( _L8("%u.%03u \t"), seconds, secondParts );
}
// aPrintBuf.AppendNum( User::FastCounter() );
TInt spaceRemaining = KCatalogsDebugChunkSize - sizeof( TCatalogsDebugChunkHeader) - header->iOffset;
if( spaceRemaining < (timeStamp.Size() + aBuffer.Size() + KCatalogsDebugLineSeparator().Size() ) )
{
_DDPRINT(( "DPRN: Debug chunk %d -> %d, full, flushing", iChunkIndex, iChunkIndex ^ 1 ));
// Not enough space for writing the entry to current chunk, need to flush it.
header->iFlags |= ECatalogsDebugFlagFlushChunk;
iMsgQueue.Send( iChunkIndex );
// Switch to the other chunk, not letting other threads get in between
iChunkIndex ^= 1; // toggle 0/1
// Get access to the new chunk. Will block if the block is still being processed by ext-logger.
// The semaphore will be signalled by ext-logger after the chunk has been processed.
_DDPRINT(( "DPRN: Debug chunk write semaphore wait" ));
iChunkWriteSemaphore.Wait();
_DDPRINT(( "DPRN: Debug chunk write semaphore wait done" ));
// Initialize the new chunk.
header = (TCatalogsDebugChunkHeader*)iChunk[iChunkIndex].Base();
header->iFlags &= ~ECatalogsDebugFlagFlushChunk;
header->iOffset = 0;
}
// Copy the output buffer to output chunk.
TUint8* ptr = (TUint8*)(header+1) + header->iOffset;
Mem::Copy( ptr, timeStamp.Ptr(), timeStamp.Size() );
ptr += timeStamp.Size();
Mem::Copy( ptr, aBuffer.Ptr(), aBuffer.Size() );
ptr += aBuffer.Size();
Mem::Copy( ptr, KCatalogsDebugLineSeparator().Ptr(), KCatalogsDebugLineSeparator().Size() );
// Update the output chunk header.
header->iOffset += timeStamp.Size() + aBuffer.Size() + KCatalogsDebugLineSeparator().Size();
// Release output for others.
iMutex.Signal();
}
void RCatalogsDebugHeap::InitExtLogger()
{
TInt err = iChunk[0].OpenGlobal( KCatalogsDebugChunk1Name, EFalse );
if( err != KErrNone ) return;
err = iChunk[1].OpenGlobal( KCatalogsDebugChunk2Name, EFalse );
if( err != KErrNone ) return;
err = iMutex.OpenGlobal( KCatalogsDebugMutexName );
if( err != KErrNone ) return;
err = iChunkWriteSemaphore.OpenGlobal( KCatalogsDebugChunkWriteSemaphoreName );
if( err != KErrNone ) return;
err = iMsgQueue.OpenGlobal( KCatalogsDebugMsgQueueName );
if( err != KErrNone ) return;
TInt freq = 0;
err = HAL::Get( HALData::EFastCounterFrequency, freq );
DASSERT( freq >= 0 );
iFastCounterFrequency = (TUint)freq;
if( err != KErrNone ) return;
// Init ok, start with chunk 0.
iChunkIndex = 0;
}
EXPORT_C void RCatalogsDebugHeap::InstallL( TBool aEnabled )
{
// DLTRACEIN( ( "" ) );
RHeap* oldHeap = &User::Heap();
// DLINFO( ( "Previous heap at %08x", oldHeap ) );
RCatalogsDebugHeap* heap = new RCatalogsDebugHeap( *oldHeap, aEnabled );
heap->InitExtLogger();
// DLINFO( ( "Created debug heap at %08x, switching", heap ) );
User::SwitchHeap( heap );
// DLTRACEOUT( ( "" ) );
}
EXPORT_C TBool RCatalogsDebugHeap::Activate( TBool aActive )
{
// Need some clever way to check that User::Heap() really is our heap.
RCatalogsDebugHeap* heap = static_cast< RCatalogsDebugHeap* >( &User::Heap() );
TBool result = heap->iActive;
heap->iActive = aActive;
return result;
}
RCatalogsDebugHeap::RCatalogsDebugHeap( RHeap& aBaseHeap, TBool aEnabled )
: iBaseHeap( aBaseHeap ), iAllocCounter( 0 ), iAllocInfo( 1024 /*list granularity*/ ),
iEnabled( aEnabled ), iActive( EFalse ), iChunkIndex( -1 )
{
}
RCatalogsDebugHeap::~RCatalogsDebugHeap()
{
iAllocInfo.Reset();
iChunk[0].Close();
iChunk[1].Close();
iChunkWriteSemaphore.Close();
iMutex.Close();
iMsgQueue.Close();
}
TAny* RCatalogsDebugHeap::operator new( TUint aSize )
{
return User::Heap().Alloc( aSize );
}
void RCatalogsDebugHeap::operator delete( TAny* aPtr )
{
User::Heap().Free( aPtr );
}
EXPORT_C void RCatalogsDebugHeap::Uninstall()
{
RCatalogsDebugHeap* heap = static_cast< RCatalogsDebugHeap* >( &User::Heap() );
if( heap->iEnabled )
{
if( heap->iAllocInfo.Count() > 0 )
{
DLTRACEIN( ( "" ) );
DLINFO( ( "Debug heap at %08x", heap ) );
DLINFO( ( "%d entries in alloc info list", heap->iAllocInfo.Count() ) );
for ( TInt i=0; i<heap->iAllocInfo.Count(); i++ )
{
TAllocInfo& info = heap->iAllocInfo[i];
DLERROR( ( "ALLOC #%d: memory leak %d bytes at %08x:", info.iAllocNum, info.iAllocSize, info.iAllocPtr ) );
const TInt KMaxText = 64;
TPtrC8 text( reinterpret_cast<unsigned char*>( info.iAllocPtr ),
info.iAllocSize <= KMaxText ? info.iAllocSize : KMaxText );
DLERROR( ( "text: %S", &text ) );
DLERRORDUMP( info.iAllocPtr, info.iAllocSize, 1024 );
}
}
}
RHeap* oldHeap = &heap->iBaseHeap;
// DLINFO( ( "Switching back to previous heap %08x", oldHeap ) );
User::SwitchHeap( oldHeap );
delete heap;
// DLTRACEOUT( ( "" ) );
}
TAny* RCatalogsDebugHeap::Alloc( TInt aSize )
{
TAny* result = iBaseHeap.Alloc( aSize );
if ( iEnabled && iActive )
{
DLTRACEIN(("aSize=%d", aSize));
iAllocCounter++;
DLINFO( ( "ALLOC #%d: allocated %d bytes at %08x", iAllocCounter, aSize, result ) );
TAllocInfo info;
info.iAllocPtr = result;
info.iAllocSize = aSize;
info.iAllocNum = iAllocCounter;
iActive = EFalse; // deactivate temporarily to prevent internal alloc recording
TInt err = iAllocInfo.Append( info );
iActive = ETrue;
if ( err != KErrNone )
{
DLERROR( ( "ALLOC ERROR appending alloc info" ) );
}
DLTRACEOUT(("%08x", result));
}
return result;
}
void RCatalogsDebugHeap::Free( TAny* aPtr )
{
iBaseHeap.Free( aPtr );
if( iEnabled && iActive )
{
DLTRACEIN(("aPtr=%08x", aPtr));
for ( TInt i=0; i<iAllocInfo.Count(); i++ )
{
TAllocInfo& info = iAllocInfo[i];
if ( info.iAllocPtr == aPtr )
{
DLINFO( ( "ALLOC #%d: freed %d bytes at %08x", info.iAllocNum, info.iAllocSize, aPtr ) );
iAllocInfo.Remove( i );
return;
}
}
}
}
TAny* RCatalogsDebugHeap::ReAlloc( TAny* aPtr, TInt aSize, TInt aMode )
{
TAny* result = iBaseHeap.ReAlloc( aPtr, aSize, aMode );
if( iEnabled )
{
for ( TInt i=0; i<iAllocInfo.Count(); i++ )
{
TAllocInfo& info = iAllocInfo[i];
if ( info.iAllocPtr == aPtr )
{
DLTRACEIN(("aPtr=%08x, aSize=%d, aMode=%d", aPtr, aSize, aMode));
DLINFO( ( "ALLOC #%d: realloc %d bytes at %08x to %d bytes at %08x",
info.iAllocNum, info.iAllocSize, aPtr, aSize, result ) );
info.iAllocSize = aSize;
info.iAllocPtr = result;
return result;
}
}
}
return result;
}
TInt RCatalogsDebugHeap::AllocLen( const TAny* aCell ) const
{
return iBaseHeap.AllocLen( aCell );
}
TInt RCatalogsDebugHeap::Compress()
{
return iBaseHeap.Compress();
}
void RCatalogsDebugHeap::Reset()
{
DLTRACEIN((""));
iBaseHeap.Reset();
DLINFO( ( "ALLOC: heap reset!" ) );
iAllocInfo.Reset();
}
TInt RCatalogsDebugHeap::AllocSize( TInt& aTotalAllocSize ) const
{
return iBaseHeap.AllocSize( aTotalAllocSize );
}
TInt RCatalogsDebugHeap::Available( TInt& aBiggestBlock ) const
{
return iBaseHeap.Available( aBiggestBlock );
}
TInt RCatalogsDebugHeap::DebugFunction( TInt aFunc, TAny* a1, TAny* a2 )
{
return iBaseHeap.DebugFunction( aFunc, a1, a2 );
}
TInt RCatalogsDebugHeap::Extension_( TUint /*aExtensionId*/, TAny*& /*a0*/, TAny* /*a1*/ )
{
DLTRACEIN((""));
DASSERT( EFalse );
return 0;
}
static void CleanupWatcherPop( TAny* aArg )
{
TCatalogsLocalCleanupStackWatcher* watcher = static_cast< TCatalogsLocalCleanupStackWatcher* >( aArg );
watcher->iPopped = ETrue;
}
EXPORT_C TCatalogsLocalCleanupStackWatcher::TCatalogsLocalCleanupStackWatcher()
: iPopped( EFalse )
{
CleanupDeletePushL( ( TAny* )NULL ); // use NULL pointer as a marker
CleanupStack::PushL( TCleanupItem( CleanupWatcherPop, ( TAny* )this ) );
}
EXPORT_C TCatalogsLocalCleanupStackWatcher::~TCatalogsLocalCleanupStackWatcher()
{
if ( !iPopped )
{
CleanupStack::PopAndDestroy();
CleanupStack::Pop( ( TAny* )NULL ); // check for NULL marker at this position
}
}
static void DebugHeapRestore( TAny* aArg )
{
TCatalogsLocalDebugHeapActivator* activator = static_cast< TCatalogsLocalDebugHeapActivator* >( aArg );
RCatalogsDebugHeap::Activate( activator->iActive );
activator->iPopped = ETrue;
}
EXPORT_C TCatalogsLocalDebugHeapActivator::TCatalogsLocalDebugHeapActivator()
: iPopped( EFalse )
{
// Activate debug heap.
iActive = RCatalogsDebugHeap::Activate( ETrue );
// Push cleanup item for restoring the previous debug heap enable status.
CleanupStack::PushL( TCleanupItem( DebugHeapRestore, ( TAny* )this ) );
}
EXPORT_C TCatalogsLocalDebugHeapActivator::~TCatalogsLocalDebugHeapActivator()
{
if ( !iPopped )
{
// Restore debug heap previous enable status.
RCatalogsDebugHeap::Activate( iActive );
CleanupStack::Pop();
}
}
EXPORT_C TCatalogsLocalExitTrace::TCatalogsLocalExitTrace( const char* aFunctionName, TInt aLine, const char* aFileId )
: iFunctionName( aFunctionName ), iLine( aLine ), iFileId( aFileId ), iDisabled( EFalse )
{
iEntryTime = User::FastCounter();
}
EXPORT_C TCatalogsLocalExitTrace::~TCatalogsLocalExitTrace()
{
if ( std::uncaught_exception() )
{
TCatalogsDebug debug( TCatalogsDebug::ETraceLeave, iFunctionName, iLine, iFileId, User::FastCounter()-iEntryTime );
debug.Print( "" );
}
else if ( !iDisabled )
{
TCatalogsDebug debug( TCatalogsDebug::ETraceOut, iFunctionName, iLine, iFileId, User::FastCounter()-iEntryTime );
debug.Print( "" );
}
}
EXPORT_C void TCatalogsLocalExitTrace::Disable()
{
iDisabled = ETrue;
}