I am using the following code to walk the stack on an exception (note: you must run it in release in order to properly receive the desired output of the stack trace to the console, not in debug mode or else it will only show a popup):
#include "stdafx.h"
#include <process.h>
#include <iostream>
#include <Windows.h>
#include "dbghelp.h"
using namespace std;
#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
#define TRACE_LOG_ERRORS FALSE
#define TRACE_DUMP_NAME L"Exception.dmp"
void function2()
{
int a = 0;
int b = 0;
throw new exception;
}
void function1()
{
int a = 0;
function2();
}
void function0()
{
function1();
}
static void threadFunction(void *param)
{
function0();
}
LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS exception)
{
CONTEXT context = *(exception->ContextRecord);
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
STACKFRAME64 frame;
memset(&frame, 0, sizeof(STACKFRAME64));
DWORD image;
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD displacement;
SymInitialize(process, NULL, TRUE);
while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
{
if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
{
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
{
printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
}
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymFromAddr: %lu.\n", GetLastError());
}
}
DWORD error = GetLastError();
if (error && TRACE_LOG_ERRORS)
{
printf("Error from StackWalk64: %lu.\n", error);
}
HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
exceptionInformation.ThreadId = GetCurrentThreadId();
exceptionInformation.ExceptionPointers = exception;
exceptionInformation.ClientPointers = FALSE;
if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL, NULL, NULL))
{
printf("Wrote a dump.");
}
return EXCEPTION_CONTINUE_SEARCH;
}
int _tmain(int argc, _TCHAR* argv[])
{
SetUnhandledExceptionFilter(UnhandledExceptionFilter);
_beginthread(threadFunction, 0, NULL);
printf("Press any key to exit.\n");
cin.get();
return 0;
}
Output:
Press any key to exit.
at threadFunction in c:\users\<youruseraccount>\documents\visual studio 2013\project
s\stacktracing\stacktracing\stacktracing.cpp: line: 135: address: 0x498B12D0
Wrote a dump.
The problem is that, the above trace contains only line: 135
, which corresponds to the call to the call to function0();
in threadFunction
. However, I would like it to include, as part of the stack trace, line: 29
, where it does a throw new exception;
. Why doesn't it include this as part of the stack trace? How can I make it also include this part of the stack trace? The only way I have been able to achieve this functionality so far is to use __try
and __except(FatalExceptionFilter(GetExceptionCode(), GetExceptionInformation()))
blocks around the call to function0()
; and pass the except to a FatalExceptionFilter
, but this is no good because it has its own caveats since it would have to be used everywhere, and I want a top-level solution. I want to catch all top level exceptions, and I want to know exactly where they were thrown.
P.S. This code is being run under a Windows 8.1, 64-bit machine. It is an MSVC++ console application compiled under a Release build/platform x64.
Update: I have tried the following using the _set_se_translator method and Petr's suggestions but it still doesn't seem to want to work. In fact, the divide by zero exception gets thrown unhandled, and nothing handles it:
#include "stdafx.h"
#include <process.h>
#include <iostream>
#include <Windows.h>
#include "dbghelp.h"
using namespace std;
#define TRACE_MAX_FUNCTION_NAME_LENGTH 1024
#define TRACE_LOG_ERRORS FALSE
#define TRACE_DUMP_NAME L"Exception.dmp"
void function2()
{
int a = 0;
int b = 0;
// The loop below should throw an unhandled exception.
for (int *i = 0; *i < 100; i++)
{
*i = 10000;
}
}
void function1()
{
int a = 0;
function2();
}
void function0()
{
function1();
}
void ShowStackTrace(EXCEPTION_POINTERS* exception)
{
CONTEXT context = *(exception->ContextRecord);
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
STACKFRAME64 frame;
memset(&frame, 0, sizeof(STACKFRAME64));
DWORD image;
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD displacement;
SymInitialize(process, NULL, TRUE);
while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
{
if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
{
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
{
printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
}
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymFromAddr: %lu.\n", GetLastError());
}
}
DWORD error = GetLastError();
if (error && TRACE_LOG_ERRORS)
{
printf("Error from StackWalk64: %lu.\n", error);
}
HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
exceptionInformation.ThreadId = GetCurrentThreadId();
exceptionInformation.ExceptionPointers = exception;
exceptionInformation.ClientPointers = FALSE;
if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL, NULL, NULL))
{
printf("Wrote a dump.");
}
}
void ShowStackTrace(CONTEXT *aContext)
{
CONTEXT context = *aContext;
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
STACKFRAME64 frame;
memset(&frame, 0, sizeof(STACKFRAME64));
DWORD image;
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD displacement;
SymInitialize(process, NULL, TRUE);
while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
{
if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
{
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
{
printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
}
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymFromAddr: %lu.\n", GetLastError());
}
}
DWORD error = GetLastError();
if (error && TRACE_LOG_ERRORS)
{
printf("Error from StackWalk64: %lu.\n", error);
}
HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, NULL, NULL, NULL))
{
printf("Wrote a dump.");
}
}
class CustomException {
public:
CustomException(EXCEPTION_POINTERS *exception = nullptr)
{
CONTEXT context;
ZeroMemory(&context, sizeof(CONTEXT));
if (exception)
{
// In case of an SEH exception.
ShowStackTrace(exception);
}
else
{
// In case of a C++ exception.
RtlCaptureContext(&context);
ShowStackTrace(&context);
}
}
};
void SEHExceptionTranslator(unsigned int, EXCEPTION_POINTERS *exception){
throw CustomException(exception);
}
static void threadFunction(void *param)
{
_set_se_translator(SEHExceptionTranslator);
function0();
}
LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS exception)
{
CONTEXT context = *(exception->ContextRecord);
HANDLE thread = GetCurrentThread();
HANDLE process = GetCurrentProcess();
STACKFRAME64 frame;
memset(&frame, 0, sizeof(STACKFRAME64));
DWORD image;
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rbp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "This platform is not supported."
#endif
SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
DWORD displacement;
while (StackWalk(image, process, thread, &frame, &context, NULL, NULL, NULL, NULL))
{
if (SymFromAddr(process, frame.AddrPC.Offset, NULL, symbol))
{
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, line))
{
printf("\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymGetLineFromAddr64: %lu.\n", GetLastError());
}
}
else if (TRACE_LOG_ERRORS)
{
printf("Error from SymFromAddr: %lu.\n", GetLastError());
}
}
DWORD error = GetLastError();
if (error && TRACE_LOG_ERRORS)
{
printf("Error from StackWalk64: %lu.\n", error);
}
HANDLE dumpFile = CreateFile(TRACE_DUMP_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
exceptionInformation.ThreadId = GetCurrentThreadId();
exceptionInformation.ExceptionPointers = exception;
exceptionInformation.ClientPointers = FALSE;
if (MiniDumpWriteDump(process, GetProcessId(process), dumpFile, MiniDumpNormal, exception ? &exceptionInformation : NULL, NULL, NULL))
{
printf("Wrote a dump.");
}
return EXCEPTION_CONTINUE_SEARCH;
}
int _tmain(int argc, _TCHAR* argv[])
{
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
SymInitialize(GetCurrentProcess(), NULL, TRUE);
_set_se_translator(SEHExceptionTranslator);
SetUnhandledExceptionFilter(UnhandledExceptionFilter);
_beginthread(threadFunction, 0, NULL);
printf("Press any key to exit.\n");
cin.get();
SymCleanup(GetCurrentProcess());
return 0;
}