Introduction
StackTracer
is a C++ class which enables you to get the call stack conveniently using Windows SEH, and then you can either show the exception message or log it to help you diagnose the problem. It works both in Debug and Release modes as long as you have the PDB files. (It might be not that accurate in Release mode as functions might be optimized out.)
Background
We use Windows' SEH (Structured Exception Handling) to handle exceptions in our application, using the __try
, __except
statements. We do this to protect our applications from crashing accidently. But this also hides the details of the problem, as we can only see the external operation that failed, but don't know what happened inside without debugging into the code.
With this StackTracer
class integrated with the __try
, __except
statements, we can now pinpoint the problem with the callstack quickly. Whenever there is an exception, the detailed information will be logged, and we just need to open the log file, and then everything will be clear.
Using the code
We use the StackTracer
as a static class; it has four public members:
static LONG ExceptionFilter(LPEXCEPTION_POINTERS e);
This function should be called in the __except
clause; it takes the EXCEPTION_POINTERS
structure as the context and works through the stack frame to get the call stack. Then, it will just return EXCEPTION_EXECUTE_HANDLER
to execute the exception handler - the __except
block.
static std::string GetExceptionMsg();
Return the exception information (exception code, call stack) with the format this class provides: as you can see at the beginning of the page.
static DWORD GetExceptionCode();
static std::vector<FunctionCall> GetExceptionCallStack();
There are two functions that return the data of the exception. If you don't like the default format provided by GetExceptionMsg
, you can make your own.
The call stack depth is set to 6, as I believe it will give us enough information to diagnose the problem, but you can also change following line to get deeper:
const int CALLSTACK_DEPTH = 6;
To use this class, you can integrate it with any important function you want to monitor, or simply, just wrap the Main
function, thus all the functions will be detected.
Here is how you use it:
Suppose you have a function which accomplishes a large operation:
void LargeOperation()
{
}
You want to protect this function using SEH, but also want to get the call stack when there is an exception. You can integrate it with StackTracer
.
void PopupCallStack()
{
std::string str = StackTracer::GetExceptionMsg();
AfxMessageBox(CString(str.c_str()));
}
void LogCallStack()
{
std::string str = StackTracer::GetExceptionMsg();
Log(str.c_str());
}
void LargeOperation()
{
__try
{
}
__except(StackTracer::ExceptionFilter(GetExceptionInformation()))
{
PopupCallStack();
}
}
Explanation
This class take the EXCEPTION_POINTERS
structure that is generated when an exception is raised, then works through the stack frames in the exception context to get the call stack. It needs symbol files to get the detailed information, such as function name and line number. Below is a skeleton of the core idea:
<a href="http://msdn.microsoft.com/en-us/library/ms681351%28VS.85%29.aspx">SymInitialize</a>(...);
STACKFRAME64 sf;
While(<a href="http://msdn.microsoft.com/en-us/library/ms680650%28VS.85%29.aspx">StackWalk64</a>(...))
{
SYMBOL_INFO symbolInfo;
<a href="http://msdn.microsoft.com/en-us/library/ms681323%28VS.85%29.aspx">SymFromAddr</a>(...);
IMAGEHLP_LINE64 lineInfo;
<a href="http://msdn.microsoft.com/en-us/library/ms681330%28VS.85%29.aspx">SymGetLineFromAddr64</a>(...);
}
<a href="http://msdn.microsoft.com/en-us/library/ms680696%28VS.85%29.aspx">SymCleanup</a>();
First, it initializes the symbol handler, loads the necessary symbol files, and then it works though the stack frame upwards, from the most recent to the older ones. StackWalk64
returns true
if it gets the previous stack frame successfully. Then, SymFromAddr
and SymGetLineFromAddr64
will get the function name and the line number in the frame.
History
- 8/30/2009: Initial version.