按照此question的建议,我在主要来源中添加了#include <fenv.h>
和feenableexcept(FE_ALL_EXCEPT & ~FE_INEXACT);
并使用g++ -O0 -Wall -Wextra -Werror -g main.cpp -o main.o
对其进行了编译。项目中的所有文件都是使用make以此方式编译的。 feenableexcept
仅添加到main中。事物通过相同的-O0和-g链接在一起。然后,我以gdb a.out
运行可执行文件。调试器为我提供了预期的SIGFPE
,但是回溯并没有提供通常有用的信息。相反,我得到这样的清单:
Program received signal SIGFPE, Arithmetic exception.
0x00002aaaabe19c0d in _amd_handle_error () from /lib64/libm.so.6
(gdb) bt
#0 0x00002aaaabe19c0d in _amd_handle_error () from /lib64/libm.so.6
#1 0x00002aaaabe19cca in _pow_special () from /lib64/libm.so.6
#2 0x00002aaaabdf6c12 in pow () from /lib64/libm.so.6
#3 0xbfc971b779361ea1 in ?? ()
#4 0x3fe85bf701f137d7 in ?? ()
#5 0x3fe914100cd71275 in ?? ()
#6 0x00007fffffffa420 in ?? ()
#7 0x00007fffffff9960 in ?? ()
#8 0x3fe7c514ca0e522d in ?? ()
#9 0x0000000000000000 in ?? ()
如果我尝试看一下框架,我将无用。在这种情况下,函数pow
在带有浮点错误的代码区域中被多次使用。此处缺少的信息就是知道如何到达pow
。
通常,当我使用-O0
和-g
时,会得到与跟踪关联的函数,文件和行号。如果我使用相同的可执行文件和断点的回溯设置断点,我将获得有用的信息。
Breakpoint 1, SurfaceModel::ProcessGroups (this=0x7fffffff9f30) at source/SurfaceModel.cpp:398
398 vector<Group>::iterator it;
(gdb) bt
#0 SurfaceModel::ProcessGroups (this=0x7fffffff9f30) at source/SurfaceModel.cpp:398
#1 0x00000000006768e6 in MainLoop (logFile=...) at source/main.cpp:94
#2 0x0000000000676337 in main (argc=1, argv=0x7fffffffbe18) at source/main.cpp:41
然后,我在代码中添加了*(int*)0=0;
,以强制进行段错误以查看其是否与信号有关。我也在那里找到了有用的信息。
Program received signal SIGSEGV, Segmentation fault.
0x000000000061f42a in SurfaceModel::ClearGroupHeatRates (this=0x7fffffff9f30) at source/SurfaceModel.cpp:456
456 *(int*)0=0; // force a seg fault
(gdb) bt
#0 0x000000000061f42a in SurfaceModel::ClearGroupHeatRates (this=0x7fffffff9f30) at source/SurfaceModel.cpp:456
#1 0x0000000000676ba5 in MainLoop (logFile=...) at source/pilager.cpp:137
#2 0x0000000000676341 in main (argc=1, argv=0x7fffffffbe18) at source/pilager.cpp:41
这似乎仅与我对浮点控制所做的事情有关。我正在运行GDB 7.12,它是用GCC 5.3.0编译的。有没有办法用SIGFPE
保留跟踪信息?
答案 0 :(得分:0)
是否可以使用SIGFPE保留跟踪信息?
跟踪信息与引发信号无关,与引发信号的功能无关。
您的pow
以某种方式缺少展开描述符(这是GDB用来展开堆栈的内容)。
这通常发生在汇编级的实现中(开发人员忽略了放置适当的.cfi
指令),或者在使用损坏的编译器构建代码时发生。
损坏的编译器似乎不太可能,而且我找不到使用汇编程序来实现pow
的GLIBC的任何最新版本。
要恢复堆栈,可以使用以下技术:
SIGFPE
向后退。这是最好的解决方案,但我怀疑rr
适用于您的(显然是很旧的)系统。计算崩溃前被调用pow
的次数:
(gdb) break pow
(gdb) commands 1
silent
cont
end
(gdb) run
# run until SIGFPE
(gdb) info break
现在,您将知道崩溃前被调用pow
的次数。
再次运行该程序,忽略断点$N-1
次(您需要先从断点中删除命令,然后使用GDB ignore 1 $N-1
命令)。现在应该在崩溃之前停止操作,并且由于您仍然不在pow
内,因此GDB可以很容易地向您显示堆栈跟踪。
仅当您的程序是确定性的时,此方法才有效。