惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
A
About on SuperTechFans
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Blog — PlanetScale
Blog — PlanetScale
aimingoo的专栏
aimingoo的专栏
云风的 BLOG
云风的 BLOG
The GitHub Blog
The GitHub Blog
Vercel News
Vercel News
G
Google Developers Blog
J
Java Code Geeks
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
Cloudbric
Cloudbric
L
LINUX DO - 最新话题
MyScale Blog
MyScale Blog
H
Heimdal Security Blog
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
S
Security @ Cisco Blogs
Latest news
Latest news
I
Intezer
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
月光博客
月光博客
T
Threatpost
博客园 - 【当耐特】
S
Schneier on Security
P
Privacy International News Feed
G
GRAHAM CLULEY
T
Tenable Blog
AWS News Blog
AWS News Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
雷峰网
雷峰网
博客园 - Franky
Engineering at Meta
Engineering at Meta
美团技术团队
S
Secure Thoughts
T
Troy Hunt's Blog
Microsoft Security Blog
Microsoft Security Blog
SecWiki News
SecWiki News
V
Visual Studio Blog
人人都是产品经理
人人都是产品经理
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Cisco Talos Blog
Cisco Talos Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Martin Fowler
Martin Fowler
Webroot Blog
Webroot Blog
Google DeepMind News
Google DeepMind News
H
Hackread – Cybersecurity News, Data Breaches, AI and More

博客园 - ZefengYao

Markdown 入门与 Word 使用指南 hdu 6223 Infinite Fraction Path 2017南宁现场赛E The Champion ACM-ICPC 2018 南京赛区网络预赛 Sum c语言几个字符串处理函数的简单实现 各种类型排序的实现及比较 随机洗牌算法Knuth Shuffle和错排公式 两个栈实现队列 面试杂题 面试题——栈的压入、弹出顺序 C++ 智能指针的简单实现 openGL初学函数解释汇总 foj Problem 2107 Hua Rong Dao foj Problem 2282 Wand UVA-1400 Ray, Pass me the dishes! 《挑战程序设计竞赛》 利用后缀数组求最长回文串 Uva 11174 Stand in a Line UVA 11375 Matches poj 3729 Facer’s string
关于崩溃报告的日志以及dump文件
ZefengYao · 2019-09-11 · via 博客园 - ZefengYao

在用户使用软件的过程当中突然产生软件崩溃的问题,必须采取相关的措施去拦截崩溃产生的原因,这有助于程序员解决此类崩溃的再次发生。特别是有些难以复现的崩溃,不稳定的崩溃,更有必要去调查崩溃产生的原因。一般来说,崩溃报告中需要记录的信息主要包含以下几点:

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;
}