Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Crash/exception handler in Qt application with thread support on Windows

I want to make a crash/exception handler for my Qt application. I have the handler working already (not included in the code below). The problem is on Windows that it only works if the crash occures in the same thread where signal() and std::set_terminate() is called.

On Linux it seems to work for all threads by default.

Is there a way to make it work for all application threads on Windows?

#include "mainwindow.h"
#include <QApplication>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <exception>

void seg_handler(int sig)
{
    // Crash/exception handling code
    // ...
    exit(1);
}

void std_handler( void ) {
     seg_handler(1);
}

int main(int argc, char *argv[]) {

    signal(SIGSEGV, seg_handler);
    std::set_terminate( std_handler );

    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}
like image 347
Jakob Simon-Gaarde Avatar asked Jul 29 '15 16:07

Jakob Simon-Gaarde


2 Answers

After very much research combined with my lack of knowledge on the Windows OS I finally reached a working solution that automatically sets up terminate functions for new threads.

The trick is to create a dll to handle your crashes/exceptions. In your dll implement the reserved DllMain() function. This function receives notifications every time a new thread or process is attached to the main process. The fdwReason parameter holds information about the event.

  • New thread: DLL_THREAD_ATTACH
  • New process: DLL_PROCESS_ATTACH

Implement your termination handlers in the dll and call signal() and std::set_terminate() for each thread or process that is started.

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved) {
    if (fdwReason==DLL_THREAD_ATTACH) {
        signal(SIGSEGV, seg_handler);
        std::set_terminate( std_handler );
    }
    if (fdwReason==DLL_PROCESS_ATTACH) {
        signal(SIGSEGV, seg_handler);
        std::set_terminate( std_handler );
    }
    return TRUE;
}

My handlers look like this:

#include <windows.h>
#include "StackWalker.h"
#include <signal.h>
#include <DbgHelp.h>
#include <iostream>
#include <qdebug.h>

void seg_handler(int sig) {
    // Simple callstack
    unsigned int   i;
    void         * stack[ 100 ];
    unsigned short frames;
    SYMBOL_INFO  * symbol;
    HANDLE         process;

    process = GetCurrentProcess();
    SymInitialize( process, NULL, TRUE );
    frames               = CaptureStackBackTrace( 0, 100, stack, NULL );
    symbol               = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
    symbol->MaxNameLen   = 255;
    symbol->SizeOfStruct = sizeof( SYMBOL_INFO );

    for( i = 0; i < frames; i++ ) {
        SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
        printf( "%i: %s - 0x%0X\n", frames - i - 1, symbol->Name, symbol->Address );
    }

    free( symbol );

    // Detailed callstack/crashinfo
    StackWalker sw;
    sw.ShowCallstack(GetCurrentThread());
    exit(1);
}

void std_handler( void ) {
     seg_handler(1);
}

The handlers above use StackWalker

Edit:

Of course you need to add the termination handlers to your main thread as well:

int main(int argc, char *argv[]) {
    CALL_LOGGER();
    signal(SIGSEGV, seg_handler);
    std::set_terminate( std_handler );
    return runapp(argc, argv);
}

Hope this helps someone

-Jakob

like image 83
Jakob Simon-Gaarde Avatar answered Sep 22 '22 13:09

Jakob Simon-Gaarde


I suspect that SetUnhandledExceptionFilter is a better way of trapping crashes on Windows than signal. It's set globally (so you don't need the DLL to install a handler in each thread) and doesn't discard information about the fault (it's possible there's some way of recovering the true cause of the SIGSEGV but I don't immediately know how).

like image 29
DrPizza Avatar answered Sep 20 '22 13:09

DrPizza