TIPS总结
使用AS自带的minidump_stackwalk
不一定要下载Breakpad的源码去编译才可以使用minidump_staackwalk去读/解析minidump文件的内容和查看崩溃崩溃信息,我们可以使用Android Studio自带的minidump_stackwalk。如在macOS下,minidump_stackwalk的位置如下
# AS安装目录下的bin/lldb/bin下
Applications/Android Studio.app/Contents/bin/lldb/bin/minidump_stackwalk
或
Applications/Android Studio.app/Contents/plugins/android-ndk/resources/lldb/bin
各个平台下编译的dump_syms只能处理各个平台的库的符号
breakpad 是跨平台的,支持linux、window和Mac os系统,不同平台上的编译配置也是不同的
- linux 平台编译出来的dump_syms 仅能再linux上运行,来处理该平台的动态库(*.so)和符号信息
- macOS 平台编译出来的dump_syms 仅能再mac OS 上运行,来处理该平台的动态库(*.dylib)的符号信息
- window 平台编译出来的dump_syms,仅能在Window上运行,来处理该平台的动态库(*.dll)的符号信息
如果在macOS使用dump_sym对动态库so文件,生成调试信息,为提示如下出错信息
file is neither a fat binary file nor a Mach-O object file
minidump_stackwalk + addr2line
使用minidump_stackwalk读取minidump文件时没有dump_sysms生成的调试符号信息时,输出的崩溃堆栈会缺少函数符号和行号等信息,
// -s 输出崩溃堆栈信息
$ /Applications/Android\ Studio.app/Contents/bin/lldb/bin/minidump_stackwalk -s 887982de-b87e-4d1a-bce21485-c7ae0dab.dmp >>result.log
//如下是输出示例的部分内容
Operating system: Android
0.0.0 Linux 4.14.186+ #1 SMP PREEMPT Tue Jul 19 21:15:54 CST 2022 armv8l
CPU: arm
ARMv1 ARM part(0x4100d450) features: half,thumb,fastmult,vfpv2,edsp,neon,vfpv3,tls,vfpv4,idiva,idivt
8 CPUs
Crash reason: SIGSEGV
Crash address: 0x0
Process uptime: not available
Thread 47 (crashed)
0 0x0
r0 = 0xede684a0 r1 = 0x00000000 r2 = 0x62fa1229 r3 = 0x00000000
r4 = 0xb29bcebc r5 = 0x00000001 r6 = 0xe0d05704 r7 = 0xb29bcfc8
r8 = 0xb29bcf00 r9 = 0xffffffff r10 = 0xe0d05704 r12 = 0xc9c029e4
fp = 0xe0d056ec sp = 0xb29bce08 lr = 0xb3e8a7e3 pc = 0x00000000
Found by: given as instruction pointer in context
Stack contents:
b29bce08 bc ce 9b b2 00 00 00 00 45 92 9f c5 00 00 00 00 ........E.......
b29bce18 10 cf 9b b2 04 57 d0 e0 00 00 00 00 01 00 00 00 .....W..........
b29bce28 5c 72 e4 ea 61 60 e4 b3 \r..a`..
Possible instruction pointers:
1 libXXX.so + 0x3f05f //这个址址值是库的相对地址了,结合使用addr2line的时候不会做转化了!
sp = 0xb29bce30 pc = 0xb3e46061
Found by: stack scanning
Stack contents:
b29bce30 c8 00 00 00 38 ac f2 b3 ....8...
Possible instruction pointers:
2 libXXX.so + 0x123c36
sp = 0xb29bce38 pc = 0xb3f2ac38
Found by: stack scanning
此时如果我们有对应的带有调试信息版本的so( 即with debug_info, not stripped的版本),我们就可以用addr2line等命令行工具去手动的解析地址对应的函数名和行号。即有dump文件跟so就可以手动解析崩溃堆栈
addr2line的使用示例如下
llvm-addr2line -p -f -C -e libXXX.so 0x3f05f 0x123c36
// llvm-addr2line is /Users/username/Library/Android/sdk/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-addr2line
有关 debug identifier
关键的代码是file_id.cc下的FileID::ConvertIdentifierToUUIDString跟FileID::ElfFileIdentifierFromMappedFile这两个方法,debug identifiers的值在非PDB格式下是读取so的build_id的值,如果没有build_id就读取so代码段的前面部份字节的值,然后对读取后的值转为UUID的格式
bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
wasteful_vector<uint8_t>& identifier) {
// Look for a build id note first.
if (FindElfBuildIDNote(base, identifier))
return true;
// Fall back on hashing the first page of the text section.
return HashElfTextSection(base, identifier);
}
// These three functions are not ever called in an unsafe context, so it's OK
// to allocate memory and use libc.
static string bytes_to_hex_string(const uint8_t* bytes, size_t count) {
string result;
for (unsigned int idx = 0; idx < count; ++idx) {
char buf[3];
snprintf(buf, sizeof(buf), "%02X", bytes[idx]);
result.append(buf);
}
return result;
}
// static
string FileID::ConvertIdentifierToUUIDString(
const wasteful_vector<uint8_t>& identifier) {
uint8_t identifier_swapped[kMDGUIDSize] = { 0 };
// Endian-ness swap to match dump processor expectation.
memcpy(identifier_swapped, &identifier[0],
std::min(kMDGUIDSize, identifier.size()));
uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
*data1 = htonl(*data1);
uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
*data2 = (*data2);
uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
*data3 = htons(*data3);
return bytes_to_hex_string(identifier_swapped, kMDGUIDSize);
}
// static
string FileID::ConvertIdentifierToString(
const wasteful_vector<uint8_t>& identifier) {
return bytes_to_hex_string(&identifier[0], identifier.size());
}
arm-linux-androideabi-addr2line -C -f -e ${SOPATH} ${Address}
# -C -f //打印错误行数所在的函数名称
# -e //打印错误地址的对应路径及行数
# ${SOPATH} //so库路径
# ${Address} //需要转换的堆栈错误信息地址,可以添加多个,但是中间要用空格隔开
有关于动态库
//TODO
有关于breakpad
//TODO
有关于崩溃日志分析
//TODO