dmesg介绍

在介绍addr2line前需要学习下dmesg:
dmesg命令用于打印Linux系统开机启动信息,kernel会将开机信息存储在ring buffer中。您若是开机时来不及查看信息,可利用dmesg来查看(print or control the kernel ring buffer)。开机信息亦保存在/var/log/dmesg的文件里。

常用参数
格式:dmesg
查看开机信息。

格式:dmesg -c
清除开机信息,但/var/log/dmesg文件中仍然有这些信息。

实例一
dmesg消息重定向
dmesg >boot.messages
实例二:
浏览dmesg输出的信息
dmesg | less
实例三:
查看dmesg尾部信息
dmesg|tail
实例四:
打印并清除内核唤醒缓冲区
dmesg -c

使用add2line定位崩溃位置

演示代码 addr2line.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int *p = NULL;
    printf("%d\n",*p);
   
    return 0;
}

使用gcc -g -o addr2line addr2line.c
./addr2line 然后程序崩溃,使用dmesg我们可以看到有如下错误打印:

[   11.587001] IPv6: ADDRCONF(NETDEV_UP): virbr0: link is not ready
[   11.786787] virbr0: port 1(virbr0-nic) entered disabled state
[604940.336021] hrtimer: interrupt took 6538722 ns
[1015198.140008] add2line[30769]: segfault at 0 ip 0000000000400531 sp 00007ffd369f4200 error 4 in add2line[400000+1000]

最后一行我们可以看到有提示add2line segfault错误,可以根据指令寄存器ip中的内容定位具体出错的行数。
使用addr2line -e add2line 0000000000400531命令可以得到如下输出:
/root/workspce/add2line.c:8
然后我们去分析代码发现第8行存在错误。

addr2line如何获取出错的行数

addr2line如何找到的这一行呢。在可执行程序中都包含有调试信息,其中很重要的一份数据就是程序源程序的行号和编译后的机器代码之间的对应关系Line Number Table。DWARF格式的Line Number Table是一种高度压缩的数据,存储的是表格前后两行的差值,在解析调试信息时,需要按照规则在内存里重建Line Number Table才能使用。
使用如下命令:
readelf -w addr2line输出内容中Line Number Statements部分:

Line Number Statements:
  [0x0000002b]  Extended opcode 2: set Address to 0x40051d
  [0x00000036]  Special opcode 10: advance Address by 0 to 0x40051d and Line by 5 to 6
  [0x00000037]  Special opcode 118: advance Address by 8 to 0x400525 and Line by 1 to 7
  [0x00000038]  Special opcode 118: advance Address by 8 to 0x40052d and Line by 1 to 8
  [0x00000039]  Advance PC by constant 17 to 0x40053e
  [0x0000003a]  Special opcode 91: advance Address by 6 to 0x400544 and Line by 2 to 10
  [0x0000003b]  Special opcode 76: advance Address by 5 to 0x400549 and Line by 1 to 11
  [0x0000003c]  Advance PC by 2 to 0x40054b
  [0x0000003e]  Extended opcode 1: End of Sequence

我们可以看到0000000000400531在第8行。因为0x40052d开始已经是第8行。
参考文章