Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

JesTracePaster - Tool to Add Method-entry Logs in Source Files

5.00/5 (4 votes)
11 Mar 2009CPOL3 min read 22.3K   208  
This simple tool will add method-entry logs in source files.
Image 1

Introduction

JesTracePaster is a simple application that can be used to add method-entry logs in source files. Such a tool comes handy when you have large amount of code of which you need to learn operational sequences. It would be quite time-consuming to add traces in all methods, manually. This tiny tool, JesTracePaster, can help you in such situations.

Background

One of my friends received a sample Imaging application for reference. Though it was a sample, the code was fairly complex and the technology was not quite familiar for him. So he decided that he could add traces to around 500 source files and check sequence of operations so that he could get an idea about the main entities of the application. But adding traces to 500 files itself might take one week. At that time, I was spending efforts on developing JesInclAnalyzer (A tool to remove unwanted header file includes from source files; already posted on CodeProject) and found that some of the classes in JesInclAnalyzer can be used (with some changes) to develop a simple application that can add method-entry logs to class-methods in source files.

Using the Tool/Code

Simple file operations and text processing are used to implement JesTracePaster. In "Log Macro/function/method" edit box, enter macro, function or method that is used for logging (E.g.: OutputDebugString). If this edit box is left blank, OutputDebugString will be used for logging. In "Sources" edit box, enter paths to source files that are to be updated. Multiple paths can be specified by separating with ";". Click on "Update" button. The tool will update source files by adding method-entry logs. Processing status will be updated in the main dialog. Please note that the tool will not process "Read-only" files. It will give "Access denied" error message for "Read-only" files. Also, the tool does not work for C-style functions and methods defined in header files.

At first, the tool creates a list of source files available in given paths with the help of JesCPPIterator class. Method bool JesCPPIterator::PopulateFileList( const CStringArray& Paths_i, bool bSrcFiles_i ) uses CFileFind to create list of source files. Source files are kept in CStringArray member and method bool JesCPPIterator::GetNextFile( CString& csSrcFile_o ) returns filenames one by one. I have no good reason to give you why I did not derive this class from CStringArray instead of wrapping it up. What I did may not be a good design.

Class JesFileProcessor is responsible for updating files with traces. Method void JesFileProcessor::UpdateTraces( const CString& csLogMethod_i ), skips single-line and multi-line comments for efficiency. This method checks for "::[MethodName](" pattern to fix method starting; checks for "{" to fix log-insertion point. Once an entry-log is written, code-lines will be skipped until method block is terminated. This is done based on pairing of braces. The following code-snippet implements this logic:.

C++
while( m_SrcFile.ReadString( csLine ))
{
    // Mark and skip Multi-line comment start
    static const CString MULTI_LINE_COMMENT_END_SYMBOL = _T( "*/" );
    if( !bMultiLineComment )
    {
        static const CString MULTI_LINE_COMMENT_SYMBOL = _T( "/*" );
        csLine.TrimLeft();
        csLine.TrimRight();
        int nMLCmntStartIdx = csLine.Find( MULTI_LINE_COMMENT_SYMBOL );
        int nMLCmntEndIdx = csLine.Find( MULTI_LINE_COMMENT_END_SYMBOL );
        if( ( -1 != nMLCmntStartIdx ) &&
            ( -1 == nMLCmntEndIdx ))
        {
            bMultiLineComment = true;
            continue;
        }
        else if( ( 0 == nMLCmntStartIdx ) &&
                 (( csLine.GetLength() - 1 ) == nMLCmntEndIdx ))
        {
            continue;
        }
        else
        {
            // Do nothing
        }
    }
    else
    {
        if( -1 != csLine.Find( MULTI_LINE_COMMENT_END_SYMBOL ))
        {
            bMultiLineComment = false;
            continue;
        }
        else
        {
            continue;
        }
    }
     // Skip single line comments
    static const CString SINGLE_LINE_COMMENT_SYMBOL = _T( "//" );
    if( -1 != csLine.Find( SINGLE_LINE_COMMENT_SYMBOL ))
    {
        continue;
    }
     const static TCHAR EXP_LINE_END = _T( ';' );
    static const TCHAR BLOCK_START = _T( '{' );
    if( nBraceCnt )
    {
        static const TCHAR BLOCK_END = _T( '}' );
        int nBlockStartIdx = csLine.Find( BLOCK_START );
        int nBlockEndIdx = csLine.Find( BLOCK_END );
        bool bBracePairs = (( -1 != nBlockStartIdx ) && ( -1 != nBlockEndIdx )) ||
                           (( -1 == nBlockStartIdx ) && ( -1 == nBlockEndIdx ));
        if( !bBracePairs )
        {
            if( -1 != nBlockStartIdx )
            {
                ++nBraceCnt;
            }
            else if( -1 != nBlockEndIdx )
            {
                --nBraceCnt;
            }
            else
            {
                // Do nothing
            }
        }
         continue;
    }
     if( bLogMsgCreated )
    {
        if( -1 != csLine.Find( BLOCK_START ))
        {
            DWORD dwLinePos = m_SrcFile.GetPosition();
            UpdateLine( dwLinePos, csLogMsg );
            csLogMsg.Empty();
            m_SrcFile.Seek( m_dwCurPos, CFile::begin );
            bLogMsgCreated = false;
            nBraceCnt = 1;
        }
         continue;
    }
     // Check method start
    const static CString SCOPE_SYMBOL = _T( "::" );
    int nScopeStartIdx = csLine.Find( SCOPE_SYMBOL );
    if( ( -1 != nScopeStartIdx ) &&
        ( -1 == csLine.Find( EXP_LINE_END )))
    {
        static const TCHAR METHOD_START = _T( '(' );
        int nMethodStartIdx = csLine.Find( METHOD_START );
        if( nScopeStartIdx > nMethodStartIdx )
        {
            continue;
        }
         static const TCHAR DATA_TYPE_DELIMITER = _T( ' ' );
        static const TCHAR METHOD_END = _T( ')' );
        static const TCHAR DOUBLE_INVERTED_QUOTE = _T( '\"' );
        static const CString BLOCK_INDENTATION = _T( "    " );
        int nMethodNameStartIdx = csLine.Find( DATA_TYPE_DELIMITER );
         // For constructors
        if( ( -1 == nMethodNameStartIdx ) ||
            ( nMethodNameStartIdx > nMethodStartIdx ))
        {
            nMethodNameStartIdx = 0;
        }
        else
        {
            nMethodNameStartIdx += 1;
        }
        CString csMethodName = csLine.Mid( nMethodNameStartIdx,
                                           nMethodStartIdx - nMethodNameStartIdx );
        csLogMsg.Format( _T( "%s%s%c %s %c%s%c %c%c%c" ), 
			BLOCK_INDENTATION, csLogMethod_i, METHOD_START,
                         	_T( "_T(" ), DOUBLE_INVERTED_QUOTE, 
			csMethodName, DOUBLE_INVERTED_QUOTE,
                         	METHOD_END, METHOD_END, EXP_LINE_END );
        bLogMsgCreated = true;
    }
}

Points of Interest

I lost considerable amount of time checking for a bug. I found this bug when I tested JesTracePaster with its own source files. In JesFileProcessor.cpp, the method...

C++
void JesFileProcessor::UpdateTraces( const CString& csLogMethod_i ) 

...is followed by methods...

C++
void JesFileProcessor::UpdateLine( DWORD dwLinePos_i, const CString& csUpdateLine_i ) 

... and...

C++
void JesFileProcessor::ShowFileException()

The behaviour of the bug was that entry-logs will never be inserted in the last two methods. The reason for the bug was the following code-line:

C++
static const CString MULTI_LINE_COMMENT_SYMBOL = _T( "/*" );

I have not fixed this bug thinking that it is a very rare possibility in normal conditions. It is left as an exercise for readers.

History

  • Version 1.0 - 11-Mar-2009

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)