--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/emailservices/emailstore/message_store/debuglog/src/DebugLog.cpp Thu Dec 17 08:39:21 2009 +0200
@@ -0,0 +1,1340 @@
+/*
+* 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: Logger implementation.
+*
+*/
+
+
+
+// ========
+// INCLUDES
+// ========
+
+#include <e32std.h>
+#include <f32file.h>
+#include <sysutil.h>
+
+#include "DebugLog.h"
+#include "DebugLogConst.h"
+
+// This takes a lot more memory to use, so it is defaulted to off. If the code is crashing
+// in DoLogLeakedObjects then uncomment this line to help find the problem. It is most likely
+// caused by a DLL that was explicitly unloaded, making the original copy of the class name
+// unavailable.
+// <cmail>
+// #define __MAKE_COPY_OF_NAMES
+// </cmail>
+
+// : IMPLEMENT THIS USING RDebugLog::Open/Close AND class name/object address
+
+// This provides additional help for debugging memory leaks. Uncomment out the following line
+// in order to log all constructions/destructions from all objects from here, rather than requiring
+// recompilation of all code.
+//#define __LOG_CONSTRUCTION_AND_DESTRUCTION
+
+// =========
+// CONSTANTS
+// =========
+
+const TInt KDateOrTimeMaxLength=30;
+
+_LIT( KDateTimeFormat,"%1%/1%2%/2%3 %H%:1%T%:2%S.%*C3 " );
+
+_LIT( KUnderscore, "_" );
+
+_LIT( KBackslash, "\\" );
+
+_LIT8(KEndOfLineCharacters8,"\r\n");
+
+_LIT8( KTwoColons, "::" );
+
+_LIT8( KColonAndSpace, ": " );
+
+const TText8 KFullStopChar8='.';
+
+const TText8 KTabChar8='\t';
+
+const TInt KFormattedBufferSize = 350;
+
+const TInt KMaxTextPerLine = 150;
+
+const TInt KMaxClassAndFuncNameLength = 35;
+
+// ================
+// INTERNAL CLASSES
+// ================
+
+// ============================================
+// 8-bit formatter overflow handler
+// ============================================
+class TDebugLogBase8Overflow : public TDes8Overflow
+{
+public:
+ virtual void Overflow( TDes8& aDes );
+};
+
+// ============================================
+// 16-bit formatter overflow handler and buffer
+// ============================================
+class TDebugLogBase16Overflow : public TDes16Overflow
+{
+public:
+ virtual void Overflow( TDes16& aDes );
+};
+
+// ============================================================
+// CLogFileHandler
+// This class handles a single log file for the current thread.
+// Note that the filenames are the same for all log files in
+// this thread, but the directory names may be different.
+// ============================================================
+class CLogFileHandler : public CBase
+{
+public:
+
+ RPointerArray<RDebugLog> iObjects;
+
+//#ifdef __MAKE_COPY_OF_NAMES
+ RPointerArray<HBufC8> iObjectNames;
+//#endif
+
+ CLogFileHandler( RFs& aFs,
+ TDes& aFormatBuffer,
+ TDes8& aOutputBuffer,
+ const TDesC& aDirectoryName,
+ const TDesC& aFileName );
+
+ virtual ~CLogFileHandler();
+
+ // returns ETrue if this handler is the log handler for the
+ // given directory
+ TBool IsHandler( const TDesC& aDirectoryName );
+
+ // returns ETrue if the log file is open and ready for writing
+ TBool ReadyToWrite();
+
+ // tries to open the log file and sets internal flags
+ // aCloseFile - set to ETrue if the file should be closed during the call
+ void TryToOpenFile( TBool aCloseFile = EFalse );
+
+ // closes the log file
+ void CloseFile();
+
+ // flushes the log file
+ void Flush();
+
+ void WriteFormat( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ TRefByValue< const TDesC16 > aFmt,
+ VA_LIST& aList );
+
+ void WriteFormat( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ TRefByValue< const TDesC8 > aFmt,
+ VA_LIST& aList );
+
+ void Write( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ const TDesC16& aDes );
+
+ void Write( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ const TDesC8& aDes );
+
+ void HexDump( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ const TDesC8& aBuffer );
+
+private:
+
+ RFile iFile;
+
+ void WriteLineL( const TDesC8& aDes );
+
+ void WriteLineL( const TDesC16& aDes );
+
+ void AddTimestampToOutputBufferL();
+
+ void AddClassAndFunctionToOutputBufferL( const TDesC8& aClassName,
+ const TDesC8& aFuncName );
+
+ RFs& iFs;
+ TDes& iFormatBuffer;
+ TPtr8 iFormatBuffer8;
+ TDes8& iOutputBuffer;
+ const TDesC& iDirectoryName;
+ const TDesC& iFileName;
+ TBool iTryToOpenFile;
+ TBool iFileIsOpen;
+ TBool iFirstTime;
+
+}; // END CLogFileHandler
+
+// ================================================================
+// CDebugLogTlsData
+// This class is the singleton object that manages all instances of
+// the log file handler for the current thread.
+// ================================================================
+class CDebugLogTlsData : private CActive
+{
+public:
+
+ static CLogFileHandler* GetLogFileHandler( const TDesC& aDirectory, RDebugLog* aObject );
+
+ static void ReleaseLogFileHandler( CLogFileHandler* aHandler, RDebugLog* aObject );
+
+ static CDebugLogTlsData* Instance();
+
+ static void FreeIfNecessary();
+
+ static void LogLeakedObjects();
+
+private:
+
+ CDebugLogTlsData();
+
+ TBool Initialize();
+
+ virtual ~CDebugLogTlsData();
+
+ CLogFileHandler* DoGetLogFileHandler( const TDesC& aDirectory, RDebugLog* aObject );
+
+ void DoReleaseLogFileHandler( CLogFileHandler* aHandler, RDebugLog* aObject );
+
+ void DoLogLeakedObjects();
+
+ // inherited from CActive
+ void RunL();
+ void DoCancel();
+
+ RFs iFs;
+ TFileName iFileName;
+ RPointerArray<CLogFileHandler> iFiles;
+ TBuf<KFormattedBufferSize> iFormatBuffer;
+ TBuf8<KMaxTextPerLine+2> iOutputBuffer; // two extra bytes for end of line characters
+
+}; // END CDebugLogTlsData
+
+// ======================
+// METHOD IMPLEMENTATIONS
+// ======================
+
+// ==========================================================================
+// METHOD: Constructor
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C RDebugLog::RDebugLog()
+ {
+ iLogFileHandler = NULL;
+ } // END RDebugLog
+
+// ==========================================================================
+// METHOD: CleanupClosePushL
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::CleanupClosePushL()
+ {
+ CleanupStack::PushL( TCleanupItem(&RDebugLog::StaticClose, this) );
+ } // END CleanupClosePushL
+
+// ==========================================================================
+// METHOD: Open
+//
+// DESIGN: Initialize this object.
+// ==========================================================================
+EXPORT_C void RDebugLog::Open( const TDesC& aLogDirectory,
+ const TDesC8& aClassName8,
+ TAny* aObjectAddress )
+ {
+ iObjectAddress = aObjectAddress;
+ iClassName8.Set( aClassName8 );
+ iLogDirectory.Set( aLogDirectory );
+
+#ifdef DEBUG_LOG_CLOSE_BETWEEN_WRITES_AS_DEFAULT
+ iCloseBetweenWrites = ETrue;
+#else
+ iCloseBetweenWrites = EFalse;
+#endif
+
+ iLogFileHandler = CDebugLogTlsData::GetLogFileHandler( aLogDirectory, this );
+
+#ifdef __LOG_CONSTRUCTION_AND_DESTRUCTION
+ if( iObjectAddress != 0 )
+ {
+ const TUint bufferSize = 20;
+ TBuf8<bufferSize> buf;
+ _LIT8( KObjAddrFmt, "0x%x" );
+ _LIT8( KDestructor, "CONSTRUCTOR" );
+ buf.Format( KObjAddrFmt, iObjectAddress );
+ Write( KDestructor, buf );
+ }
+#endif
+
+ if( !iLogFileHandler )
+ {
+ CDebugLogTlsData::FreeIfNecessary();
+ } // end if
+
+ } // END Open
+
+// ==========================================================================
+// METHOD: Close
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::Close()
+ {
+#ifdef __LOG_CONSTRUCTION_AND_DESTRUCTION
+ if( iObjectAddress != 0 )
+ {
+ const TUint bufferSize = 20;
+ TBuf8<bufferSize> buf;
+ _LIT8( KObjAddrFmt, "0x%x" );
+ _LIT8( KDestructor, "DESTRUCTOR" );
+ buf.Format( KObjAddrFmt(), iObjectAddress );
+ Write( KDestructor(), buf );
+ }
+#endif
+
+ if( iLogFileHandler )
+ {
+ CDebugLogTlsData::ReleaseLogFileHandler( iLogFileHandler, this );
+ iLogFileHandler = NULL;
+
+ CDebugLogTlsData::FreeIfNecessary();
+
+ } // end if
+
+ } // END Close
+
+// ==========================================================================
+// METHOD: Destructor
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C RDebugLog::~RDebugLog()
+ {
+ if( iLogFileHandler )
+ {
+ // Error! This means that Open was called, but not Close.
+ if( PrepareForWrite() )
+ {
+ _LIT8( KDummyFuncName, "***" );
+ _LIT8( KErrorText, "DEBUG LOG NOT CLOSED!" );
+
+ iLogFileHandler->Write( iClassName8, KDummyFuncName, KErrorText );
+ WriteComplete();
+ } // end if
+
+ Close();
+ }
+
+ } // END Destructor
+
+// ==========================================================================
+// METHOD: SetCloseBetweenWrites
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::SetCloseBetweenWrites( TBool aValue )
+ {
+ iCloseBetweenWrites = aValue;
+
+ } // END SetCloseBetweenWrites
+
+// ==========================================================================
+// METHOD: Write
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::Write( const TDesC8& aFuncName8,
+ const TDesC16& aText ) const
+ {
+ RDebugLog* self = const_cast<RDebugLog*>(this);
+
+ if( self->PrepareForWrite() )
+ {
+ // Write the descriptor to the log file
+ iLogFileHandler->Write( iClassName8, aFuncName8, aText );
+ self->WriteComplete();
+ } // end if
+
+ } // END Write
+
+// ==========================================================================
+// METHOD: Write
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::Write( const TDesC8& aFuncName8,
+ const TDesC8& aText ) const
+ {
+ RDebugLog* self = const_cast<RDebugLog*>(this);
+
+ if( self->PrepareForWrite() )
+ {
+ // Write the descriptor to the log file
+ iLogFileHandler->Write( iClassName8, aFuncName8, aText );
+ self->WriteComplete();
+ } // end if
+
+ } // END Write
+
+// ==========================================================================
+// METHOD: WriteFormat
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::WriteFormat( const TDesC8& aFuncName8,
+ TRefByValue< const TDesC16 > aFmt,
+ ... ) const
+ {
+ VA_LIST list;
+ VA_START(list,aFmt);
+
+ RDebugLog* self = const_cast<RDebugLog*>(this);
+
+ if( self->PrepareForWrite() )
+ {
+ // Call WriteFormat method with argument list
+ iLogFileHandler->WriteFormat( iClassName8, aFuncName8, aFmt, list );
+ self->WriteComplete();
+ } // end if
+
+ } // END WriteFormat
+
+// ==========================================================================
+// METHOD: WriteFormat
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::WriteFormat( const TDesC8& aFuncName8,
+ TRefByValue< const TDesC16 > aFmt,
+ VA_LIST& aList ) const
+ {
+ RDebugLog* self = const_cast<RDebugLog*>(this);
+
+ if( self->PrepareForWrite() )
+ {
+ // Call WriteFormat method with argument list
+ iLogFileHandler->WriteFormat( iClassName8, aFuncName8, aFmt, aList );
+ self->WriteComplete();
+ } // end if
+
+ } // END WriteFormat
+
+// ==========================================================================
+// METHOD: WriteFormat
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::WriteFormat( const TDesC8& aFuncName8,
+ TRefByValue< const TDesC8 > aFmt,
+ ... ) const
+ {
+ VA_LIST list;
+ VA_START(list,aFmt);
+
+ RDebugLog* self = const_cast<RDebugLog*>(this);
+
+ if( self->PrepareForWrite() )
+ {
+ // Call WriteFormat method with argument list
+ iLogFileHandler->WriteFormat( iClassName8, aFuncName8, aFmt, list );
+ self->WriteComplete();
+ } // end if
+
+ } // END WriteFormat
+
+// ==========================================================================
+// METHOD: WriteFormat
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::WriteFormat( const TDesC8& aFuncName8,
+ TRefByValue< const TDesC8 > aFmt,
+ VA_LIST& aList ) const
+ {
+ RDebugLog* self = const_cast<RDebugLog*>(this);
+
+ if( self->PrepareForWrite() )
+ {
+ // Call WriteFormat method with argument list
+ iLogFileHandler->WriteFormat( iClassName8, aFuncName8, aFmt, aList );
+ self->WriteComplete();
+ } // end if
+
+ } // END WriteFormat
+
+// ==========================================================================
+// METHOD: HexDump
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::HexDump( const TDesC8& aFuncName8,
+ const TDesC8& aBuffer ) const
+ {
+ RDebugLog* self = const_cast<RDebugLog*>(this);
+
+ if( self->PrepareForWrite() )
+ {
+ // Call WriteFormat method with argument list
+ iLogFileHandler->HexDump( iClassName8, aFuncName8, aBuffer );
+ self->WriteComplete();
+ } // end if
+
+ } // END HexDump
+
+// ==========================================================================
+// METHOD: PrepareForWrite
+//
+// DESIGN:
+// ==========================================================================
+TBool RDebugLog::PrepareForWrite()
+ {
+ if( !iLogFileHandler )
+ {
+ iLogFileHandler = CDebugLogTlsData::GetLogFileHandler( iLogDirectory, this );
+ } // end if
+
+ return (iLogFileHandler != NULL) && iLogFileHandler->ReadyToWrite();
+
+ } // END PrepareForWrite
+
+// ==========================================================================
+// METHOD: WriteComplete
+//
+// DESIGN:
+// ==========================================================================
+void RDebugLog::WriteComplete()
+ {
+ if( iCloseBetweenWrites )
+ {
+ iLogFileHandler->CloseFile();
+ }
+ else
+ {
+ iLogFileHandler->Flush();
+ } // end if
+
+ } // END WriteComplete
+
+// ==========================================================================
+// METHOD: StaticClose
+//
+// DESIGN:
+// ==========================================================================
+void RDebugLog::StaticClose( TAny* ptr )
+ {
+ RDebugLog* debugLog = reinterpret_cast<RDebugLog*>(ptr);
+ debugLog->Close();
+ } // end StaticClose
+
+// ==========================================================================
+// METHOD: LogLeakedObjects
+//
+// DESIGN:
+// ==========================================================================
+EXPORT_C void RDebugLog::LogLeakedObjects()
+ {
+ CDebugLogTlsData::LogLeakedObjects();
+ }
+
+TPtrC8 RDebugLog::ClassName8()
+ {
+ return iClassName8;
+ }
+
+TUint32 RDebugLog::ObjectAddress()
+ {
+ return (TUint32)iObjectAddress;
+ }
+
+// ---------------
+// CLogFileHandler
+// ---------------
+
+// ==========================================================================
+// METHOD: Constructor
+//
+// DESIGN:
+// ==========================================================================
+CLogFileHandler::CLogFileHandler( RFs& aFs,
+ TDes& aFormatBuffer,
+ TDes8& aOutputBuffer,
+ const TDesC& aDirectoryName,
+ const TDesC& aFileName ) :
+ iFs( aFs ),
+ iFormatBuffer( aFormatBuffer ),
+ iFormatBuffer8( const_cast<TUint8*>(reinterpret_cast<const TUint8*>(aFormatBuffer.Ptr())), 0, aFormatBuffer.MaxLength()*2 ),
+ iOutputBuffer( aOutputBuffer ),
+ iDirectoryName( aDirectoryName ),
+ iFileName( aFileName ),
+ iTryToOpenFile( ETrue ),
+ iFileIsOpen( EFalse ),
+ iFirstTime( ETrue )
+ {
+ } // END CLogFileHandler
+
+// ==========================================================================
+// METHOD: Destructor
+//
+// DESIGN:
+// ==========================================================================
+CLogFileHandler::~CLogFileHandler()
+ {
+//#ifdef __MAKE_COPY_OF_NAMES
+ iObjectNames.ResetAndDestroy();
+//#endif
+ iObjects.Close();
+ iFile.Close();
+ } // END ~CLogFileHandler
+
+// ==========================================================================
+// METHOD: IsHandler
+//
+// DESIGN:
+// ==========================================================================
+TBool CLogFileHandler::IsHandler( const TDesC& aDirectoryName )
+ {
+ return (iDirectoryName.Compare( aDirectoryName ) == 0);
+ } // end IsHandler
+
+// ==========================================================================
+// METHOD: ReadyToWrite
+//
+// DESIGN:
+// ==========================================================================
+TBool CLogFileHandler::ReadyToWrite()
+ {
+ if( iTryToOpenFile )
+ {
+ TryToOpenFile();
+ } // end if
+
+ return iFileIsOpen;
+
+ } // END ReadyToWrite
+
+// ==========================================================================
+// METHOD: TryToOpenFile
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::TryToOpenFile( TBool aCloseFile )
+ {
+ if( !iFileIsOpen )
+ {
+ // Add the directory name to the start of the file name, to avoid
+ // many files with the same name in different directories.
+ TFileName logFileName;
+ logFileName.Copy( KDebugLogsBaseDirectory );
+ logFileName.Append( iDirectoryName );
+ logFileName.Append( KBackslash );
+ logFileName.Append( iDirectoryName );
+ logFileName.Append( KUnderscore );
+ logFileName.Append( iFileName );
+
+ TInt result = iFile.Open( iFs, logFileName, EFileWrite | EFileShareAny );
+
+ if( result == KErrNotFound )
+ {
+ result = iFile.Create( iFs, logFileName, EFileWrite | EFileShareAny );
+ } // end if
+
+ if( iFirstTime )
+ {
+ iFirstTime = EFalse;
+
+ // Put a marker in the file.
+ if( result == KErrNone )
+ {
+ // Write the marker line and software version.
+ TBuf<KSysUtilVersionTextLength> text;
+ SysUtil::GetSWVersion(text);
+
+ const TUint bufSize = KSysUtilVersionTextLength+30;
+ TBuf8<bufSize> text8;
+
+ _LIT8( KMarkerLine, "------------------" );
+
+ text8.Copy( text );
+ text8.Insert( 0, KEndOfLineCharacters8 );
+ text8.Insert( 0, KMarkerLine );
+ text8.Append( KEndOfLineCharacters8 );
+
+ iFile.Write( KMaxTInt, text8 );
+ }
+ }
+
+ if( aCloseFile )
+ {
+ iFile.Close();
+ }
+ else
+ {
+ // Leave the file open.
+ iFileIsOpen = (result == KErrNone );
+ } // end if
+
+ // Only try next time if this open succeeded.
+ iTryToOpenFile = (result == KErrNone );
+
+ } // end if
+
+ } // END TryToOpenFile
+
+// ==========================================================================
+// METHOD: CloseFile
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::CloseFile()
+ {
+ iFile.Close();
+
+ iFileIsOpen = EFalse;
+
+ } // END CloseFile
+
+// ==========================================================================
+// METHOD: Flush
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::Flush()
+ {
+ iFile.Flush();
+
+ } // END Flush
+
+// ==========================================================================
+// METHOD: WriteFormat
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::WriteFormat( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ TRefByValue< const TDesC16 > aFmt,
+ VA_LIST& aList )
+ {
+ TDebugLogBase16Overflow overflow16;
+
+ iFormatBuffer.SetLength( 0 );
+ iFormatBuffer.AppendFormatList( aFmt, aList, &overflow16 );
+
+ Write( aClassName, aFuncName, iFormatBuffer );
+
+ } // END WriteFormat
+
+// ==========================================================================
+// METHOD: WriteFormat
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::WriteFormat( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ TRefByValue< const TDesC8 > aFmt,
+ VA_LIST& aList )
+ {
+ TDebugLogBase8Overflow overflow8;
+
+ iFormatBuffer8.SetLength( 0 );
+ iFormatBuffer8.AppendFormatList( aFmt, aList, &overflow8 );
+
+ Write( aClassName, aFuncName, iFormatBuffer8 );
+
+ } // END WriteFormat
+
+// ==========================================================================
+// METHOD: Write
+//
+// DESIGN: Write the given buffer. Chunk the string if it is longer than
+// KMaxTextPerLine.
+// ==========================================================================
+void CLogFileHandler::Write( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ const TDesC16& aDes )
+ {
+ TRAP_IGNORE( AddTimestampToOutputBufferL() );
+
+ TInt prefixLength = iOutputBuffer.Length();
+
+ TRAP_IGNORE( AddClassAndFunctionToOutputBufferL( aClassName, aFuncName ) );
+
+ TInt curPos = 0;
+
+ while( curPos < aDes.Length() )
+ {
+ TInt lengthToWrite = Min( KMaxTextPerLine - iOutputBuffer.Length(), aDes.Length() - curPos );
+
+ TRAP_IGNORE( WriteLineL( aDes.Mid( curPos, lengthToWrite ) ) );
+
+ iOutputBuffer.SetLength( prefixLength );
+
+ curPos += lengthToWrite;
+
+ } // end while
+
+ } // END Write
+
+// ==========================================================================
+// METHOD: Write
+//
+// DESIGN: Write the given buffer. Chunk the string if it is longer than
+// KDebugLogBufferSize.
+// ==========================================================================
+void CLogFileHandler::Write( const TDesC8& aClassName,
+ const TDesC8& aFuncName,
+ const TDesC8& aDes )
+ {
+ TRAP_IGNORE( AddTimestampToOutputBufferL() );
+
+ TInt prefixLength = iOutputBuffer.Length();
+
+ TRAP_IGNORE( AddClassAndFunctionToOutputBufferL( aClassName, aFuncName ) );
+
+ TInt curPos = 0;
+
+ while( curPos < aDes.Length() )
+ {
+ TInt lengthToWrite = Min( KMaxTextPerLine - iOutputBuffer.Length(), aDes.Length() - curPos );
+
+ TRAP_IGNORE( WriteLineL( aDes.Mid( curPos, lengthToWrite ) ) );
+
+ iOutputBuffer.SetLength( prefixLength );
+
+ curPos += lengthToWrite;
+
+ } // end while
+
+ } // END Write
+
+// ==========================================================================
+// METHOD: AddTimestampToOutputBufferL
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::AddTimestampToOutputBufferL()
+ {
+ // Add timestamp
+
+ TBuf<KDateOrTimeMaxLength> dateTimeBuffer;
+ TTime t;
+ t.HomeTime();
+ t.FormatL(dateTimeBuffer, KDateTimeFormat);
+
+ iOutputBuffer.Copy( dateTimeBuffer );
+
+ } // END AddTimestampToOutputBufferL
+
+// ==========================================================================
+// METHOD: AddClassAndFunctionToOutputBufferL
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::AddClassAndFunctionToOutputBufferL( const TDesC8& aClassName,
+ const TDesC8& aFuncName )
+ {
+ // Add class name & function name
+
+ iOutputBuffer.Append( aClassName.Right( KMaxClassAndFuncNameLength ) );
+ iOutputBuffer.Append( KTwoColons );
+ iOutputBuffer.Append( aFuncName.Right( KMaxClassAndFuncNameLength ) );
+ iOutputBuffer.Append( KColonAndSpace );
+
+ } // END AddClassAndFunctionToOutputBufferL
+
+// ==========================================================================
+// METHOD: WriteLineL
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::WriteLineL( const TDesC16& aSrc )
+ {
+ // Converting from Unicode to Utf8 is overkill for debug logging. Instead, just use the
+ // check-and-dirty Append function, which converts multi-byte characters to char 1.
+ // CnvUtfConverter::ConvertFromUnicodeToUtf8( iOutputBuffer, aSrc );
+
+ iOutputBuffer.Append( aSrc );
+
+ TChar ch;
+ for( TInt i=0; i < iOutputBuffer.Length(); i++ )
+ {
+ ch=iOutputBuffer[i];
+ if(!((ch.IsPrint()) || (ch==KTabChar8)))
+ {
+ iOutputBuffer[i]=KFullStopChar8;
+ } // end if
+ } // end for
+
+ iOutputBuffer.Append( KEndOfLineCharacters8 );
+
+ iFile.Write( KMaxTInt, iOutputBuffer );
+
+ } // END WriteLineL
+
+// ==========================================================================
+// METHOD: WriteLineL
+//
+// DESIGN: Prepend date/time, truncate, and convert unprintable characters to '.'.
+// ==========================================================================
+void CLogFileHandler::WriteLineL( const TDesC8& aSrc )
+ {
+ TInt i = iOutputBuffer.Length();
+
+ iOutputBuffer.Append( aSrc );
+
+ TChar ch;
+ while( i < iOutputBuffer.Length() )
+ {
+ ch=iOutputBuffer[i];
+ if(!((ch.IsPrint()) || (ch==KTabChar8)))
+ {
+ iOutputBuffer[i]=KFullStopChar8;
+ } // end if
+ i++;
+ } // end for
+
+ iOutputBuffer.Append( KEndOfLineCharacters8 );
+
+ iFile.Write( KMaxTInt, iOutputBuffer );
+
+ } // END WriteLineL
+
+// ==========================================================================
+// METHOD: HexDump
+//
+// DESIGN:
+// ==========================================================================
+void CLogFileHandler::HexDump( const TDesC8& /*aClassName*/,
+ const TDesC8& /*aFuncName*/,
+ const TDesC8& aBuffer )
+ {
+ TInt remainingLength = aBuffer.Length();
+ TInt i = 0;
+
+ while( remainingLength > 0 )
+ {
+ const TInt KHexDumpWidth=32;
+ TInt n = Min( KHexDumpWidth, remainingLength );
+
+ // Add the starting byte number.
+ _LIT8(KFirstFormatString,"%04x : ");
+ iFormatBuffer8.Format(KFirstFormatString, i);
+
+ // Add hex values.
+ TInt j;
+ for (j=0; j<n; j++)
+ {
+ _LIT8(KSecondFormatString,"%02x ");
+ iFormatBuffer8.AppendFormat(KSecondFormatString, aBuffer[i+j]);
+ } // end for
+
+ // Fill in incomplete lines.
+ while (j<KHexDumpWidth)
+ {
+ _LIT8(KThreeSpaces," ");
+ iFormatBuffer8.Append(KThreeSpaces);
+ j++;
+ } // end while
+
+ // Add text representation.
+ _LIT8(KTwoSpaces," ");
+ iFormatBuffer8.Append(KTwoSpaces);
+ for (j=0; j<n; j++)
+ {
+ _LIT8(KThirdFormatString,"%c");
+ iFormatBuffer8.AppendFormat( KThirdFormatString, aBuffer[i+j] );
+ } // end for
+
+ iOutputBuffer.SetLength( 0 );
+ TRAP_IGNORE( WriteLineL( iFormatBuffer8 ) );
+
+ remainingLength -= n;
+ i += n;
+
+ } // end while
+
+ } // END HexDump
+
+// ----------------
+// CDebugLogTlsData
+// ----------------
+
+// ==========================================================================
+// METHOD: Instance
+//
+// DESIGN:
+// ==========================================================================
+CDebugLogTlsData* CDebugLogTlsData::Instance()
+ {
+ CDebugLogTlsData* tlsData = NULL;
+
+ if ( !Dll::Tls() )
+ {
+ tlsData = new CDebugLogTlsData;
+
+ if( ( tlsData ) && tlsData->Initialize() )
+ {
+ Dll::SetTls( tlsData );
+ }
+ else
+ {
+ delete tlsData;
+ tlsData = NULL;
+ } // end if
+ }
+ else
+ {
+ tlsData = reinterpret_cast<CDebugLogTlsData*>(Dll::Tls());
+ } // end if
+
+ return tlsData;
+ } // END Instance
+
+// ==========================================================================
+// METHOD: Constructor
+//
+// DESIGN:
+// ==========================================================================
+CDebugLogTlsData::CDebugLogTlsData() : CActive( EPriorityNormal )
+ {
+ CActiveScheduler::Add(this);
+ } // END constructor
+
+// ==========================================================================
+// METHOD: Destructor
+//
+// DESIGN:
+// ==========================================================================
+CDebugLogTlsData::~CDebugLogTlsData()
+ {
+ Cancel();
+ iFs.Close();
+ iFiles.Close();
+ } // END ~CDebugLogTlsData
+
+// ==========================================================================
+// METHOD: Initialize
+//
+// DESIGN:
+// ==========================================================================
+TBool CDebugLogTlsData::Initialize()
+ {
+ TInt returnValue = ( iFs.Connect() == KErrNone );
+
+ if( returnValue )
+ {
+ // Request notification of directory adds/deletes in the logs directory.
+ iFs.NotifyChange( ENotifyDir, iStatus, KDebugLogsBaseDirectory );
+ SetActive();
+
+ // Dynamically create the name of the log files based on the application name
+ // and thread ID. This will eliminate multiple processes/thread usage of
+ // this debug logging infrastructure from scribbling each others traces.
+ RProcess thisProcess;
+ RThread thisThread;
+
+ // The file name is the process name followed by the thread ID.
+ TParsePtrC fileNameParser( thisProcess.FileName() );
+ iFileName.Copy( fileNameParser.Name() );
+ iFileName.Append( KUnderscore );
+ iFileName.Append( thisThread.Name() );
+ iFileName.Append( KDebugLogFileExt );
+
+ } // end if
+
+ return returnValue;
+
+ } // END Initialize
+
+// ==========================================================================
+// METHOD: FreeIfNecessary
+//
+// DESIGN:
+// ==========================================================================
+void CDebugLogTlsData::FreeIfNecessary()
+ {
+ CDebugLogTlsData* tlsData;
+
+ tlsData = reinterpret_cast<CDebugLogTlsData*>(Dll::Tls());
+
+ if( tlsData->iFiles.Count() == 0 )
+ {
+ delete tlsData;
+ Dll::SetTls( NULL );
+ }
+
+ } // END FreeIfNecessary
+
+// ==========================================================================
+// METHOD: RunL
+//
+// DESIGN:
+// ==========================================================================
+void CDebugLogTlsData::RunL()
+ {
+ if( iStatus == KErrNone )
+ {
+ // Try to open each file, just in case the modified directory was
+ // for one of the log files.
+ for( TInt i = 0; i < iFiles.Count(); i++ )
+ {
+ iFiles[i]->TryToOpenFile( ETrue );
+ } // end for
+
+ iFs.NotifyChange( ENotifyDir, iStatus, KDebugLogsBaseDirectory );
+ SetActive();
+
+ } // end if
+
+ } // END RunL
+
+// ==========================================================================
+// METHOD: DoCancel
+//
+// DESIGN:
+// ==========================================================================
+void CDebugLogTlsData::DoCancel()
+ {
+ iFs.NotifyChangeCancel();
+
+ } // END DoCancel
+
+// ==========================================================================
+// METHOD: GetLogFileHandler
+//
+// DESIGN:
+// ==========================================================================
+CLogFileHandler* CDebugLogTlsData::GetLogFileHandler( const TDesC& aDirectory, RDebugLog* aObject )
+ {
+ CLogFileHandler* returnValue = NULL;
+
+ CDebugLogTlsData* tlsData = CDebugLogTlsData::Instance();
+
+ if( tlsData )
+ {
+ returnValue = tlsData->DoGetLogFileHandler( aDirectory, aObject );
+ } // end if
+
+ return returnValue;
+ } // END GetLogFileHandler
+
+// ==========================================================================
+// METHOD: ReleaseLogFileHandler
+//
+// DESIGN:
+// ==========================================================================
+void CDebugLogTlsData::ReleaseLogFileHandler( CLogFileHandler* aHandler, RDebugLog* aObject )
+ {
+ if ( Dll::Tls() )
+ {
+ CDebugLogTlsData* tlsData;
+
+ tlsData = reinterpret_cast<CDebugLogTlsData*>(Dll::Tls());
+
+ tlsData->DoReleaseLogFileHandler( aHandler, aObject );
+ } // end if
+
+ } // END ReleaseLogFileHandler
+
+// ==========================================================================
+// METHOD: DoGetLogFileHandler
+//
+// DESIGN:
+// ==========================================================================
+CLogFileHandler* CDebugLogTlsData::DoGetLogFileHandler( const TDesC& aDirectory, RDebugLog* aObject )
+ {
+ CLogFileHandler* handler = NULL;
+
+ // Look for the log file handler for the given directory.
+ TInt index = 0;
+ while( index < iFiles.Count() && ( !handler ) )
+ {
+ CLogFileHandler* currentHandler = iFiles[index];
+ if( currentHandler->IsHandler( aDirectory ) )
+ {
+ handler = currentHandler;
+ }
+ else
+ {
+ index++;
+ } // end if
+ } // end while
+
+ if( !handler )
+ {
+ handler = new CLogFileHandler( iFs, iFormatBuffer, iOutputBuffer, aDirectory, iFileName );
+
+ if( handler )
+ {
+ iFiles.Append( handler );
+ } // end if
+ }
+
+ if( handler )
+ {
+ // Handler already exists. Add object to list of objects.
+//#ifdef __MAKE_COPY_OF_NAMES
+ HBufC8* copyOfName = HBufC8::New( aObject->ClassName8().Length() );
+ if ( copyOfName )
+ {
+ copyOfName->Des().Copy( aObject->ClassName8() );
+ handler->iObjectNames.Append( copyOfName );
+ }
+//#endif
+
+ handler->iObjects.Append( aObject );
+ } // end if
+
+ return handler;
+
+ } // END DoGetLogFileHandler
+
+// ==========================================================================
+// METHOD: DoReleaseLogFileHandler
+//
+// DESIGN:
+// ==========================================================================
+void CDebugLogTlsData::DoReleaseLogFileHandler( CLogFileHandler* aHandler, RDebugLog* aObject )
+ {
+ TInt index = aHandler->iObjects.Find( aObject );
+
+ if( index >= 0 )
+ {
+//#ifdef __MAKE_COPY_OF_NAMES
+ delete aHandler->iObjectNames[index];
+ aHandler->iObjectNames.Remove(index);
+//#endif
+ aHandler->iObjects.Remove( index );
+
+ if( aHandler->iObjects.Count() == 0 )
+ {
+ delete aHandler;
+
+ iFiles.Remove( iFiles.Find(aHandler) );
+ } // end if
+ }
+ else
+ {
+ // Something is wrong. The most likely case is incorrect usage of the debug log macros.
+ _LIT8( KDummyFuncName, "***" );
+ _LIT8( KErrorText, "INCORRECT DEBUG LOG USAGE!" );
+
+ aObject->Write( KDummyFuncName, KErrorText );
+ }
+
+ } // END DoReleaseLogFileHandler
+
+// ==========================================================================
+// METHOD: LogLeakedObjects
+//
+// DESIGN:
+// ==========================================================================
+void CDebugLogTlsData::LogLeakedObjects()
+ {
+ if( Dll::Tls() )
+ {
+ // If the TLS data wasn't cleaned up then there was a leak, either due
+ // to incorrect usage of the logger or due to the leak of an object that
+ // is using the logger.
+ Instance()->DoLogLeakedObjects();
+ }
+ }
+
+// ==========================================================================
+// METHOD: DoLogLeakedObjects
+//
+// DESIGN:
+// ==========================================================================
+void CDebugLogTlsData::DoLogLeakedObjects()
+ {
+ for( TInt i = 0; i < iFiles.Count(); i++ )
+ {
+ // There are still objects hanging around. Something leaked, or the log macros
+ // were not properly used.
+
+ _LIT8( KDebugLogClassName, "DebugLogger" );
+ _LIT8( KDebugLogFunctionNameName, "DoLogLeakedObjects" );
+ _LIT8( KDebugLogErrorText, "LEAKS DETECTED!" );
+ const TUint bufSize = 60;
+
+ TBuf8<bufSize> tempBuffer;
+
+ if( iFiles[i]->ReadyToWrite() )
+ {
+ iFiles[i]->Write( KDebugLogClassName, KDebugLogFunctionNameName, KDebugLogErrorText );
+
+ for( TInt j = 0; j < iFiles[i]->iObjects.Count(); j++ )
+ {
+ _LIT8( KLeakFmt, "Leak #%i" );
+ tempBuffer.Format( KLeakFmt, j+1 );
+ iFiles[i]->Write( KDebugLogClassName, KDebugLogFunctionNameName, tempBuffer );
+ _LIT8( KAdressFmt, "address=0x%x" );
+ tempBuffer.Format( KAdressFmt(), iFiles[i]->iObjects[j]->ObjectAddress() );
+ iFiles[i]->Write( KDebugLogClassName, KDebugLogFunctionNameName, tempBuffer );
+
+//#ifdef __MAKE_COPY_OF_NAMES
+ TPtrC8 className( iFiles[i]->iObjectNames[j]->Des() );
+//#else
+// TPtrC8 className( iFiles[i]->iObjects[j]->ClassName8() );
+//#endif
+ _LIT8( KClassFmt, "class=%S" );
+ tempBuffer.Format(KClassFmt() , &className );
+ iFiles[i]->Write( KDebugLogClassName, KDebugLogFunctionNameName, tempBuffer );
+ }
+ iFiles[i]->CloseFile();
+ }
+ }
+ }
+
+// ----------------------
+// TDebugLogBase8Overflow
+// ----------------------
+
+// ==========================================================================
+// METHOD: Overflow
+//
+// DESIGN: Handle 8-bit descriptor overflow from the 8-bit formatter.
+// ==========================================================================
+void TDebugLogBase8Overflow::Overflow( TDes8& /* aDes */ )
+ {
+ // NOOP; nothing we can do. The descriptor parameter is the
+ // string that has already been printed, not the remainder that
+ // hasn't been printed yet.
+
+ } // END Overflow
+
+// -----------------------
+// TDebugLogBase16Overflow
+// -----------------------
+
+// ==========================================================================
+// METHOD: Overflow
+//
+// DESIGN: Handle 16-bit descriptor overflow from the 16-bit formatter.
+// ==========================================================================
+void TDebugLogBase16Overflow::Overflow( TDes16& /* aDes */ )
+ {
+ // NOOP; nothing we can do. The descriptor parameter is the
+ // string that has already been printed, not the remainder that
+ // hasn't been printed yet.
+
+ } // END Overflow
+
+// END FILE DebugLog.cpp
+