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:.
while( m_SrcFile.ReadString( csLine ))
{
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
{
}
}
else
{
if( -1 != csLine.Find( MULTI_LINE_COMMENT_END_SYMBOL ))
{
bMultiLineComment = false;
continue;
}
else
{
continue;
}
}
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
{
}
}
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;
}
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 );
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...
void JesFileProcessor::UpdateTraces( const CString& csLogMethod_i )
...is followed by methods...
void JesFileProcessor::UpdateLine( DWORD dwLinePos_i, const CString& csUpdateLine_i )
... and...
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:
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