1 使用breakpad
1.1 安装breakpad
1.1.1 通过vcpkg安装breakpad
注:安装vcpkg时,如果报以下错误可能是电脑不能访问到vcpkg,可以多试几次:
curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
1.1.1.1 windows
git clone https://github.com/microsoft/vcpkg
cd vcpkg
bootstrap-vcpkg.bat
vcpkg install breakpad:x64-windows
1.1.1.2 mac
git clone https://github.com/microsoft/vcpkg
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg install breakpad
1.2 通过cmake加载breakpad
1.2.1 cmakelists.txt
cmake_minimum_required(VERSION 3.20)
IF (WIN32)
MESSAGE(STATUS "Now is windows")
set(VCPKG_ROOT F:/git/vcpkg)
ELSEIF (APPLE)
MESSAGE(STATUS "Now is Apple system.")
set(VCPKG_ROOT /Users/hualongzhang/work/vcpkg)
ENDIF ()
set(VCPKG_CMAKE ${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
set(CMAKE_TOOLCHAIN_FILE ${VCPKG_CMAKE})
project(testbreakpad)
set(CMAKE_CXX_STANDARD 17)
find_package(unofficial-breakpad CONFIG REQUIRED)
link_libraries(unofficial::breakpad::libbreakpad unofficial::breakpad::libbreakpad_client)
add_executable(untitled1 main.cpp)
1.3 代码中使用breakpad
#include <iostream>
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#include "client/windows/handler/exception_handler.h"
#elif defined(__APPLE__) && (defined(__GNUC__) || defined(__xlC__) || defined(__xlc__))
#include "client/mac/handler/exception_handler.h"
#endif
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
static bool minidumpCallback(const wchar_t* dump_path, const wchar_t* id,
void* context, EXCEPTION_POINTERS* exinfo,
MDRawAssertionInfo* assertion,
bool succeeded)
{
if (succeeded) {
std::wcout << "Mini Dump file: " << id << ".dump Path: " << dump_path << std::endl;
}
return succeeded;
}
static google_breakpad::ExceptionHandler eh(
L".", NULL, minidumpCallback, NULL,
google_breakpad::ExceptionHandler::HANDLER_ALL);
#else
static bool minidumpCallback(const char* dump_path, const char* id, void* context, bool succeeded)
{
if (succeeded) {
std::cout << "Mini Dump file: " << id << ".dump Path: " << dump_path << std::endl;
}
return succeeded;
}
static google_breakpad::ExceptionHandler eh(".", NULL, minidumpCallback, NULL, true, NULL);
#endif
void testCrash()
{
int* ptr = NULL;
*ptr = 0;
}
int main() {
testCrash();
return 0;
}
1.4 build
1.4.1 cmake编译
mkdir build
cd build
cmake ..
cmake --build .
1.4.2 开发工具加载vcpkg编译
参见vcpkg总览
1.5 run
运行代码崩溃,输出:
Mini Dump file: 449BD1E3-1ECA-4968-A25B-DD6D8328D849.dump Path: .
2 排查breakpad崩溃
2.1 windows
参见:Dump调试
2.2 mac
2.2.1 编译dump_syms
打开dump_syms目录:
/Users/hualongzhang/work/vcpkg/buildtrees/breakpad/src/0b5fa7f760-4cf530ff2b.clean/src/tools/mac/dump_syms
用xcode打开dump_syms工程,我本机路径如下:
dump_syms.xcodeproj
配置release:
Product > Scheme > Edit Scheme... > Run > Info > Build Configuration,选择Release
编译,并找到生成的dump_syms
,我本机路径如下:
/Users/hualongzhang/Library/Developer/Xcode/DerivedData/dump_syms-fptymbnkqoskmeasatvnedmomwzg/Build/Products/Release
拷贝dump_syms
到minidump_stackwalk
同级目录(方便操作),我本机路径如下:
/Users/hualongzhang/work/vcpkg/buildtrees/breakpad/src/0b5fa7f760-4cf530ff2b.clean/src/processor
2.2.2 分析dump
2.2.2.1 进入processor目录
先cd到processor
,即minidump_stackwalk
目录:
cd /Users/hualongzhang/work/vcpkg/buildtrees/breakpad/src/0b5fa7f760-4cf530ff2b.clean/src/processor
要想查看完整的崩溃堆栈,app和dylib都得生成符号文件,现以cppdetector来示例
2.2.2.2 生成app sym
以app名称生成sym文件:
./dump_syms /Users/hualongzhang/work/cppdetector/bin/cppdetector > cppdetector.sym
查看sym的信息:
head -n1 cppdetector.sym
输出的sym信息:MODULE mac x86_64 07E8CDAF8CCA3B22849636E1AF94D4D70 cppdetector
根据二进制文件名称:cppdetector
和上一步输出的:07E8CDAF8CCA3B22849636E1AF94D4D70
创建目录:
mkdir -p ./symbols/cppdetector/07E8CDAF8CCA3B22849636E1AF94D4D70
将cppdetector.sym移动到新创建的目录中
mv cppdetector.sym ./symbols/cppdetector/07E8CDAF8CCA3B22849636E1AF94D4D70
2.2.2.3 生成dylib sym
./dump_syms /Users/hualongzhang/work/cppdetector/bin/libdetector_core.dylib > libdetector_core.dylib.sym
head -n1 libdetector_core.dylib.sym
mkdir -p ./symbols/libdetector_core.dylib/0E1F07DAE411385F9C409F87CDD8C9510
mv libdetector_core.dylib.sym ./symbols/libdetector_core.dylib/0E1F07DAE411385F9C409F87CDD8C9510
2.2.2.4 输出堆栈
通过minidump_stackwalk
命令生成堆栈
./minidump_stackwalk /Users/hualongzhang/work/cppdetector/bin/A61B41CB-A7FE-46DD-881D-774AC907AC5C.dmp ./symbols
2.2.2.5 终端结果如下:
➜ processor git:(master) ✗ ./dump_syms /Users/hualongzhang/work/cppdetector/bin/cppdetector > cppdetector.sym
➜ processor git:(master) ✗ head -n1 cppdetector.sym
MODULE mac x86_64 07E8CDAF8CCA3B22849636E1AF94D4D70 cppdetector
➜ processor git:(master) ✗ mkdir -p ./symbols/cppdetector/07E8CDAF8CCA3B22849636E1AF94D4D70
➜ processor git:(master) ✗ mv cppdetector.sym ./symbols/cppdetector/07E8CDAF8CCA3B22849636E1AF94D4D70
➜ processor git:(master) ✗ ./dump_syms /Users/hualongzhang/work/cppdetector/bin/libdetector_core.dylib > libdetector_core.dylib.sym
➜ processor git:(master) ✗ head -n1 libdetector_core.dylib.sym
MODULE mac x86_64 0E1F07DAE411385F9C409F87CDD8C9510 libdetector_core.dylib
➜ processor git:(master) ✗ mkdir -p ./symbols/libdetector_core.dylib/0E1F07DAE411385F9C409F87CDD8C9510
➜ processor git:(master) ✗ mv libdetector_core.dylib.sym ./symbols/libdetector_core.dylib/0E1F07DAE411385F9C409F87CDD8C9510
➜ processor git:(master) ✗ ./minidump_stackwalk /Users/hualongzhang/work/cppdetector/bin/6419528F-7020-4DC1-8FB5-6B9C58395FEE.dmp ./symbols > dump.log
2.2.2.6 自动化脚本
上述示例过程如果全部手动执行及其繁琐,特别是拥有多个库的大型项目更是如此,有必要通过sh脚本将其自动化,将如下自动化脚本保存成:dumpanalyzer.sh
#!/bin/bash
if [ $# != 3 ] ; then
echo "USAGE: $0 TARGET_DIR DMP_NAME OUTPUT_NAME"
echo " e.g.: $0 appdir 4391167F-D2E0-4CD1-973D-BED11A9A7FE9.dmp callstack.log"
exit 1;
fi
#获取输入参数
target_dir=$1
dmp_file_name=$2
output_file_name=$3
target_file_name=${target_dir##*/}
echo $target_file_name
getSymbol() {
echo "@getSymbol: start get symbol: $target_dir/$target_file_name"
./dump\_syms $target_dir/$target_file_name > $target_file_name'.sym'
}
createSymDir() {
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
#创建特定的目录结构,并将符号文件移进去
mkdir -p ./symbols/$target_file_name/$version_number
mv $sym_file_name ./symbols/$target_file_name/$version_number
}
redirectStackToFile() {
#将堆栈跟踪信息重定向到文件中
minidump_stackwalk $dmp_file_name ./symbols > $output_file_name
}
getStackTrace() {
for target_file_name in `ls $1`
do
echo "path: $target_file_name"
getSymbol $target_file_name
createSymDir $target_file_name
done
redirectStackToFile
}
main() {
echo $target_path
getStackTrace $target_dir
}
#运行main
main
调用方式:
./dumpanalyzer.sh /Users/hualongzhang/work/cppdetector/bin /Users/hualongzhang/work/cppdetector/bin/4391167F-D2E0-4CD1-973D-BED11A9A7FE9.dmp callstack.log
第一个参数是app目录,第二个参数是dump文件路径,第三方参数是调用堆栈文件名
2.2.2.7 堆栈信息
堆栈信息如下,可以定位出崩溃的函数,但是:
Operating system: Mac OS X
11.1.0 20C69
CPU: amd64
family 6 model 158 stepping 10
12 CPUs
GPU: UNKNOWN
Crash reason: EXC_BAD_ACCESS / KERN_INVALID_ADDRESS
Crash address: 0x0
Process uptime: 0 seconds
Thread 0 (crashed)
0 libdetector_core.dylib!CPPPath::CPPPath(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 0x55
rax = 0x0000000000000000 rdx = 0x00007ffeea448ee8
rcx = 0x00007ffeea4491c0 rbx = 0x0000000000000000
rsi = 0x00007ffeea448ef0 rdi = 0x00007ffeea4491d8
rbp = 0x00007ffeea449160 rsp = 0x00007ffeea448f70
r8 = 0x0000000000000005 r9 = 0x0000000000000005
r10 = 0x00007fcc64500000 r11 = 0x0000000000000000
r12 = 0x0000000000000000 r13 = 0x0000000000000000
r14 = 0x0000000000000000 r15 = 0x0000000000000000
rip = 0x00000001058ad4f5
Found by: given as instruction pointer in context
1 libdetector_core.dylib!CPPPath::CPPPath(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 0x1d
rbp = 0x00007ffeea449180 rsp = 0x00007ffeea449170
rip = 0x00000001058ade7d
Found by: previous frame's frame pointer
2 libdetector_core.dylib!DetectorContext::makePathPairFromDir(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 0x41
rbp = 0x00007ffeea449210 rsp = 0x00007ffeea449190
rip = 0x00000001059378b1
Found by: previous frame's frame pointer
3 libdetector_core.dylib!DetectorContext::detect(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 0xa9
rbp = 0x00007ffeea449300 rsp = 0x00007ffeea449220
rip = 0x0000000105937199
Found by: previous frame's frame pointer
4 libdetector_core.dylib!DetectorContext::detectWithRuleNames(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > > > const&) + 0x1e0
rbp = 0x00007ffeea449490 rsp = 0x00007ffeea449310
rip = 0x00000001059367d0
Found by: previous frame's frame pointer
5 libdetector_core.dylib!DetectorContext::detectAll(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 0x64
rbp = 0x00007ffeea449510 rsp = 0x00007ffeea4494a0
rip = 0x00000001059365b4
Found by: previous frame's frame pointer
6 cppdetector!main + 0xcdf
rbp = 0x00007ffeea449a00 rsp = 0x00007ffeea449520
rip = 0x00000001057bc15f
Found by: previous frame's frame pointer
7 libdyld.dylib + 0x15621
rbp = 0x00007ffeea449a10 rsp = 0x00007ffeea449a10
rip = 0x00007fff20379621
Found by: previous frame's frame pointer
Loaded modules:
0x1057b6000 - 0x1057e9fff cppdetector ??? (main)
0x105860000 - 0x105bbffff libdetector_core.dylib ???
0x7fff20094000 - 0x7fff20095fff libsystem_blocks.dylib ???
0x7fff20096000 - 0x7fff200cbfff libxpc.dylib ???
0x7fff200cc000 - 0x7fff200e3fff libsystem_trace.dylib ???
0x7fff200e4000 - 0x7fff20182fff libcorecrypto.dylib ???
0x7fff20183000 - 0x7fff201affff libsystem_malloc.dylib ???
0x7fff201b0000 - 0x7fff201f4fff libdispatch.dylib ???
0x7fff201f5000 - 0x7fff2022dfff libobjc.A.dylib ???
0x7fff2022e000 - 0x7fff20230fff libsystem_featureflags.dylib ???
0x7fff20231000 - 0x7fff202b9fff libsystem_c.dylib ???
0x7fff202ba000 - 0x7fff2030ffff libc++.1.dylib ???
0x7fff20310000 - 0x7fff20328fff libc++abi.dylib ???
0x7fff20329000 - 0x7fff20357fff libsystem_kernel.dylib ???
0x7fff20358000 - 0x7fff20363fff libsystem_pthread.dylib ???
0x7fff20364000 - 0x7fff2039efff libdyld.dylib ??? (WARNING: No symbols, libdyld.dylib, 000000000000000000000000000000000)
0x7fff2039f000 - 0x7fff203a8fff libsystem_platform.dylib ???
0x7fff203a9000 - 0x7fff203d4fff libsystem_info.dylib ???
0x7fff203d5000 - 0x7fff20870fff CoreFoundation ???
0x7fff2255f000 - 0x7fff227c0fff libicucore.A.dylib ???
0x7fff227c1000 - 0x7fff227cafff libsystem_darwin.dylib ???
0x7fff22bdb000 - 0x7fff22be6fff libsystem_notify.dylib ???
0x7fff24b36000 - 0x7fff24b44fff libsystem_networkextension.dylib ???
0x7fff24ba2000 - 0x7fff24bb8fff libsystem_asl.dylib ???
0x7fff262a0000 - 0x7fff262a7fff libsystem_symptoms.dylib ???
0x7fff282f2000 - 0x7fff28302fff libsystem_containermanager.dylib ???
0x7fff28ffe000 - 0x7fff29001fff libsystem_configuration.dylib ???
0x7fff29002000 - 0x7fff29006fff libsystem_sandbox.dylib ???
0x7fff29d09000 - 0x7fff29d0bfff libquarantine.dylib ???
0x7fff2a28a000 - 0x7fff2a28efff libsystem_coreservices.dylib ???
0x7fff2a4a5000 - 0x7fff2a4ecfff libsystem_m.dylib ???
0x7fff2a4ee000 - 0x7fff2a4f3fff libmacho.dylib ???
0x7fff2a510000 - 0x7fff2a51bfff libcommonCrypto.dylib ???
0x7fff2a51c000 - 0x7fff2a526fff libunwind.dylib ???
0x7fff2a527000 - 0x7fff2a52efff liboah.dylib ???
0x7fff2a52f000 - 0x7fff2a539fff libcopyfile.dylib ???
0x7fff2a53a000 - 0x7fff2a541fff libcompiler_rt.dylib ???
0x7fff2a542000 - 0x7fff2a544fff libsystem_collections.dylib ???
0x7fff2a545000 - 0x7fff2a547fff libsystem_secinit.dylib ???
0x7fff2a548000 - 0x7fff2a54afff libremovefile.dylib ???
0x7fff2a54b000 - 0x7fff2a54bfff libkeymgr.dylib ???
0x7fff2a54c000 - 0x7fff2a553fff libsystem_dnssd.dylib ???
0x7fff2a554000 - 0x7fff2a559fff libcache.dylib ???
0x7fff2a55a000 - 0x7fff2a55bfff libSystem.B.dylib ???
0x7fff2a55c000 - 0x7fff2a55ffff libfakelink.dylib ???
0x7fff2a560000 - 0x7fff2a560fff SoftLinking ???
0x7fff2d98d000 - 0x7fff2d98dfff liblaunch.dylib ???
0x7fff2fe3a000 - 0x7fff2fe3afff libsystem_product_info_filter.dylib ???
0x7fff5dc88000 - 0x7fff5dc97fff libSimplifiedChineseConverter.dylib ???
备注:
找到模块的符号文件,输出如下:
0x1057b6000 - 0x1057e9fff cppdetector ??? (main)
0x105860000 - 0x105bbffff libdetector_core.dylib ???
没有找到模块的符号文件会输出:
libdyld.dylib ??? (WARNING: No symbols, libdyld.dylib, 000000000000000000000000000000000)