Breakpad 是 Google 用 C++ 編寫的一個開源、跨平臺的崩潰報告系統(tǒng),它支持 Windows、Linux 和 macOS,并提供了一個上傳器,可以在進程崩潰時向一個配置好的 URL 提交 minidump 文件。
目前,有很多大型項目都在使用 Breakpad,例如:Google Chrome、Firefox、Google Picasa、Camino、Google Earth 等。
主頁:https://chromium.googlesource.com/breakpad/breakpad/
文檔:https://chromium.googlesource.com/breakpad/breakpad/+/HEAD/docs
GitHub 地址:https://github.com/google/breakpad
1
工作原理
BreakPad 工作原理:
其中,包含了三個主要組件:
Breakpad client:是一個庫(即:libbreakpad_client.a),將來要集成到我們的程序中。用于寫 minidump 文件,捕獲當前線程的狀態(tài),以及可執(zhí)行文件/共享庫的標識。
Breakpad 符號轉儲工具:是一個程序(即:dump_syms),用于讀取由編譯器產生的調試信息,并以 Breakpad 自己的格式生成一個符號文件。
Breakpad minidump 處理器:是一個程序(即:minidump_stackwalk),用于讀取 minidump 文件和符號文件,并生成一個可讀的 C/C++ 堆棧跟蹤。
2
編譯安裝
下載 Breakpad 源碼;
由于 Breakpad 依賴于 LSS,所以還需要下載它(地址:https://github.com/adelshokhy112/linux-syscall-support);
將 LSS 中的 linux_syscall_support.h
文件放至 breakpad/src/third_party/lss/
目錄下。
編譯 Breakpad,步驟非常簡單:
$ cd breakpad
$ ./configure && make
$ make
$ sudo make install
成功之后,會生成 libbreakpad_client.a
庫文件,以及 dump_syms
、minidump_stackwalk
等程序。
3
將 Breakpad 集成到程序中
和其他第三方庫的用法一樣,要將 Breakpad 集成到程序中,先要鏈接生成的庫 libbreakpad_client.a
,并設置包含路徑為 breakpad/src/
,接下來就可以 include 頭文件了。
為了生成 mindump 文件,我們需要實例化一個 ExceptionHandler
對象,并提供一個存儲 mindump 的路徑,以及一個回調函數來接收關于已寫入的 mindump 的信息:
#include 'client/linux/handler/exception_handler.h'
#include <iostream>
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
void* context,
bool succeeded)
{
std::cout << 'Dump path:' << descriptor.path() << std::endl;
return succeeded;
}
void crash()
{
int* a = nullptr;
*a = 1;
}
int main(int argc, char* argv[])
{
google_breakpad::MinidumpDescriptor descriptor('/tmp');
google_breakpad::ExceptionHandler eh(descriptor, nullptr, dumpCallback, nullptr, true, -1);
crash();
return 0;
}
編譯運行這個程序,會在 /tmp/
目錄下生成一個 minidump 文件,并在退出之前打印該文件的路徑。
注意:在使用 Breakpad 時,需要目標程序內包含調試信息,這樣 dump_syms
工具才能從中解析出調試符號。而在默認情況下,Release 模式編譯的程序是并不包含調試信息,若想包含,需要在編譯程序時添加 -g
選項。
4
生成可讀的堆棧跟蹤
帶調試信息的目標程序有了,minidump 文件有了,dump_syms
和 minidump_stackwalk
等工具也有了,現在要做的就是生成有用的堆棧跟蹤信息了!
1. 生成符號文件
Breakpad 要求我們將目標程序中的調試符號轉換為文本格式的符號文件,這一步需要通過 dump_syms
工具來完成。
假設,我們的程序名為 test
,執(zhí)行以下命令,便會生成一個名為 test.sym
的符號文件:
$ dump_syms ./test > test.sym
2. 將符號文件放在特定目錄下
為了使用這些符號,需要將生成的符號文件放在特定的目錄結構中:
查看符號文件,第一行包含了生成目錄結構所需的信息:
$ head -n1 test.sym
MODULE Linux x86_64 95E20E34BE203CB093B675D606A7D7D20 test
創(chuàng)建以上目錄結構,并將符號文件移動到該路徑下:
$ mkdir -p ./symbols/test/95E20E34BE203CB093B675D606A7D7D20
$ mv test.sym ./symbols/test/95E20E34BE203CB093B675D606A7D7D20/
3. 生成堆棧跟蹤信息
當一切準備就緒,minidump_stackwalk
工具就派上用場了。
只需將 mindump 文件和符號路徑作為命令行參數傳遞給它,便能生成堆棧跟蹤信息了。為了便于分析,可以將輸出重定向至文件中:
$ minidump_stackwalk /tmp/48596758-1d7a-4318-15edb4af-a9186ad7.dmp ./symbols > error.log
5
定位 Crash 所在位置
打開 error.log
,搜索關鍵字“crashed”
,一般與它最接近的一行,就是發(fā)生崩潰時程序的堆棧信息:
可以很清楚地看到,崩潰發(fā)生在 main.cpp 的第 16 行。
查看我們的測試程序,與預期結果一樣:
至此,Crash 所在位置已經準確地定位到了,趕緊抓緊時間修改吧!
6
將上述過程腳本化
由于以上過程會經常執(zhí)行,可以將其做成自動化腳本,不僅可有效防止步驟命令的遺忘,而且極大的加快了操作速度。
新建一個腳本 dumptool.sh
,內容如下:
#!/bin/bash
if [ $# != 3 ] ; then
echo 'USAGE: $0 TARGET_NAME DMP_NAME OUTPUT_NAME'
echo ' e.g.: $0 test 48596758-1d7a-4318-15edb4af-a9186ad7.dmp error.log'
exit 1;
fi
#獲取輸入參數
target_file_name=$1
dmp_file_name=$2
output_file_name=$3
getSymbol() {
echo '@getSymbol: start get symbol'
dump\_syms ./$target_file_name > $target_file_name'.sym'
}
getStackTrace() {
echo '@getStackTrace: start get StackTrace'
sym_file_name=$target_file_name'.sym'
#獲取符號文件中的第一行
line1=`head -n1 $sym_file_name`
#從第一行字符串中獲取版本號
OIFS=$IFS; IFS=' '; set -- $line1; aa=$1;bb=$2;cc=$3;dd=$4; IFS=$OIFS
version_number=$dd
#創(chuàng)建特定的目錄結構,并將符號文件移進去
mkdir -p ./symbols/$target_file_name/$version_number
mv $sym_file_name ./symbols/$target_file_name/$version_number
#將堆棧跟蹤信息重定向到文件中
minidump_stackwalk $dmp_file_name ./symbols > $output_file_name
}
main() {
getSymbol
if [ $? == 0 ]
then
getStackTrace
fi
}
#運行main
main
使用比較簡單,先為腳本增加可執(zhí)行權限:
$ chmod +x ./dump_tool.sh
然后執(zhí)行下述命令,將二進制文件、minidump 文件、輸出日志文件作為參數傳遞給它:
$ ./dumptool.sh ./test /tmp/48596758-1d7a-4318-15edb4af-a9186ad7.dmp error.log
完成之后,所有的堆棧跟蹤信息就會被輸出到 error.log
文件中了。
- EOF -