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

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

其中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, , &bufferSize); HMODULE* modules = (HMODULE*)malloc(bufferSize);
gEnumProcessModules(hProcess, modules, bufferSize, &bufferSize); UINT32 numModules = bufferSize / sizeof(HMODULE);
for (UINT32 i = ; 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 != )
{
IMAGEHLP_MODULE64 imageInfo;
memset(&imageInfo, , 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 = ;
HANDLE hThread = CreateThread(nullptr, , &win32_writeMiniDumpWorker, &param, , &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, , 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 ;
}

关于崩溃报告的日志以及dump文件的更多相关文章

  1. [转]让程序在崩溃时体面的退出之SEH+Dump文件

    原文地址:http://blog.csdn.net/starlee/article/details/6649605 在我上篇文章<让程序在崩溃时体面的退出之SEH>中讲解了SEH中try/ ...

  2. Dump文件的生成和使用

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/lk142500/article/detai ...

  3. 如何使用dump文件进行调试

    转载[文尾出处链接] 1 简介第一次遇到程序崩溃的问题,之前为单位开发了一个插件程序,在本机运行没有出现问题,但把生成的可执行文件拷贝到服务器上一运行程序,刚进入插件代码,插件服务就崩溃了,当时被这个 ...

  4. mac下利用Breakpad的dump文件进行调试

    一.前情回顾 最近把公司的一个视频处理程序更新了一个版本,准备提交测试的发现了崩溃的情况.这个程序采用Qt和ffmpeg技术栈开发,主要用于对视频进行渲染拼接处理,在Windows和mac两个平台同时 ...

  5. [JAVA]JAVA章3 如何获取及查看DUMP文件

    一.dump基本概念 在故障定位(尤其是out of memory)和性能分析的时候,经常会用到一些文件来帮助我们排除代码问题.这些文件记录了JVM运行期间的内存占用.线程执行等情况,这就是我们常说的 ...

  6. Windows下dump文件生成与分析

    一.    生成Dump文件方式 1.1任务管理器 在程序崩溃后,先不关闭程序,在任务管理器中找到该程序对应的进程.右键—>创建转储文件. 此时会在默认的目录下创建出一个dump文件. 可以看出 ...

  7. dump文件

    https://blog.csdn.net/icandoit_2014/article/details/78739962 可以看出,此种方法只适用于程序崩溃但没有立即自行退出的情况.倘若程序故障后自行 ...

  8. 编写的windows程序,崩溃时产生crash dump文件的办法

    一.引言 dump文件是C++程序发生异常时,保存当时程序运行状态的文件,是调试异常程序重要的方法,所以程序崩溃时,除了日志文件,dump文件便成了我们查找错误的最后一根救命的稻草.windows程序 ...

  9. [转]让程序在崩溃时体面的退出之Dump文件

    原文地址:http://blog.csdn.net/starlee/article/details/6630816 在我的那篇<让程序在崩溃时体面的退出之CallStack>中提供了一个在 ...

随机推荐

  1. Hive导入导出数据的方法

    Hive导入数据的方式 官网文档: LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (p ...

  2. Docker下载镜像并创建容器运行

    在linux系统中安装完成docker后,我们开始进行docker的镜像.容器的使用. 在使用docker时,首先要明确的两个概念:image(镜像) 与  container (容器) image: ...

  3. Linux架构之Nginx 高可用

    第53章 Nginx之高可用Keepalived 一.Keepalived高可用基本概述 1.1)什么是高可用 一般是指2台机器启动着完全相同的业务系统,当有一台机器down机了,另外一台服务器就能快 ...

  4. vb中的资源文件

    --- 一.VB中资源文件的分类 ---- 通常将资源分为两类:1.字符串资源,包括菜单和文本中使用的字符串,如"Hello World"等:2.二进制资源,如位图(.BMP),图 ...

  5. 02python程序设计基础——字符串

    字符串方法 format 1.替换字段名 在最简单的情况下,只需向 format 提供要设置其格式的未命名参数,并在格式字符串中使用未命名字段.此时,将按顺序将字段和参数配对.你还可给参数指定名称,这 ...

  6. shell安装mysql,连接数据库,创建数据库

    https://blog.csdn.net/yhflyl/article/details/83061126 https://blog.csdn.net/wyl9527/article/details/ ...

  7. [NOI2018]归程(kruscal重构树)

    [NOI2018]归程 题面太长辣,戳这里 模拟赛上写了一个spfa (关于spfa,它已经死了),然后一个st表水完暴力跑路.考后说是Kruscal重构树或者可持久化并查集???这都是些什么东西.不 ...

  8. jQuery入门教程-CSS样式操作大全

    1.获取样式 2.设置样式 3.追加样式 4.移除样式 5.重复切换anotherClass样式 6.判断是否含有某项样式 7.设置 CSS 属性 参数 描述 name 必需.规定 CSS 属性的名称 ...

  9. Flask【第7篇】:Flask中的wtforms使用

    flask中的wtforms使用 一.简单介绍flask中的wtforms WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证. 安装: pip3 install w ...

  10. 关于vs2019

    一.vs2019中的MFC 在想创建一个基于对话的应用时找不着模版了,这下可慌了,试遍了已有的各个模版都没要,要么就是缺少头文件,我在想是不是少安装了什么选项.重装了相关模块,最后又核对了一遍,都对. ...