Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What actions do I need to take to get a crash dump in ALL error scenarios?

We're on Windows and we want to get a crash dump (possibly using MiniDumpWriteDump) for all scenarios where our application exit's unexpectedly.

So far we have identified, and set up, the following:

  • SetUnhandledExceptionFilter for unhandled exception (Win32 as well as "normal" C++ ones.)
  • _set_invalid_parameter_handler for the CRT invalid argument handling
  • _set_abort_behaviorplus a SIGABRT handler to account for calls to abort()

Is there anything we missed? (Modulo some code non-legitimately calling ExitProcess, TerminateProcess or one of the exit variants.)


I'll note that this question here is orthogonal to how a crash dump is then obtained. E.g., if you want a crash dump in case of abort, you always must use _set_abort_behaviour because otherwise abort just exits.

I'll also note that on Windows7+, not setting SetUHEF and just setting up the "correct" WER dump settings in the registry is often a viable way.

like image 271
Martin Ba Avatar asked Nov 27 '12 19:11

Martin Ba


2 Answers

To expand on all the answers here's what I found to work best for 100M+ installs:

  • SetErrorMode to prevent any WER dialogs showing up.
  • EnableCrashingOnCrashes
  • PreventSetUnhandledExceptionFilter
  • SetUnhandledExceptionFilter for system exceptions.
  • _set_invalid_parameter_handler for the CRT invalid argument handling
  • _set_abort_behavior plus a SIGABRT handler to account for calls to abort()

std::set_terminate and std::set_unexpected perhaps should also be mentioned.

And the most important part to get it all right:

  • all these handlers should call a function that executes under a mutex/critical section to ensure that if there are any other crashes happening in other threads at the same time they would all stop and wait instead of causing havoc.
  • signal handler for SIGABRT must set itself back as a SIGABRT handler! Without this if you get crashes happening at the same time from other threads you process will exit immediately without giving you any time to handle the crash.
  • actual handling of the error should ideally happen in another process, or at least in another thread that was started at the beginning of the process, otherwise you won't be able to handle low memory conditions or stackoverflow errors.

See setExceptionHandlers below for reference. Also, most likely you don't want to hook up all the handlers in debug builds or when IsDebuggerPresent.

#include <signal.h>
#include <windows.h>
#include <boost/thread/mutex.hpp>

void EnableCrashingOnCrashes();
void PreventSetUnhandledExceptionFilter();

static void exceptionHandler(EXCEPTION_POINTERS* excpInfo)
{
    // your code to handle the exception. Ideally it should
    // marshal the exception for processing to some other
    // thread and waif for the thread to complete the job
}

static boost::mutex unhandledExceptionMx;
static LONG WINAPI unhandledException(EXCEPTION_POINTERS* excpInfo = NULL)
{
    boost::mutex::scoped_lock lock(unhandledExceptionMx);
    if (!excpInfo == NULL)
    {
        __try // Generate exception to get proper context in dump
        {
            RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
        }
        __except (exceptionHandler(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER)
        {
        }
    }
    else
    {
        exceptionHandler(excpInfo);
    }

    return 0;
}

static void invalidParameter(const wchar_t* expr, const wchar_t* func,
    const wchar_t* file, unsigned int line, uintptr_t reserved)
{
    unhandledException();
}

static void pureVirtualCall()
{
    unhandledException();
}

static void sigAbortHandler(int sig)
{
    // this is required, otherwise if there is another thread
    // simultaneously tries to abort process will be terminated
    signal(SIGABRT, sigAbortHandler);
    unhandledException();
}

static void setExceptionHandlers()
{
    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
    SetUnhandledExceptionFilter(unhandledException);
    _set_invalid_parameter_handler(invalidParameter);
    _set_purecall_handler(pureVirtualCall);
    signal(SIGABRT, sigAbortHandler);
    _set_abort_behavior(0, 0);
    EnableCrashingOnCrashes();
    PreventSetUnhandledExceptionFilter();
}
like image 146
Pavel P Avatar answered Oct 06 '22 07:10

Pavel P


I use exactly the ones you've listed, plus _set_purecall_handler, plus this handy snippet of code:

void EnableCrashingOnCrashes()
{
    typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
    typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
    static const DWORD EXCEPTION_SWALLOWING = 0x1;

    const HMODULE kernel32 = LoadLibraryA("kernel32.dll");
    const tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
    const tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
    if(pGetPolicy && pSetPolicy)
    {
        DWORD dwFlags;
        if(pGetPolicy(&dwFlags))
        {
            // Turn off the filter
            pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
        }
    }
}

Source: http://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesnt-work/

These other articles on his site also helped me understand this: http://randomascii.wordpress.com/2011/12/07/increased-reliability-through-more-crashes/ http://randomascii.wordpress.com/2012/07/22/more-adventures-in-failing-to-crash-properly/

like image 27
Ben Hymers Avatar answered Oct 06 '22 08:10

Ben Hymers