生产环境定位问题往往遇到各种限制,比如事后日志发现程序是收到SIGSEGV退出了(segment fault),但是因为:
- 没配置limit
- 存储空间不够了
- 其他未知原因
没有正常生成core文件,那么这会如何定位问题呢?
场景构造
测试程序ctest,明显的空指针错误。
代码语言:javascript复制#include <stdio.h>
struct Ctest
{
int i;
};
int main()
{
printf("hin");
printf("hin");
struct Ctest *c;
c->i = 10;
printf("hi %dn", c->i);
printf("hin");
return 0;
}
执行后:
代码语言:javascript复制$ ./ctest
hi
hi
Segmentation fault (core dumped)
但是没有生成core文件。
定位方法1
系统中的任何程序收到SIGSEGV都会记录在内核日志中:
代码语言:javascript复制dmesg -T
[Mon Jul 11 15:51:08 2022] ctest[9040]: segfault at 0 ip 0000000000401162 sp 00007ffcc0d3a0a0 error 6 in ctest[401000 1000]
这条日志的含义:
9040
:进程号segfault
:错误名称ip 0000000000401162
:instruction pointer就是代码执行位置的指针sp 00007ffcc0d3a0a0
:程序执行栈指针error 6
:Architecture-specific flags; see arch/*/mm/fault.c for your platform.
最后的error 6是按二进制来使用的,含义需要找对应平台的fault.c中具体查询。‘’
我们关心的只是程序运行到哪里报错了,所以只需要关注ip 0000000000401162
$ addr2line -e ./ctest 0000000000401162
/tmp/ctest.c:14
可以看到程序在执行14行的时候,收到内核的SIGSEGV。
定位方法2
dump二进制编译信息objdump -d ./ctest | more
可以看到401162位置上的movl $0xa,(%rax)
,直接看汇编代码:
- 将立即数0xa(十进制的10)赋给寄存器rax记录的地址
- 但是rax并没有分配任何地址,所以使用非法地址越界报错
(复杂代码的汇编可读性很差,建议使用方法1)
代码语言:javascript复制objdump -d ./ctest | more
0000000000401142 <main>:
401142: 55 push %rbp
401143: 48 89 e5 mov %rsp,%rbp
401146: 48 83 ec 10 sub $0x10,%rsp
40114a: bf 10 20 40 00 mov $0x402010,�i
40114f: e8 dc fe ff ff callq 401030 <puts@plt>
401154: bf 10 20 40 00 mov $0x402010,�i
401159: e8 d2 fe ff ff callq 401030 <puts@plt>
40115e: 48 8b 45 f8 mov -0x8(%rbp),%rax
401162: c7 00 0a 00 00 00 movl $0xa,(%rax) # 将立即数0xa(十进制的10)赋给寄存器rax记录的地址
# 但是rax并没有分配任何地址,所以使用非法地址越界报错
401168: 48 8b 45 f8 mov -0x8(%rbp),%rax
40116c: 8b 00 mov (%rax),�x
40116e: 89 c6 mov �x,%esi
401170: bf 13 20 40 00 mov $0x402013,�i
401175: b8 00 00 00 00 mov $0x0,�x
40117a: e8 c1 fe ff ff callq 401040 <printf@plt>
40117f: bf 10 20 40 00 mov $0x402010,�i
401184: e8 a7 fe ff ff callq 401030 <puts@plt>
401189: b8 00 00 00 00 mov $0x0,�x
40118e: c9 leaveq
40118f: c3 retq
使用GDB来验证下,给地址3赋值,越界访问后收到内核信号SIGSEGV