I am trying to figure out a way to get the stack trace of a deployed c++ application in the event of a crash. I have tried a couple of approaches and I think my issue is related to the stack after an exception occurs.
I created a test application in Qt. Here is the code.
void miniDumpFunc()
{
MiniDump dump;
dump.Create(L"C:\\dmp\\dmp.dmp");
}
void anotherFunc()
{
miniDumpFunc();
}
LONG WINAPI OurCrashHandler(EXCEPTION_POINTERS * /*ExceptionInfo*/)
{
miniDumpFunc();
return EXCEPTION_EXECUTE_HANDLER;
}
void badFunc()
{
int *myNull = NULL;
*myNull = 42;
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
::SetUnhandledExceptionFilter(OurCrashHandler);
anotherFunc();
return app.exec();
}
If I call anotherFunc() and then look at the stack trace in WinDbg I get the following stack.
I think there is some in-lining going on here, but it looks about right.
If I call badFunc() however this is what I get.
The stack trace starts at UnhandledExceptionFilter. It seems like the stack is getting messed up by the exception.
Here is where I got the code to generate the mini dumps. http://blog.aaronballman.com/2011/05/generating-a-minidump/
The stack trace you posted for
badFunc
is perfectly fine, and the expected result, given the implementation you are using. MiniDumpWriteDump runs a stack trace using the current instruction pointer, unless you are passing the SEH exception's EXCEPTION_POINTERS through the MINIDUMP_EXCEPTION_INFORMATION structure. Since the unhandled exception filter is installed at the bottom of the exception frames, callingMiniDumpWriteDump
from there produces the stack trace you observed.To get a more helpful stack trace, you need to pass the
EXCEPTION_POINTERS
toMiniDumpWriteDump
.This is just one of many issues with the implementation you are using. There are more:
nullptr
for theExceptionParam
parameter, but then moves on to decide to always passnullptr
. The interface should really provide two overloads: One that takes anEXCEPTION_POINTERS
argument, and one without. This allows the functionality to be called from anywhere, but also retains the stack trace, when called from an unhandled exception filter (the latter is by far the most common use case).MiniDumpWriteDump
already suspends all threads in the process before moving forward. You don't even have a choice there. The entire implementation to suspend threads (including the filter to exclude the helper thread) is superfluous. It needs to go.MiniDumpWriteDump
, as well as their own thread suspending implementation): Threads get suspended at arbitrary points. If any of these threads hold any locks (like the global heap allocation mutex), you are in for an instant dead lock. After all,MiniDumpWriteDump
will want to allocate memory from the process heap as well. The solution here is more involved: The call toMiniDumpWriteDump
must be performed in its own process, with appropriate IPC in place.Either of the above is a fairly substantial bug, and needs to be addressed. As published, the code is both useless as well as dangerous. If you feel like implementing your own solution in the meantime, have a look at the following resources for reliable information: