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_dumpminidump_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,正常生成:

 

posted @ 2024-12-07 22:45  耿明岩  阅读(49)  评论(0编辑  收藏  举报
希望能帮助到你,顺利解决问题! ...G(^_−)☆