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