Hi,
I wrote a StackTrace class to use for debugging that would show me a full listing of which functions are called and in what order...
It works (mostly), but I'm wondering if anyone has any better ideas of how to implement it?
Since it should be multi-thread safe, I use CriticalSections around the logging areas; and since I don't want to keep opening & closing the output file, I open it once when I initialize the class and close it when I uninitialize it. It's designed to be Initialized when the program starts, then Uninitialized at the very end of your program. Here's the code:
Code:
// StackTrace.h - Interface for the StackTrace class.
///////////////////////////////////////////////////////////////////////////////
#ifndef STACKTRACE_H_INCLUDED_APR_1_2008
#define STACKTRACE_H_INCLUDED_APR_1_2008
#include <windows.h>
#include <fstream>
#include <string>
class StackTrace
{
public:
StackTrace( const std::string& func );
~StackTrace();
const std::string& FunctionName() const;
static bool Initialize( const std::string& traceFile );
static bool Uninitialize();
private:
const std::string m_Func;
static std::ofstream m_TraceFile;
static CRITICAL_SECTION m_CS;
static unsigned int m_Level;
StackTrace( const StackTrace& );
StackTrace& operator=( const StackTrace& );
};
#endif // STACKTRACE_H_INCLUDED_APR_1_2008
Code:
// StackTrace.cpp - Implementation for the StackTrace class.
///////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include "StackTrace.h"
// Define static member variables.
std::ofstream StackTrace::m_TraceFile;
CRITICAL_SECTION StackTrace::m_CS;
unsigned int StackTrace::m_Level = 0;
StackTrace::StackTrace( const std::string& func )
: m_Func( func )
{
#ifdef GET_STACK_TRACE
EnterCriticalSection( &m_CS );
if ( m_TraceFile.is_open() == true )
{
m_TraceFile << "Entering: [" << m_Level << "] " << m_Func << std::endl;
}
++m_Level;
LeaveCriticalSection( &m_CS );
#endif // GET_STACK_TRACE
}
StackTrace::~StackTrace()
{
#ifdef GET_STACK_TRACE
EnterCriticalSection( &m_CS );
--m_Level;
if ( m_TraceFile.is_open() == true )
{
m_TraceFile << "Exiting: [" << m_Level << "] " << m_Func << std::endl;
}
LeaveCriticalSection( &m_CS );
#endif // GET_STACK_TRACE
}
const std::string&
StackTrace::FunctionName() const
{
return m_Func;
}
// static
bool
StackTrace::Initialize( const std::string& traceFile )
{
#ifdef GET_STACK_TRACE
bool ret = m_TraceFile.is_open();
if ( ret == false )
{
m_TraceFile.open( traceFile.c_str() );
if ( m_TraceFile.is_open() == true )
{
InitializeCriticalSection( &m_CS );
# if (_WIN32_WINNT >= 0x0403)
SetCriticalSectionSpinCount( &m_CS, 4000 );
# endif // _WIN32_WINNT
ret = true;
}
}
return ret;
#else
return true;
#endif // GET_STACK_TRACE
}
// static
bool
StackTrace::Uninitialize()
{
#ifdef GET_STACK_TRACE
if ( m_TraceFile.is_open() == true )
{
m_TraceFile.close();
DeleteCriticalSection( &m_CS );
}
#endif // GET_STACK_TRACE
return true;
}
The problem I've noticed so far is when I use the class before I call the static Initialize() function, such as in constructors of objects which are global variables...
Here's how you'd typically use this class:
Code:
#include "StackTrace.h"
void Func1()
{
StackTrace trace( "Func1()" );
...
}
void Func2()
{
StackTrace trace( "Func2()" );
...
}
int main()
{
StackTrace::Initialize( "C:\\TraceFile.txt" );
Func1();
Func2();
...
StackTrace::Uninitialize();
return 0;
}