windows下MSVC编译breakpad、minidump_stackwalk记录
Windows下使用MSVC 编译Breakpad和minidump_stackwalk
在使用Breakpad捕获dump前,推荐使用其他命令行方式,比如windbg、procdump都可以捕获指定进程的dump文件,可以在程序main函数中启动时以后台方式命令行启动,相对来说比使用Breakpad捕获要省时省力很多。
项目中使用了海康SDK,在用windbg命令行方式捕获dump时发现有时会出现卡死问题,procdump并不会,如果使用了windbg偶发出现卡死(应用程序未响应,并不崩溃),尝试切换procdump。
并且dump文件和pdb、exe、源码是绑定关系,在发布程序时一定要备份好pdb、exe、源码,方便拿到客户现场的dump文件后可以通过pdb和exe、源码定位到崩溃调用堆栈。
breakpad源码分支: chrome_99
已编译Breakpad
编译Breakpad MSVC:MSVC 2017 (v142)
编译环境: Windows 10
已编译好的breakpad x86、x64 /MD、/MDd、/MT、/MTd库和minidump_stackwalk:https://wwzo.lanzoub.com/ip5u31clkv4f
include: 库包含目录
lib: breakpad x86、x64库
dump_syms.exe: 根据程序pdb文件生成符号文件,方便在minidump_stackwalk分析dump文件时,提供文件代码行数信息,有了代码行数信息开发人员看才有意义,方便定位到崩溃位置
minidump_stackwalk: 解析dump文件,生成崩溃信息,方便排查崩溃问题。这里提供了Debug和Release,功能都一样,那个能运行用哪个
编译breakpad
安装GYP
主要使用gyp生成.sln文件,方便使用VS打开使用MSVC编译breakpad 库.
安装gyp前,需要先安装python2.7,因为gyp是用python写的
python 2.7安装包地址:https://wwzo.lanzoub.com/iT7Ug1cnkwsh
没有PIP包管理工具
检查python2.7目录下的Scripts中有没有pip.exe
如果有pip,先检查PATH中有没有正确配置{python2.7目录}/Scripts,例如我安装在C:/Python27,那我的PATH中就有: C:\Python27\Scripts
如果没有pip,就需要先安装pip包管理工具.
安装pip过程
安装setuptools:
最新的setuptools发现并不支持python2.7,经测试setuptools 36.6.0可以正常安装,下载setuptools解压后,在setup.py所在目录执行:
python setup.py install
支持python2.7的pip版本是: pip 9.0.1(https://wwzo.lanzoub.com/ipldz1cnlsof密码:894y), 下载pip解压, 在setup.py文件所在目录执行:
python setup.py install
添加环境变量:
在PATH变量中添加{python安装目录}/Scripts,比如我python安装在C:\python27,那PATH中的值就是(如果PATH中存在就不用添加):
C:\Python27\Scripts
下载支持python2.7的six(https://wwzo.lanzoub.com/iReYr1cnmgkf密码:givo),在setup.py文件所在目录执行:
python setup.py install
下载GYP,解压,在setup.py所在目录执行:
python setup.py install
正常情况下,打开cmd,执行gyp,返回:
gyp: usage: gyp [options ...] [build_file ...]
gyp: error: no build_file
表示正常安装gyp.
获取breakpad
如果能访问github,可以在github中下载,访问不了就用gitee.
github: https://github.com/google/breakpad
gitee下请自行搜索,有很多个人维护的:
注意不要使用master分支,因为master分支一般没有.gyp文件,我们需要使用.gyp文件来生成.sln VS项目文件。
可以选择chrome_99分支,下载前可以浏览一下,看在src/client/windows下有没有breakpad_client.gyp文件、src/processor下有没有processor.gyp文件。
编译breakpad库
确定存在gyp文件后下载解压进入src/client/windows,打开命令行执行:
gyp --no-circular-check breakpad_client.gyp -G msvs_version=2017
这里的 msvc_version=2017 是MSVC的编译器版本,我装的是VS2019,编译器是MSVC2017,正常生成显示:
Warning: Missing input files: unittests\..\..\..\testing\googlemock\src\gmock-all.cc unittests\..\..\..\testing\googletest\src\gtest_main.cc unittests\..\..\..\testing\googletest\src\gtest-all.cc
执行后会在breakpad_client.gyp所在目录生成breakpad_client.sln,用VS打开,正常编译crash_generation_client、crash_generation_server、exception_handler三个项目就可以:
直接使用build_all报错提示少文件,不用管他,单独编译以上四个项目就行。
程序捕获异常生成dump用到的库
进程内写dump,不需要crash_generation_server库,只需要crash_generation_client、exception_handler、common。
进程外写dump就需要crash_generation_server,也就是软件启动后是两个进程,一个是软件自己的进程,还有一个专门写dump的进程,两个进程间通过pipe(命名管道)通讯。当软件崩溃时会把崩溃信息通过pipe发送到写dump进程服务进行写入dump文件。
进程内和进程外通讯
配置代码生成
一般的项目我们使用的是/MD、/MDd,crash_generation_server/crash_generation_client/exception_handler默认配置是: /MT:
项目需要的话需要改成/MD,重新编译
如果项目和库不匹配时会报:
exception_handler.lib(exception_handler.obj):-1: error: LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MD_DynamicRelease”(main.obj 中)
common.lib(guid_string.obj):-1: error: LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MD_DynamicRelease”(main.obj 中)
crash_generation_client.lib(crash_generation_client.obj):-1: error: LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MD_DynamicRelease”(main.obj 中)
编译完成后在项目目录下生成Debug/Release文件夹:
编译minidump_stackwalk
在程序崩溃生成dump后,需要minidump_stackwalk和dump_syms解析dump文件,来分析具体那个线程下的崩溃位置调用堆栈。
dump_syms在项目中已经提供,在breakpad-chrome_99\src\tools\windows\binaries下,minidump_stackwalk没有提供,需要我们自己编译。
ps: minidump_stackwalk最好是和写dump库(crash_generation_client/crash_generation_server)是一个版本,因为breakpad 版本不同,可能dump格式有小改动,如果写dump引用的是chrome_99分支,minidump_stackwalk是用的chrome_90分支代码编译的,解析时可能有问题,所以minidump_stackwalk需要我们自己编译。
打开breakpad-chrome_99\src\processor目录, 执行:
gyp --no-circular-check processor.gyp -G msvs_version=2017
执行后生成processor.sln项目文件:
1. 移除common项目
如果不移除common,在编译minidump_dump项目时会报:
严重性 代码 说明 项目 文件 行 禁止显示状态 错误 MSB8013 此项目不包含配置和平台组合 Debug|Win32。 minidump_dump D:\develop\vs2019\vs2019\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets 436
2. 编译wingetopt库
minidump_dump和minidump_stackwalk中使用到了getopt函数(unistd.h包含getopt声明),不过这个函数在windows下没有直接实现
unistd.h头文件经常在UNIX、Linux下看到,在windows下需要用其他库(wingetopt)实现:
下载https://github.com/alex85k/wingetopt, 下载后解压在CMakeLists.txt所在的目录下执行:
mkdir build
cd build
默认是64位:
cmake ..
如果想编译32位库:
cmake -G"Visual Studio 16 2019" -A Win32 ..
执行后会在build目录下生成wingetopt.sln,VS 打开wingetopt.sln,单独编译wingetopt:
编译成功后,在项目目录下生成Debug/Release文件夹,拿到wingetopt.lib,getopt.h头文件在源码目录src下。
3. 关闭将警告视为错误
将 (third_party)/libdisasm、minidump_dump、minidump_stackwalk、processor关闭将警告视为错误
1. 解决unistd.h找不到问题
准备好编译好的wingetopt项目: wingetopt.lib和getopt.h 库和头文件,分别设置minidump_dump、minidump_stackwalk项目包含目录、库目录和连接器->输入引入wingetopt:
例如我把 wingetopt.lib和getopt.h放到了processor文件夹下:
minidump_dump解决unistd.h问题
打开minidump_dump项目下的minidump_dump.cc文件,删除#include <unistd.h>,替换成#include <getopt.h>:
替换前
替换后
minidump_stackwalk解决unistd.h问题
minidump_stackwalk解决unistd.h找不到问题和minidump_dump一样:
替换前
替换后
解决__except_handler4_common无法解析外部符号:
严重性 代码 说明 项目 文件 行 禁止显示状态
错误 LNK2019 无法解析的外部符号 __except_handler4_common,函数 __except_handler4 中引用了该符号 minidump_stackwalk C:\Users\Geng\Desktop\breakpad\2\breakpad-chrome_99\breakpad-chrome_99\src\processor\MSVCRTD.lib(chandler4gs.obj) 1
打开连接器->输入->附加依赖项,添加msvcrt库:
如果当前是Debug,添加msvcrtd.lib
如果当前是Release,添加msvcrt.lib
因为我是Debug下,所以是:msvcrtd.lib
实现BaseName函数:
breakpad windows下没有直接实现BaseName,需要自己实现:
minidump_dump实现BaseName:
编辑minidump_dump项目下minidump_dump.cc文件添加:
#include <Shlwapi.h>
......
char* basename(const char* path) {
const char* filename = PathFindFileNameA(path);
// 创建一个新的字符串副本
char* result = _strdup(filename);
return result;
}
static string BaseName(const string& path) {
char* path_tmp = strdup(path.c_str());
assert(path_tmp);
string result(basename(path_tmp));
free(path_tmp);
return result;
}
注意添加 Shlwapi.h 头文件,Shlwapi.h中声明了PathFindFileNameA函数。
链接器->输入->附加依赖项: 添加shlwapi.lib
修改Usage函数下的google_breakpad::BaseName(argv[0]).c_str());为:
修改前
修改后
再次重新编译,正常生成minidump_dump.exe
minidump_stackwalk实现BaseName:
添加shlwapi.lib库引用和minidump_dump一样,在链接器->输入->附加依赖项添加shlwapi.lib.
编辑minidump_stackwalk项目下minidump_stackwalk.cc文件添加:
#include <Shlwapi.h>
....
char* basename(const char* path) {
const char* filename = PathFindFileNameA(path);
// 创建一个新的字符串副本
char* result = _strdup(filename);
return result;
}
static string BaseName(const string& path) {
char* path_tmp = strdup(path.c_str());
assert(path_tmp);
string result(basename(path_tmp));
free(path_tmp);
return result;
}
修改Usage函数下的google_breakpad::BaseName(argv[0]).c_str());为
修改前
修改后
重新编译minidump_stackwalk,正常生成: