I am catching an exception using Win32 SEH:
try
{
// illegal operation that causes access violation
}
__except( seh_filter_func(GetExceptionInformation()) )
{
// abort
}
where the filter function looks like:
int seh_filter_func(EXCEPTION_POINTERS *xp)
{
// log EIP, other registers etc. to file
return 1;
}
This works so far and the value in xp->ContextRecord->Eip
tells me which function caused the access violation (actually - ntdll.dll!RtlEnterCriticalSection , and the value for EDX tells me that this function was called with a bogus argument).
However, this function is called in many places, including from other WinAPI functions, so I still don't know which code is responsible for calling this function with the bogus argument.
Is there any code I can use to generate a trace of the chain of function calls leading up to where EIP is now, based on the info in EXCEPTION_POINTERS
or otherwise? (Running the program under an external debugger isn't an option).
Just EIP values would be OK as I can look them up in the linker map and symbol tables, although if there is a way to automatically map them to symbol names that'd be even better.
I am using C++Builder 2006 for this project, although an MSVC++ solution might work anyway.
It seems that in 64-bit mode the SEH filter function is executed on the same stack without unwinding it, so you can indeed look at the suffix of boost::stacktrace::stacktrace()
to see where the error has happened, as shown in another answer.
However, it does not work for me in 32-bit mode. I had to walk the stack using the StackWalk64
function from DbgHelp.h
/DbgHelp.lib
. Although it needs the STACKFRAME64
to start, it is possible populate it with corresponding registers obtained from the CONTEXT
struct at xp->ContextRecord
.
The following code works for me in 32-bit mode:
#include <boost/stacktrace.hpp> // For symbols only
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
int seh_filter_func(EXCEPTION_POINTERS* xp) {
CONTEXT context = *xp->ContextRecord;
STACKFRAME64 s;
ZeroMemory(&s, sizeof(s));
s.AddrPC.Offset = context.Eip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = context.Ebp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrStack.Offset = context.Esp;
s.AddrStack.Mode = AddrModeFlat;
// Not thread-safe!
for (int i = 0;
StackWalk64(
IMAGE_FILE_MACHINE_I386,
GetCurrentProcess(),
GetCurrentThread(),
&s,
&context,
NULL,
NULL,
NULL,
NULL);
i++) {
std::cout << i << ": " << boost::stacktrace::frame((void*)s.AddrPC.Offset) << "\n";
}
return 1;
}
For some reason, that does not work in 64-bit mode even if I replace 32-bit registers with corresponding 64-bit registers. It prints the first frame correctly and prints something unclear later.