關於崩潰報告的日志以及dump文件


在用戶使用軟件的過程當中突然產生軟件崩潰的問題,必須采取相關的措施去攔截崩潰產生的原因,這有助於程序員解決此類崩潰的再次發生。特別是有些難以復現的崩潰,不穩定的崩潰,更有必要去調查崩潰產生的原因。一般來說,崩潰報告中需要記錄的信息主要包含以下幾點:

1.產生崩潰時電腦的硬件相關信息

2.崩潰發生的時間

3.最重要的,即崩潰時的函數調用堆棧信息

4.用戶可以手動填寫如何制造崩潰的方法,方便復現崩潰

關於dump文件,可以用於恢復崩潰時的函數堆棧信息,方便程序員調試

接下來簡述一下如何利用win32系統函數制作崩潰報告。

首先就是一個輸出崩潰報告的函數:

void CrashHandler::reportCrash()
{
    //將getStackTrace()中讀取到的堆棧日志記錄下來
    logErrorAndStackTrace(getStackTrace());
    //保存錯誤日志
    saveCrashLog();
    //保存dump文件
    mCrashMiniDumpPath = mSavePath + sMiniDumpName;
    win32_writeMiniDump(mCrashMiniDumpPath, nullptr);
}

函數主要由兩部分組成,第一部分是利用win32的函數調取函數堆棧信息,並將這些信息保存至日志當中;第二部分則是獲取dump文件並保存。

先來看看第一部分是如何做的:

std::string CrashHandler::getStackTrace()
{
    CONTEXT context;
    RtlCaptureContext(&context);
    win32_initPSAPI();
    win32_loadSymbols();
    return win32_getStackTrace(context, 2);
}

其中win32_loadSymbols()函數主要用於獲取堆棧中函數名等標志信息,如果不獲取這些標志信息,則堆棧信息中只有十六進制的地址,無法通過這個地址訪問到其他有用的信息,這樣即時記錄了函數堆棧信息也沒有任何意義了。(注意要開啟debug或者debugWithRelease版本去編譯才可以獲得標志信息,使用release版本編譯無法調試,一樣無法獲得標志信息),接下來win32_getStackTrace函數正式獲取到了函數堆棧信息,下面寫一下這幾個函數的具體實現方案:

void CrashHandler::win32_loadSymbols()
{
    if (gSymbolsLoaded)
        return;
    HANDLE hProcess = GetCurrentProcess();
    UINT32 options = SymGetOptions();

    options |= SYMOPT_LOAD_LINES;
    options |= SYMOPT_EXACT_SYMBOLS;
    options |= SYMOPT_UNDNAME;
    options |= SYMOPT_FAIL_CRITICAL_ERRORS;
    options |= SYMOPT_NO_PROMPTS;
    SymSetOptions(options);
    if (!SymInitialize(hProcess, nullptr, false))
    {
        Log::message("SymInitialize failed.Error code : %d", (UINT32)GetLastError());
        return;
    }
    DWORD bufferSize;
    gEnumProcessModules(hProcess, nullptr, 0, &bufferSize);

    HMODULE* modules = (HMODULE*)malloc(bufferSize);
    gEnumProcessModules(hProcess, modules, bufferSize, &bufferSize);

    UINT32 numModules = bufferSize / sizeof(HMODULE);
    for (UINT32 i = 0; i < numModules; i++)
    {
        MODULEINFO moduleInfo;

        char moduleName[MAX_STACKTRACE_NAME_BYTES];
        char imageName[MAX_STACKTRACE_NAME_BYTES];

        gGetModuleInformation(hProcess, modules[i], &moduleInfo, sizeof(moduleInfo));
        gGetModuleFileNameEx(hProcess, modules[i], imageName, MAX_STACKTRACE_NAME_BYTES);
        gGetModuleBaseName(hProcess, modules[i], moduleName, MAX_STACKTRACE_NAME_BYTES);

        char pdbSearchPath[MAX_STACKTRACE_NAME_BYTES];
        char* fileName = nullptr;
        GetFullPathNameA(moduleName, MAX_STACKTRACE_NAME_BYTES, pdbSearchPath, &fileName);
        *fileName = '\0';

        SymSetSearchPath(GetCurrentProcess(), pdbSearchPath);

        DWORD64 moduleAddress = SymLoadModule64(hProcess, modules[i], imageName, moduleName, (DWORD64)moduleInfo.lpBaseOfDll,
            (DWORD)moduleInfo.SizeOfImage);

        if (moduleAddress != 0)
        {
            IMAGEHLP_MODULE64 imageInfo;
            memset(&imageInfo, 0, sizeof(imageInfo));
            imageInfo.SizeOfStruct = sizeof(imageInfo);

            if (!SymGetModuleInfo64(GetCurrentProcess(), moduleAddress, &imageInfo))
            {
                Log::message("Warning:Failed retrieving module info for module: %s Error code: %d", moduleName, (UINT32)GetLastError());
            }
            else
            {
                // Disabled because too much spam in the log, enable as needed
            }
        }
        else
        {
            Log::message("Warning:Failed loading module %s.Error code: %d. Search path: %s. Image name: %s", moduleName, (UINT32)GetLastError(), pdbSearchPath, imageName);
        }
    }
    free(modules);
    gSymbolsLoaded = true;
}
std::string CrashHandler::win32_getStackTrace(CONTEXT context, UINT32 skip)
{
    UINT64 rawStackTrace[MAX_STACKTRACE_DEPTH];
    UINT32 numEntries = win32_getRawStackTrace(context, rawStackTrace);

    numEntries = min((UINT32)MAX_STACKTRACE_DEPTH, numEntries);

    UINT32 bufferSize = sizeof(PIMAGEHLP_SYMBOL64) + MAX_STACKTRACE_NAME_BYTES;
    UINT8* buffer = (UINT8*)malloc(bufferSize);

    PIMAGEHLP_SYMBOL64 symbol = (PIMAGEHLP_SYMBOL64)buffer;
    symbol->SizeOfStruct = bufferSize;
    symbol->MaxNameLength = MAX_STACKTRACE_NAME_BYTES;

    HANDLE hProcess = GetCurrentProcess();

    std::stringstream outputStream;
    for (UINT32 i = skip; i < numEntries; i++)
    {
        if (i > skip)
            outputStream << std::endl;

        DWORD64 funcAddress = rawStackTrace[i];

        // Output function name
        DWORD64 dummy;
        if (SymGetSymFromAddr64(hProcess, funcAddress, &dummy, symbol))
        {
            outputStream << std::string(symbol->Name) + "() - ";
        }

        // Output file name and line
        IMAGEHLP_LINE64    lineData;
        lineData.SizeOfStruct = sizeof(lineData);
        std::string addressString = std::to_string(funcAddress);

        DWORD column;
        if (SymGetLineFromAddr64(hProcess, funcAddress, &column, &lineData))
        {
            std::string filePath = lineData.FileName;
            outputStream << "0x" + addressString + " File[" + filePath + ":" + std::to_string(lineData.LineNumber) + " (" + std::to_string(column) + ")]";
        }
        else
        {
            outputStream << "0x" + addressString;
        }

        // Output module name
        IMAGEHLP_MODULE64 moduleData;
        moduleData.SizeOfStruct = sizeof(moduleData);

        if (SymGetModuleInfo64(hProcess, funcAddress, &moduleData))
        {
            std::string filePath = moduleData.ImageName;

            outputStream << " Module[" + filePath + "]";
        }
    }

    free(buffer);

    return outputStream.str();
}

接下來就是dump文件的記錄,由win32_writeMiniDump()函數完成

void CrashHandler::win32_writeMiniDump(const std::string& filePath, EXCEPTION_POINTERS* exceptionData)
{
    MiniDumpParams param = { filePath, exceptionData };

    // Write minidump on a second thread in order to preserve the current thread's call stack
    DWORD threadId = 0;
    HANDLE hThread = CreateThread(nullptr, 0, &win32_writeMiniDumpWorker, &param, 0, &threadId);

    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
}

其中win32_writeMiniDumpWorker()用於導出dump文件,實現方案如下:

DWORD CALLBACK win32_writeMiniDumpWorker(void* data)
{
    CrashHandler::MiniDumpParams* params = (CrashHandler::MiniDumpParams*)data;

    std::wstring pathString = string2wstring(params->filePath);
    HANDLE hFile = CreateFileW(pathString.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
        nullptr);

    if (hFile != INVALID_HANDLE_VALUE)
    {
        MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo;

        DumpExceptionInfo.ThreadId = GetCurrentThreadId();
        DumpExceptionInfo.ExceptionPointers = params->exceptionData;
        DumpExceptionInfo.ClientPointers = false;

        MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal,
            &DumpExceptionInfo, nullptr, nullptr);
        CloseHandle(hFile);
    }

    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM