本文介绍在Linux环境下,Qt编程中segfault问题解决方法。
1.问题原因
segfault问题是编程中较为常见的错误,造成此问题的原因通常是程序访问了不允许访问的存储区域,比如C++程序中数组访问越界,通常会导致segfalut问题。出现segfault问题通常会导致程序崩溃,无法运行。
2.确认问题
前面介绍过,出现segfault问题通常会导致程序崩溃,但是导致程序崩溃的也有可能是其他原因,这时就需要确认是segfault问题。程序出现崩溃后,在Linux环境下,可以输入:
sudo dmesg
这里以1个项目问题为例,dmesg后出现:
show_signal_msg: 46 callbacks suppressed
[ 1155.768459] QThread[4022]: segfault at 7f3a2c021000 ip 0000564cd13fca33 sp 00007f3a32a2a6f0 error 4 in LightSource[564cd13f6000+18000]
[ 1155.768463] Code: 55 dc c7 45 fc 00 00 00 00 c6 45 fa 00 c6 45 fb 00 c7 45 fc 00 00 00 00 8b 45 fc 3b 45 dc 73 45 8b 55 fc 48 8b 45 e0 48 01 d0 <0f> b6 00 30 45 fa c6 45 fb 08 80 7d fb 00 74 25 0f b6 45 fa 84 c0
从打印消息可以很明显看出是segfault问题。
3.问题排查
1)确认故障位置
确认故障位置很关键,它有助于我们进一步找到在那个函数中出现segfault问题。
从打印消息中我们知道程序是从BASE:564cd13f6000处开始执行的,出现segfault问题后的IP(也就是程序计数器指针)为:0000564cd13fca33,那么程序相对于基地址的位置为:
Address=IP-BASE
带入值,可得:
Address=0000564cd13fca33-564cd13f6000=6a33
也就是说,程序在运行到相对0起始地址的6a33位置处出错。
2)确认函数位置
知道了报错地址,就需要进一步确认在那个函数中出现此问题。
这里有2种方法:
a)方法1
在Debug目录下,输入:
addr2line -e LightSource 6a33
命令的功能是实现有地址到程序行的转换。注意,这里要使用Debug版本的程序。
输出:
/home/xxx/Projects/LightSource/build-LightSource-Desktop_Qt_5_13_2_GCC_64bit-Debug/../LightSource/comm.cpp:366
由输出我们知道报错的位置在comm.cpp的366行。进入comm.cpp定位到366行就知道在哪里导致segfault问题了。
b)方法2
方法2是采用反汇编的方式,在Debug目录下,输入:
sudo objdump -ld LightSource > ~/Desktop/dump.txt
将反汇编后的结果导出到dump.txt中,然后,打开dump.txt,搜索6a33地址,如下所示:
00000000000069fc <_ZN4Comm8calcCRC8EPKhj>:
69fc: 55 push %rbp
69fd: 48 89 e5 mov %rsp,%rbp
6a00: 48 89 7d e8 mov %rdi,-0x18(%rbp)
6a04: 48 89 75 e0 mov %rsi,-0x20(%rbp)
6a08: 89 55 dc mov %edx,-0x24(%rbp)
6a0b: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
6a12: c6 45 fa 00 movb $0x0,-0x6(%rbp)
6a16: c6 45 fb 00 movb $0x0,-0x5(%rbp)
6a1a: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
6a21: 8b 45 fc mov -0x4(%rbp),%eax
6a24: 3b 45 dc cmp -0x24(%rbp),%eax
6a27: 73 45 jae 6a6e <_ZN4Comm8calcCRC8EPKhj+0x72>
6a29: 8b 55 fc mov -0x4(%rbp),%edx
6a2c: 48 8b 45 e0 mov -0x20(%rbp),%rax
6a30: 48 01 d0 add %rdx,%rax
6a33: 0f b6 00 movzbl (%rax),%eax
6a36: 30 45 fa xor %al,-0x6(%rbp)
6a39: c6 45 fb 08 movb $0x8,-0x5(%rbp)
6a3d: 80 7d fb 00 cmpb $0x0,-0x5(%rbp)
6a41: 74 25 je 6a68 <_ZN4Comm8calcCRC8EPKhj+0x6c>
6a43: 0f b6 45 fa movzbl -0x6(%rbp),%eax
6a47: 84 c0 test %al,%al
6a49: 79 0e jns 6a59 <_ZN4Comm8calcCRC8EPKhj+0x5d>
6a4b: 0f b6 45 fa movzbl -0x6(%rbp),%eax
6a4f: 01 c0 add %eax,%eax
6a51: 83 f0 31 xor $0x31,%eax
6a54: 88 45 fa mov %al,-0x6(%rbp)
6a57: eb 03 jmp 6a5c <_ZN4Comm8calcCRC8EPKhj+0x60>
6a59: d0 65 fa shlb -0x6(%rbp)
6a5c: 0f b6 45 fb movzbl -0x5(%rbp),%eax
6a60: 83 e8 01 sub $0x1,%eax
6a63: 88 45 fb mov %al,-0x5(%rbp)
6a66: eb d5 jmp 6a3d <_ZN4Comm8calcCRC8EPKhj+0x41>
6a68: 83 45 fc 01 addl $0x1,-0x4(%rbp)
6a6c: eb b3 jmp 6a21 <_ZN4Comm8calcCRC8EPKhj+0x25>
6a6e: 0f b6 45 fa movzbl -0x6(%rbp),%eax
6a72: 5d pop %rbp
6a73: c3 retq
我们就可以知道程序出现问题的地方在"_ZN4Comm8calcCRC8EPKhj",进而很容易的就知道是Comm中的calcCRC8函数了。其实,我们也可以发现在”2.确认问题”中的Code内容也在这里可以发现。
如果是库函数中的某个地址,也可以使用这种方法,不过就要先进入库所在目录了。
3)故障解决
故障解决的方法就不详细介绍了。
4.其他方法
1)集成开发环境(IDE)方法
如果故障容易再现或有一定的触发手段情况下,在使用开发环境下进入Debug,触发故障,这时Debugger会Hold住,我们根据backtrace就可以知道出现问题时的栈帧,即函数调用关系,以及一些辅助的信息,比如出现问题时相应的一些变量值,有助于我们找到问题原因。注意:在.pro中添加:
QMAKE_CXXFLAGS += -g
2)gdb方法
这个和1)比较类似,1)中的Debugger也不过是封装了gdb而已(Linux环境下),命令行下输入:
gdb LightSource
run
触发故障后,输入
backtrace
backtrace full
总结,本文介绍了Qt编程中segfault问题解决方法。