2

We have a rare crash in a 32bit C++ executable on Windows that doesn't produce a memory dump. We don't have a repro case and when the crash happens the game just disappears after a few seconds, so we need a memory dump to find the cause. Other crashes do produce a memory dump, so we know that our memory dump function sometimes works, just not on this particular crash.

Under what circumstances does the combination of _set_se_translator and MiniDumpWriteDump fail to produce a memory dump? Are we doing something wrong in our memory dump handling that causes it to sometimes not produce a dump?

Here's what we currently do:

At the start of the main function in the main thread we call SetUnhandledExceptionFilter(CrashDumpManager::unhandledExceptionHandler);

In every thread we call _set_se_translator(CrashDumpManager::MiniDumpFunction);

This is what CrashDumpManager.h looks like:

#include <Windows.h>
#include <Dbghelp.h>

class CrashDumpManager
{
public:
    static void MiniDumpFunction(unsigned int nExceptionCode, EXCEPTION_POINTERS *pException);
    static LONG CALLBACK unhandledExceptionHandler(EXCEPTION_POINTERS* e);

    //If the game crashes because of a memory leak then there won't be enough memory free to generate a memory dump
    //Therefore 10MB is allocated here and deleted before the crashdump is written.
    static unsigned char* crashdumpMemory;
};

And this is what CrashDumpManager.cpp looks like:

#include "CrashDumpManager.h"

void CrashDumpManager::MiniDumpFunction(unsigned int nExceptionCode, EXCEPTION_POINTERS *pException)
{
    delete crashdumpMemory;
    crashdumpMemory = nullptr;

    // prevent stack overflow when crashing in this function
    static bool calledFunctionOnce = false;
    if (!calledFunctionOnce)
    {
        calledFunctionOnce = true;

        HMODULE dbgHelpModule = LoadLibraryA("dbghelp");
        if (dbgHelpModule == nullptr)
            return;
        auto writeMiniDumpFunction = (decltype(&MiniDumpWriteDump))GetProcAddress(dbgHelpModule, "MiniDumpWriteDump");
        if (writeMiniDumpFunction == nullptr)
            return;

        char name[MAX_PATH];
        {
            char* nameEnd = name + GetModuleFileNameA(GetModuleHandleA(0), name, MAX_PATH);
            SYSTEMTIME t;
            GetSystemTime(&t);
            wsprintfA(nameEnd - strlen(".exe"), "_%4d%02d%02d_%02d%02d%02d.mdmp",
                      t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
        }

        HANDLE dumpFileHandle = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
        if (dumpFileHandle == INVALID_HANDLE_VALUE)
            return;

        MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
        exceptionInfo.ThreadId = GetCurrentThreadId();
        exceptionInfo.ExceptionPointers = pException;
        exceptionInfo.ClientPointers = FALSE;

        auto dumped = writeMiniDumpFunction(GetCurrentProcess(), GetCurrentProcessId(), dumpFileHandle,
                                            MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
                                            pException ? &exceptionInfo : nullptr, nullptr, nullptr);

        CloseHandle(dumpFileHandle);
    }
}

LONG CALLBACK CrashDumpManager::unhandledExceptionHandler(EXCEPTION_POINTERS* e)
{
    CrashDumpManager::MiniDumpFunction(0, e);
    return EXCEPTION_CONTINUE_SEARCH;
}

unsigned char* CrashDumpManager::crashdumpMemory = new unsigned char[10*1024*1024];
Oogst
  • 358
  • 2
  • 14
  • 2
    That's pretty normal, you are expecting a fairly convoluted function like MiniDumpWriteDump() to still operate correctly in a corrupted process. The only truly safe way to do it is with a separate out-of-process watchdog process. https://stackoverflow.com/questions/13590980/how-do-i-get-at-the-exception-information-when-using-minidumpwritedump-out-of-pr – Hans Passant Feb 15 '19 at 14:36
  • 1
    One corner-case worth mentioning is that you have to be careful where you store that file. As posted it is doomed to fail on a properly installed program. No write access to c:\program files. – Hans Passant Feb 15 '19 at 15:16
  • 1
    Some things to try: 1) Don't use dynamic memory allocation (and if you do, then remember to use `delete[]` for arrays) - the heap might be messed up. 2) Make sure only one thread can be in `MiniDumpFunction` (use a proper synchronization primitive) 3) Make sure all threads call `_set_se_translator` (have you checked for libraries creating threads?) 4) Follow the advice of @Hans Passant and (if you can reproduce the crash on a dev machine) disable your own crash dump mechanism and setup the system to create full memory dumps for all crashes – user786653 Feb 15 '19 at 17:32
  • 1
    Forgot to add for 2): Other threads reaching `MiniDumpFunction` should hang if they're not the first to arrive there - definitely not `EXCEPTION_CONTINUE_SEARCH`. And the thread writing the minidump should exit the application ASAP afterwards. – user786653 Feb 15 '19 at 17:41

0 Answers0