Qt C++ Unhandled exceptions stack trace

2.8k views Asked by At

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/

1

There are 1 answers

9
IInspectable On BEST ANSWER

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, calling MiniDumpWriteDump from there produces the stack trace you observed.

To get a more helpful stack trace, you need to pass the EXCEPTION_POINTERS to MiniDumpWriteDump.

This is just one of many issues with the implementation you are using. There are more:

  • The author seems to be well aware, that they can pass nullptr for the ExceptionParam parameter, but then moves on to decide to always pass nullptr. The interface should really provide two overloads: One that takes an EXCEPTION_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).
  • The author raises an "interesting question: what should be done with other threads in the application?" As it stands, 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.
  • The author fails to address a real issue (both of the vanilla 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 to MiniDumpWriteDump 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: