Breakpad在Windows,Linux双平台编译、集成以及dump文件的分析

Breakpad在Windows,Linux双平台编译、集成以及dump文件的分析

1、Windows平台

Windows平台上非常好的参考文档:

https://r12f.com/posts/google-breakpad-1-introduction-with-windows/

https://r12f.com/posts/google-breakpad-2-implementations-on-windows/

1.1、源码下载及编译

平台:Win10 + Intel

IDE:Visual Studio 2017

1.1.1、源码下载

github上的main分支一致编译不通过也没有gyp用于生成VS的sln。

当前我们使用github上breakpad的master分支即可

1.1.2、源码编译依赖

源码编译需要GYP生成VS打开的sln文件,GYP又依赖于python2.7.x版本

依赖关系:python2.7.x -> GYP -> Visual Studio;可顺序安装

详细请见:https://blog.csdn.net/zyhse/article/details/112577340博客

1.1.3、编译

breakpad只需要生成client中的项目,目录:breakpad-master\src\client\windows

该目录下可以看到breakpad_client.gyp文件(2023年后github main分支上已经没有该文件了)

使用安装好的GYP生成SLN(若gyp执行失败,大概率是python版本不对

gyp --no-circular-check "./breakpad_client.gyp" -Dwin_release_RuntimeLibrary=2 -Dwin_debug_RuntimeLibrary=3

使用vs2017打开调整为Release|x64编译

请添加图片描述

生成如上静态库

1.2、集成到现有代码中

breakpad集成到现有VS c++代码中非常的容易

1.2.1、包含头文件以及lib库

头文件:项目属性 -> C/C++ -> 附加包含目录 添加文件文件路径

lib库: 项目属性 -> 链接器 -> 常规 -> 附加库目录 添加库目录

​ 项目属性 -> 链接器 -> 输入 -> 附加依赖项 添加 common.lib crash_generation_client.lib crash_generation_server.lib exception_handler.lib 或使用代码方式添加

#pragma comment(lib, "common.lib")
#pragma comment(lib, "crash_generation_client.lib")
#pragma comment(lib, "crash_generation_server.lib")
#pragma comment(lib, "exception_handler.lib")

1.2.2、使用breakpad C/S模式增加下面代码段至项目中就好

// 头文件
#include "client/windows/handler/exception_handler.h"
#include "client/windows/crash_generation/crash_generation_server.h"
#include "client/windows/crash_generation/client_info.h"

// lib库
#pragma comment(lib, "common.lib")
#pragma comment(lib, "crash_generation_client.lib")
#pragma comment(lib, "crash_generation_server.lib")
#pragma comment(lib, "exception_handler.lib")

// server回调函数
void onClientConnected(void* context,
	const google_breakpad::ClientInfo* client_info) 
{
}

void onClientDumpRequest(void* context,
	const google_breakpad::ClientInfo* client_info,
	const std::wstring* file_path)
{
}

void onClientExited(void* context,
	const google_breakpad::ClientInfo* client_info) {
}

// client端回调函数,写完minidump后的回调函数
bool onMinidumpDumped(const wchar_t* dump_path, const wchar_t* id,
	void* context, EXCEPTION_POINTERS* exinfo,
	MDRawAssertionInfo* assertion,
	bool succeeded) {
	return succeeded;
}

bool InitBreakpad()
{
	if (_wmkdir(s_strCrashDir.c_str()) && (errno != EEXIST)) {
		return false;
	}

	google_breakpad::CrashGenerationServer *pCrashServer =
		new google_breakpad::CrashGenerationServer(s_pPipeName,
			NULL,
			onClientConnected,
			NULL,
			onClientDumpRequest,
			NULL,
			onClientExited,
			NULL,
			NULL,
			NULL,
			true,
			&s_strCrashDir);

	if (pCrashServer == NULL) {
		return false;
	}

	// 如果已经服务端已经启动了,此处启动会失败
	if (!pCrashServer->Start()) {
		delete pCrashServer;
		pCrashServer = NULL;
	}

	google_breakpad::ExceptionHandler *pCrashHandler =
		new google_breakpad::ExceptionHandler(s_strCrashDir,
			nullptr,
			onMinidumpDumped,
			NULL,
			google_breakpad::ExceptionHandler::HANDLER_ALL,
			MiniDumpNormal,
			(pCrashServer == NULL) ? s_pPipeName : NULL, // 如果是服务端,则直接使用进程内dump
			NULL);

	if (pCrashHandler == NULL) {
		return false;
	}
	return true;
}

int main(...)
{
    // ....
    bool ret = InitBreakpad();
    // ... 项目当前的代码 ...
}

1.3、dump文件分析

Windows上dump文件分析非常的简单,要么Visual Studio,要么Windbg。

下面我们使用windbg进行分析dump文件;

注意:项目生成exe文件必须要有pdb文件,该文件主要存储symbols的,如果没有请修改项目属性;
请添加图片描述
请添加图片描述

打开这两个属性就可以生成pdb文件了。

使用windbg打开dmp文件,配置好Symbol file path路径(就是该dump的exe对应的pdb文件路径)以及Source file path路径(该dump的exe的源码路径)
请添加图片描述

点击!analyze -v就行
请添加图片描述

非常容易的看到保存的代码行
请添加图片描述

2、Linux平台

linux平台与windows平台有些许不同。

首先是依赖项;

其次是分析dump文件的工具(breakpad自带的);

2.1、依赖项

需要去google上下载lss依赖包放在/breakpad-master/src/third_party/lss目录下,里面主要就是linux_syscall_support.h文件在代码中依赖了

2.2、分析工具

在linux上编译的breakpad会生成自带的分析工具minidump_stackwalk以及symbol生成工具dump_syms

  1. minidump_stackwalk:./breakpad-master/src/processor目录下
  2. dump_syms: ./breakpad-master/src/tools/linux/dump_syms目录下

2.3、具体使用

#include <unistd.h>
#include <thread>
#include <iostream>
#include "client/linux/handler/exception_handler.h"

static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
void* context, bool succeeded) {
  printf("Dump path: %s\n", descriptor.path());
  return succeeded;
}

void crash() { volatile int* a = (int*)(NULL); *a = 1; }

void task1(string msg)
{
    std::cout << "task1 says: " << msg << std::endl;
    crash();
}

int main(int argc, char* argv[]) {
  google_breakpad::MinidumpDescriptor descriptor("/tmp/hzh");
  google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1);
  std::thread t1(task1, "Hello");
  t1.detach();
  sleep(2);
  return 0;
}
$ g++ -g a.cpp -I/home/hzh/soft/softy/breakpad/include/breakpad -L/home/hzh/soft/softy/breakpad/lib -lbreakpad -lbreakpad_client -pthread -o test

1,运行test,会崩溃并产生 179cac63-2e41-4de0-09e8b58c-56069f80.dmp 文件。

2,从可执行程序生成符号表:

$ /home/xxx/soft/softy/breakpad/bin/dump_syms test >> test.sym

3,建立一个目录结构,目录名必须为“可执行程序的名字”,然后再该目录里面建立一个目录,名字为 test.sym 的第一行的某个数据,具体如下:

$ head -n1 test.sym

得到: MODULE Linux x86_64 A35260606902350047A2A3559926FE410 test ,我们就要 A35260606902350047A2A3559926FE410 作为目录名。

$  mkdir -p ./symbols/test/A35260606902350047A2A3559926FE410

4,将 test.sym 移动到目录里:

$  mv test.sym symbols/test/A35260606902350047A2A3559926FE410/

5,开始分析:

$  /home/xxx/soft/softy/breakpad/bin/minidump_stackwalk 179cac63-2e41-4de0-09e8b58c-56069f80.dmp ./symbols