• 关于崩溃报告的日志以及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 = '';
    
            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;
    }
  • 相关阅读:
    zabbix验证微信
    free
    有名管道和无名管道
    shell实现并发控制
    TCP/IP协议簇 端口 三次握手 四次挥手 11种状态集
    自动化运维
    JSON对象(自定义对象)
    对象中属性的遍历、删除与成员方法
    对象间的赋值操作
    自定义类
  • 原文地址:https://www.cnblogs.com/ZefengYao/p/11507867.html
Copyright © 2020-2023  润新知