Linux下段错误问题定位
本文总结程序开发中段错误的常见定位思路与实用排查方法。
1 生成core dump文件调试
一、 相关知识
- ulimit命令 来自英文词组“user limit”的缩写,是一条bash解释器的内部命令,其功能是用于限制使用系统资源。通过使用ulimit命令能够限制用户最大启动进程数、最长CPU使用时长、最高内存占用量等资源,提高整体服务器的运行稳定性,让每位用户都可以充分、合理、公平地利用系统资源。
二、 环境准备
- 查看core dump功能是否打开,如果为0则表示处于关闭状态
(base) kwephispre11269:~ # ulimit -c
0
- 指定core dump文件大小 1)在当前session生效
ulimit -c 1024 # 1024个blocks,一般1block=512bytes
ulimit -c unlimited # 取消大小限制,开发,试运行阶段可以使用次参数
2)持久化配置,修改limits.conf文件
(base) kwephispre11269:~ # cat /etc/security/limits.conf
#<domain> <type> <item> <value>
#* soft core 0
3.设置core dump文件名及存储位置 1)查看默认位置
(base) kwephispre11269:~ # cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %e
核心文件由上述程序处理,通常会保存到/var/lib/systemd/coredump/目录下。
2)修改存储位置及名称
(base) kwephispre11269:~ # echo "/path/coredump/file-%e-%P-%t" > /proc/sys/kernel/core_pattern
(base) kwephispre11269:~ # cat /proc/sys/kernel/core_pattern
/path/coredump/file-%e-%P-%t
3)文件参数说明
%% - 单个%字符
%p - 添加pid
%u - 添加当前uid
%g - 添加当前gid
%s - 添加导致产生core的信号
%t - 添加core文件生成时的unix时间
%h - 添加主机名
%e - 添加程序文件名
三、使用GDB调试 TODO 待补充链接
2 使用dmesg与addr2line命令
一、可执行程序错误
注意代码需要指定-g选项,以调试模式编译
0)示例代码
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int *p = NULL;
6 *p = 0;
7 printf("bad\n");
8 return 0;
9 }
1)查看错误信息
(base) kwephispre11269:/data # dmesg | grep segfault
[1467662.487224] test[12986]: segfault at 0 ip 00000000004003a0 sp 00007ffe6c077ca8 error 6 in test[400000+1000]
(base) kwephispre11269:/data # cat /var/log/messages | grep segfault
2024-05-23T20:29:10.756390+08:00 kwephispre11269 kernel: [1467662.487224] test[12986]: segfault at 0 ip 00000000004003a0 sp 00007ffe6c077ca8 error 6 in test[400000+1000]
可以使用dmesg命令或查看message日志文件查看。上述日志指出错误发生在test程序中。
上述
error转换为二进制110,表示用户态程序访问无效地址error的取值范围是0~7,从高到底分别为bit2、bit1和bit0 bit2: 1表示用户态程序内存访问越界,0表示是内核态程序内存访问越界 bit1: 1表示写操作导致内存访问越界,0表示是读操作导致内存访问越界 bit0: 1表示没有足够的权限访问非法地址的内容,0表示访问的非法地址根本没有对应的页面,也就是无效地址
2)查找错误行
(base) kwephispre11269:/data # addr2line -e test 00000000004003a0
/data/test.c:6
使用指令指针寄存器(IP)的值,调用addr2line命令得到源码具体行号。
二、非可执行程序错误 0)示例代码
test.c
1 #include "foo.h"
2
3 int main(void)
4 {
5 foo();
6 return 0;
7 }
foo.h
1 #ifndef __FOO_LIB_H__
2 #define __FOO_LIB_H__
3
4 int foo(void);
5
6 #endif
foo.c
1 #include "foo.h"
2
3 int foo()
4 {
5 int *p = 0;
6 *p = 0;
7 return 0;
8 }
1)编译链接并运行
(base) kwephispre11269:/data # gcc -O3 -g -o libfoo.so -shared -fPIC foo.c
(base) kwephispre11269:/data # gcc -O3 -g -o test test.c -L. -lfoo
(base) kwephispre11269:/data # export LD_LIBRARY_PATH=.
(base) kwephispre11269:/data # ./test
Segmentation fault
注:编译时使用-O1/2/3优化后,可能导致信息不准确,建议使用-O0
2)查看错误信息
(base) kwephispre11269:/data # dmesg | grep segfault
[1513188.944663] test[15961]: segfault at 0 ip 00007f873d18c4e0 sp 00007ffccc5edf58 error 6 in libfoo.so[7f873d18c000+1000]
3)查找错误行
计算动态库相对地址值:00007f873d18c4e0 - 00007f873d18c4e0 = 4e0,并指定动态库为-e输入
(base) kwephispre11269:/data # addr2line -e libfoo.so 4e0
/data/foo.c:6
3 段错误发生时输出堆栈信息
一、相关知识
1)信号是内核提供的向用户进程发送信息的机制。SIGSEGV是当一个进程执行了无效的内存引用,或发生段错误时发送给它的信息。
2)栈回溯back trace当用户态或内核态程序异常退出时,回溯堆栈信息。由于不同操作系统堆栈实现不同(向上增长或向下增长),分析结果时需要考虑栈增长方向。
二、编码实现
1)使用execinfo.h的backtrace函数
1 #include <unistd.h>
2 #include <signal.h>
3 #include <execinfo.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <stdarg.h>
9
10 #define BACKTRACE_SIZE 256
11
12
13 void segv_handler(int sig)
14 {
15 void *func[BACKTRACE_SIZE];
16 char **symb = NULL;
17 int size;
18
19 size = backtrace(func, BACKTRACE_SIZE);
20 backtrace_symbols_fd(func, size, STDERR_FILENO);
21 exit(1);
22 }
23
24
25 int main(void)
26 {
27 int *p = NULL;
28 signal(SIGSEGV, segv_handler);
29 *p = 0xdeadbeef;
30 return 0;
31 }
2)编译运行
(base) kwephispre11269:/data # gcc -O3 -g -o auto auto.c
(base) kwephispre11269:/data # ./auto
./auto[0x400614]
/lib64/libc.so.6(+0x362b0)[0x7fe41e3b92b0]
./auto[0x400513]
/lib64/libc.so.6(__libc_start_main+0xed)[0x7fe41e3a525d]
./auto[0x40054a]
3)查找错误行
当前使用x86_64系统,堆栈逆序查看,先调用的函数在下方。前两行为信号SIGSEGV的处理函数调用,则判断段错误发生在./auto[0x400614]处。
(base) kwephispre11269:/data # addr2line -e auto 0x400513
/data/auto.c:29