Jakob Simon-Gaarde
Jakob Simon-Gaarde

Reputation: 725

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();
}

Upvotes: 7

Views: 3641

Answers (3)

Jakob Simon-Gaarde
Jakob Simon-Gaarde

Reputation: 725

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

Upvotes: 5

DrPizza
DrPizza

Reputation: 18360

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).

Upvotes: 1

user2610678
user2610678

Reputation:

According to MSDN:

In a multithreaded environment, terminate functions are maintained separately for each thread. Each new thread needs to install its own terminate function. Thus, each thread is in charge of its own termination handling.

I guess you have to set it separately for each thread.

Upvotes: 0

Related Questions