Code Snippet
// TestLibDebug.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <stdlib.h>
#include <malloc.h>
#include <psapi.h>
#include "dbghelp.h"
#define STACK_TRACE_BUFFER_SIZE (1024)
#define DEBUG_SYMBOL_LENGTH_MAX (512)
#define TEST_COUNT_MAX 100000
// Defines a simple exception for managing errors. Includes stack trace capability.
class SimpleException
{
public:
// Default constructor.
SimpleException();
// Constructor that optionally does a stack trace.
SimpleException( bool a_bSkipStackTrace );
// Returns a pointer to the member stack trace buffer.
LPTSTR GetStackTrace();
// Do and save a stack trace.
void SetStackTrace();
private:
// Holds the stack trace results in string form.
TCHAR m_pszStackTraceBuffer[ STACK_TRACE_BUFFER_SIZE ];
};
// Wraps access to dbghelp lib calls.
CRITICAL_SECTION g_csProtectDbgHelp;
// If a_pszFilename is a string, returns a pointer to the substring that represents just the filename.
LPTSTR StripFilenamePath( LPTSTR a_pszFilename );
// Sets up the dbghelp library for access.
void InitializeDbgHelp();
// Brings down the dbghelp library.
void DeinitializeDbgHelp();
// Look up symbol info on the specified address.
void GetAddressDescription( DWORD64 a_dw64Address );
// Execute a stack trace.
void GetStackTrace( int a_nFrameDepthEnd );
// Simple pass-through functions for testing.
void Sub1();
void Sub2();
void Sub3();
// Application entry point.
int _tmain(int argc, _TCHAR* argv[])
{
InitializeDbgHelp();
for( int l_nCount = 0; l_nCount < TEST_COUNT_MAX; l_nCount++ )
{
printf( "Count: %6d\n", l_nCount );
Sub1();
}
DeinitializeDbgHelp();
return 0;
}
void Sub1()
{
Sub2();
}
void Sub2()
{
Sub3();
}
void Sub3()
{
// Test 1: Memory leak. SetStackTrace() called in constructor.
SimpleException l_Exception1(true);
//// Test 2: No memory leak. SetStackTrace() called after constructor.
//SimpleException l_Exception2(false);
//l_Exception2.SetStackTrace();
}
// Execute a stack trace.
void GetStackTrace( int a_nFrameDepthEnd )
{
// All "dbghelp" library functions must be accessed synchronously.
EnterCriticalSection( &g_csProtectDbgHelp );
// Set up structures for StackWalk64().
CONTEXT l_ContextRecord;
memset(&l_ContextRecord, 0, sizeof(l_ContextRecord ));
STACKFRAME64 l_StackFrame64;
memset(&l_StackFrame64, 0, sizeof(l_StackFrame64));
l_ContextRecord.ContextFlags = CONTEXT_FULL;
__asm
{
call NextInstruction
NextInstruction: pop eax
mov l_ContextRecord.Eip, eax // Program counter.
mov l_ContextRecord.Ebp, ebp // Frame base pointer.
mov l_ContextRecord.Esp, esp // Stack pointer.
} // end __asm
l_StackFrame64.AddrPC.Offset = l_ContextRecord.Eip; // Program counter.
l_StackFrame64.AddrPC.Mode = AddrModeFlat; // Virtual address (flat addressing).
l_StackFrame64.AddrFrame.Offset = l_ContextRecord.Ebp; // Frame base pointer.
l_StackFrame64.AddrFrame.Mode = AddrModeFlat; // Virtual address (flat addressing).
l_StackFrame64.AddrStack.Offset = l_ContextRecord.Esp; // Stack pointer.
l_StackFrame64.AddrStack.Mode = AddrModeFlat; // Virtual address (flat addressing).
// Step through the stack frames and dump what we can until we hit the top of the stack
// or we're prevented from going any further.
for( int l_nFrameCount = 0; l_nFrameCount < a_nFrameDepthEnd + 1; l_nFrameCount++ )
{
// Get information about the current stack frame and advance to the next frame in the stack.
BOOL l_bSuccess = StackWalk64(
IMAGE_FILE_MACHINE_I386,
GetCurrentProcess(),
GetCurrentThread(),
&l_StackFrame64,
&l_ContextRecord,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL );
// Check if we advanced to a valid frame.
if( (!l_bSuccess) || (0 == l_StackFrame64.AddrPC.Offset) || (0 == l_StackFrame64.AddrReturn.Offset) )
{
// We've traced to the end of the stack or into outside memory, which are normal trace
// termination conditions, or StackWalk64() failed in some other way. Either way we're
// done with the trace.
break;
}
// Look up the symbol info for the frame address.
GetAddressDescription( l_StackFrame64.AddrPC.Offset );
} // for
LeaveCriticalSection( &g_csProtectDbgHelp );
}
// Look up symbol info for the specified address.
void GetAddressDescription( DWORD64 a_dw64Address )
{
// All "dbghelp" library functions must be accessed synchronously.
EnterCriticalSection( &g_csProtectDbgHelp );
// Make sure the address is valid.
if( 0 != a_dw64Address )
{
// l_pSymbol64 is used by SymGetSymFromAddr64() to return the lookup result to us.
// It needs an additional string buffer grafted onto it.
IMAGEHLP_SYMBOL64 * l_pSymbol64 = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + DEBUG_SYMBOL_LENGTH_MAX);
memset(l_pSymbol64, 0, sizeof(IMAGEHLP_SYMBOL64) + DEBUG_SYMBOL_LENGTH_MAX);
l_pSymbol64->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
l_pSymbol64->MaxNameLength = DEBUG_SYMBOL_LENGTH_MAX - 1;
// Do the lookup.
DWORD64 l_dw64Displacement;
BOOL l_bSuccess = SymGetSymFromAddr64(
GetCurrentProcess(),
a_dw64Address,
&l_dw64Displacement,
l_pSymbol64 );
// Free the symbol info structure.
free(l_pSymbol64);
l_pSymbol64 = NULL;
}
LeaveCriticalSection( &g_csProtectDbgHelp );
}
///////////////////////////////////////////////////////////////////////////
//
// StripFilenamePath()
//
// If a_pszFilename represents a file name with or without a prepending path,
// then return a pointer to the start of the filename.
//
LPTSTR StripFilenamePath( LPTSTR a_pszFilename )
{
// Walk backwards through the string until a path separation
// character or the beginning of the string is found.
size_t l_nSize = _tcslen(a_pszFilename);
size_t l_nCharIdx = l_nSize - 1;
while( (l_nCharIdx >= 0) && (l_nCharIdx < l_nSize) )
{
if( '\\' == a_pszFilename[l_nCharIdx] )
break;
l_nCharIdx--;
}
// Return a pointer to the character just after the path separator,
// or the beginning of the string.
return &(a_pszFilename[l_nCharIdx + 1]);
}
void InitializeDbgHelp()
{
// Use a critical section to synchronize access to all "dbghelp" library functions.
InitializeCriticalSection( &g_csProtectDbgHelp );
// All dbghelp functions must be accessed synchronously.
EnterCriticalSection( &g_csProtectDbgHelp );
//// Set debug symbols to load on-demand.
//SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
// Set all symbols to load up front. Load symbols without compiler decorations.
SymSetOptions(SYMOPT_UNDNAME);
// Get the path of the currently running executable.
TCHAR l_pszExecutablePath[MAX_PATH];
l_pszExecutablePath[0] = (TCHAR) 0;
DWORD l_dwSuccess = GetModuleFileNameEx(
GetCurrentProcess(),
NULL,
l_pszExecutablePath,
MAX_PATH );
if( l_dwSuccess )
{
// Get just the path of the executable. Null-terminate the path string
// where the filename begins.
LPTSTR l_pszExecutableFileName = StripFilenamePath( l_pszExecutablePath );
l_pszExecutableFileName[0] = (TCHAR) 0;
// Search for the symbol file in the executable's path. Note this does a
// recursive directory search, so be careful the executable's path doesn't
// sit at the top of a very very large directory tree.
SymInitialize(GetCurrentProcess(), l_pszExecutablePath, TRUE);
}
else
{
// Couldn't get the executable's path. Look in the system default locations for
// the symbol file.
SymInitialize(GetCurrentProcess(), NULL, TRUE);
}
// Done accessing dbghelp functions.
LeaveCriticalSection( &g_csProtectDbgHelp );
}
///////////////////////////////////////////////////////////////////////
//
// Clean up the dbghelp library support.
//
void DeinitializeDbgHelp()
{
// All "dbghelp" library functions must be accessed synchronously.
EnterCriticalSection( &g_csProtectDbgHelp );
// Release debug resources.
SymCleanup(GetCurrentProcess);
// Done accessing dbghelp functions.
LeaveCriticalSection( &g_csProtectDbgHelp );
// Presumably we're coming down and there will be no more access to the
// dbghelp library from anywhere.
DeleteCriticalSection( &g_csProtectDbgHelp );
}
// Default constructor.
SimpleException::SimpleException()
{
m_pszStackTraceBuffer[0] = (TCHAR) 0;
}
// Constructor that optionally does a stack trace.
SimpleException::SimpleException( bool a_bSaveStackTrace )
{
m_pszStackTraceBuffer[0] = (TCHAR) 0;
if( a_bSaveStackTrace )
{
SetStackTrace();
}
}
// Return a pointer to the member stack trace buffer.
LPTSTR SimpleException::GetStackTrace()
{
return m_pszStackTraceBuffer;
}
// Execute a stack trace and save the results in the member stack trace buffer.
void SimpleException::SetStackTrace()
{
::GetStackTrace( 200 );
}