Displaying Exception Information Before Emergency Exit

Why and for what


During development, things are not always smooth. There are problems that do not pop up on the tests, but after the release. For such cases, we made a small system for catching exceptions and internal errors.


Exception catch


In order for the program to tell us before the crash (outside the debugger) what the problem is, you can use the signal processing function .


A few examples of such signals:


    signal(SIGABRT, abort_handler); //  ,    nullptr   
    signal(SIGFPE, floating_point_handler); //      
    signal(SIGILL, illegal_instruction_handler); //     intrinsics (SSE/AVX/Other) 

Signal handlers



inline void abort_handler(int signal)
{
    handler_base("Application is aborting");
}

inline void floating_point_handler(int signal)
{
    handler_base("Floating point error");
}

inline void illegal_instruction_handler(int signal)
{
    if (!CPU::Info.hasFeature(CPUFeature::SSE42))
        handler_base("SSE4.2 and AVX instructions isn't legal on your CPU");
    else if (!CPU::Info.hasFeature(CPUFeature::AVX)) 
        handler_base("AVX instructions isn't legal on your CPU");
    else
        handler_base("Illegal instruction");
}

(_set_invalid_parameter_handler), (_set_new_handler) .


: callstack'a


, , . , CallStack. : StackWalker


: dump-


, Dump .


using MINIDUMPWRITEDUMP = BOOL(WINAPI*)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
    CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
    CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
    CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
    );

LONG WriteMinidump(struct _EXCEPTION_POINTERS* pExceptionInfo)
{
    long retval = EXCEPTION_CONTINUE_SEARCH;

    // firstly see if dbghelp.dll is around and has the function we need
    // look next to the EXE first, as the one in System32 might be old
    // (e.g. Windows 2000)
    HMODULE hDll = nullptr;
    string_path szDbgHelpPath;

    if (GetModuleFileName(nullptr, szDbgHelpPath, _MAX_PATH))
    {
        char* pSlash = strchr(szDbgHelpPath, '\\');
        if (pSlash)
        {
            const char dbgHelpStr[] = "DBGHELP.DLL";
            xr_strcpy(pSlash + 1, sizeof(dbgHelpStr), dbgHelpStr);
            hDll = LoadLibraryA(szDbgHelpPath);
        }
    }

    if (!hDll)
    {
        //  dbgHelp 
        hDll = LoadLibraryA("DBGHELP.DLL");
    }

    LPCSTR szResult = nullptr;

    if (hDll)
    {
        MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");
        if (pDump)
        {
            string512 ErrorString = { NULL };
            DWORD ErrorSysCode = NULL;
            DWORD ErrorStringSize = NULL;

            string_path szDumpPath = { 0 };
            string_path szFilename = { 0 };
            string_path szScratch = { 0 };
            string64 t_stemp = { 0 };

            timestamp(t_stemp);
            xr_strcat(szDumpPath, DumpFilePath);

            //    : App Name, User Name, Time, Ext
            xr_strcat(szFilename, Core.ApplicationName);
            xr_strcat(szFilename, "_");
            xr_strcat(szFilename, Core.UserName);
            xr_strcat(szFilename, "_");
            xr_strcat(szFilename, t_stemp);
            xr_strcat(szFilename, ".mdmp");

            xr_strcat(szDumpPath, szFilename);

            //  
            HANDLE hFile = CreateFileA(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
            if (INVALID_HANDLE_VALUE == hFile)
            {
                //    ,    
                ZeroMemory(szDumpPath, sizeof(szDumpPath));
                xr_strcat(szDumpPath, szFilename);
                hFile = CreateFileA(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
            }
            if (hFile != INVALID_HANDLE_VALUE)
            {
                _MINIDUMP_EXCEPTION_INFORMATION ExInfo;

                ExInfo.ThreadId = GetCurrentThreadId();
                ExInfo.ExceptionPointers = pExceptionInfo;
                ExInfo.ClientPointers = NULL;

                //  Dump 
                MINIDUMP_TYPE dump_flags = MINIDUMP_TYPE(MiniDumpNormal | MiniDumpFilterMemory | MiniDumpScanMemory);

                //   
                char* logFileContent = nullptr;
                DWORD logFileContentSize = 0;

/////////////////////////////////////
                //        (logFileContent)
/////////////////////////////////////

        MINIDUMP_USER_STREAM_INFORMATION UserStreamsInfo = { 0 };
                MINIDUMP_USER_STREAM LogFileUserStream = { 0 };

                if (logFileContent)
                {
                    UserStreamsInfo.UserStreamCount = 1;
                    LogFileUserStream.Buffer = logFileContent; //   
                    LogFileUserStream.BufferSize = logFileContentSize; //  
                    LogFileUserStream.Type = MINIDUMP_STREAM_TYPE::CommentStreamA; // 
                    UserStreamsInfo.UserStreamArray = &LogFileUserStream;
                }

                BOOL bOK = pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, dump_flags, &ExInfo, &UserStreamsInfo, nullptr);
                if (bOK)
                {
                    xr_sprintf(szScratch, "Saved dump file to '%s'", szDumpPath);
                    szResult = szScratch;
                    retval = EXCEPTION_EXECUTE_HANDLER;
                }
                else
                {
                    ErrorSysCode = GetLastError();
                    ErrorStringSize = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, ErrorSysCode, 0, ErrorString, sizeof(ErrorString) - 1, nullptr);

                    if (!!ErrorString && ErrorSysCode && ErrorStringSize)
                        xr_sprintf(szScratch, "Failed to save dump file to '%s' (error %d '%s')", szDumpPath, ErrorSysCode, ErrorString);
                    else
                        xr_sprintf(szScratch, "Failed to save dump file to '%s' (No system error)", szDumpPath);
                    szResult = szScratch;

                }
                CloseHandle(hFile);
            }
            else
            {
                ErrorSysCode = GetLastError();
                ErrorStringSize = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, ErrorSysCode, 0, ErrorString, sizeof(ErrorString) - 1, nullptr);

                if (!!ErrorString && ErrorSysCode && ErrorStringSize)
                    xr_sprintf(szScratch, "Failed to create dump file '%s' (error %d '%s')", szDumpPath, ErrorSysCode, ErrorString);
                else
                    xr_sprintf(szScratch, "Failed to create dump file '%s' (No system error)", szDumpPath);
                szResult = szScratch;
            }
        }
        else
            szResult = "DBGHELP.DLL too old";
    }
    else
        szResult = "DBGHELP.DLL not found";

    return retval;
}


, , (, , ), , :
image
C# + WPF,


:


  • Continue program execution (if this is an internal error)
  • Report a problem (the stack flies to the clipboard, the user goes to the discord server in the desired topic)
  • Abort a program

Source: https://habr.com/ru/post/undefined/


All Articles