使用 addr2line 定位崩溃代码行
·
在 Linux/C++ 开发中,程序崩溃后经常只能拿到一个地址,例如:
0x55b3d2d1a5fe
这时候如果没有源码行号,排查问题会非常困难。
addr2line 是 GNU Binutils 提供的一个非常实用的工具,可以把“地址”转换成:
-
源文件
-
函数名
-
代码行号
非常适合:
-
core dump 分析
-
crash 日志定位
-
ASan/TSan/SIGSEGV 排查
-
线上问题快速定位
一、addr2line 基本作用
核心功能:
addr2line -e 可执行文件 地址
例如:
addr2line -e app 0x4011c3
输出:
/home/test/main.cpp:25
说明:
-
地址
0x4011c3 -
对应
main.cpp -
第 25 行
二、准备一个示例程序
创建:
#include <iostream>
void crash()
{
int *p = nullptr;
*p = 100;
}
void test()
{
crash();
}
int main()
{
test();
return 0;
}
文件名:
main.cpp
三、编译时必须带调试符号
这是很多人最容易遗漏的地方。
正确编译:
g++ -g -O0 main.cpp -o app
参数说明:
| 参数 | 作用 |
|---|---|
| -g | 生成调试符号 |
| -O0 | 关闭优化,避免行号混乱 |
如果没有 -g:
addr2line
将无法找到源码行号。
四、运行程序制造崩溃
执行:
./app
输出:
Segmentation fault (core dumped)
五、通过 gdb 获取崩溃地址
进入 gdb:
gdb ./app core
查看 backtrace:
(gdb) bt
示例输出:
#0 0x0000555555555169 in crash() at main.cpp:6
#1 0x0000555555555180 in test() at main.cpp:11
#2 0x0000555555555191 in main() at main.cpp:16
如果线上日志只有地址:
0x0000555555555169
就可以用 addr2line 分析。
六、使用 addr2line 定位源码
执行:
addr2line -e app 0x0000555555555169
输出:
/home/test/main.cpp:6
成功定位:
*p = 100;
七、显示函数名
增加:
-f
例如:
addr2line -f -e app 0x0000555555555169
输出:
crash
/home/test/main.cpp:6
含义:
-
第一行:函数名
-
第二行:源码位置
八、实际线上场景
很多线上日志类似:
signal 11 received
RIP: 0x7f12ab34cd91
或者:
backtrace:
0x55aa12345678
0x55aa12345789
0x55aa12345890
可以批量解析:
addr2line -f -C -e app \
0x55aa12345678 \
0x55aa12345789 \
0x55aa12345890
参数说明:
| 参数 | 作用 |
|---|---|
| -f | 显示函数名 |
| -C | 反解 C++ 符号 |
| -e | 指定 ELF 文件 |
九、C++ 符号反解
没有 -C:
_Z5crashv
加上:
-C
输出:
crash()
建议线上分析固定使用:
addr2line -f -C -e app 地址
十、配合 nm 使用
有时只有函数名,没有地址。
先查符号:
nm app | grep crash
输出:
0000000000001169 T _Z5crashv
然后:
addr2line -f -C -e app 0x1169
十一、为什么有时定位不准确
1. 编译时开启优化
例如:
-O2
会导致:
-
内联
-
指令重排
-
行号错乱
建议问题排查阶段:
-g -O0
2. strip 删除符号
如果执行:
strip app
调试符号会被删除。
此时:
??
:0
解决方法:
保留独立 debug 文件:
objcopy --only-keep-debug app app.debug
3. 地址来自共享库
如果崩溃来自:
libxxx.so
必须指定对应 so:
addr2line -e libxxx.so 地址
十二、线上推荐命令
推荐直接封装:
addr2line -f -C -p -e app 地址
参数:
| 参数 | 作用 |
|---|---|
| -f | 函数名 |
| -C | demangle |
| -p | 单行输出 |
示例:
addr2line -f -C -p -e app 0x555555555169
输出:
crash at /home/test/main.cpp:6
十三、批量解析脚本
实际工作中很常用。
#!/bin/bash
APP=./app
for addr in "$@"
do
addr2line -f -C -p -e $APP $addr
done
使用:
./decode.sh \
0x555555555169 \
0x555555555180
十四、与 ASan 配合使用
ASan 日志中常见:
#0 0x55d8e0b2d1c3 in crash()
有时 ASan 已经给出行号。
但如果没有:
addr2line -f -C -e app 地址
依然可以快速定位。
十五、总结
addr2line 是 Linux/C++ 崩溃分析中的基础工具。
核心流程:
崩溃地址
↓
addr2line
↓
源码文件 + 行号
推荐牢记:
addr2line -f -C -p -e app 地址
这是线上排查问题最常用的组合。
更多推荐
所有评论(0)