SEGFAULT 随着优化级别的降低而消失?
SEGFAULT disappears with lower optimisation level?
所以,我想帮助我的研究人员调试 Fortran 程序,出于演示目的,我创建了一个 故意 导致段错误的程序。
来源如下:
program segfault
implicit none
integer :: n(10), i
integer :: ios, u
open(newunit=u, file='data.txt', status='old', action='read', iostat=ios)
if (ios /= 0) STOP "error opening file"
i = 0
do
i = i + 1
read(u, *, iostat=ios) n(i)
if (ios /= 0) exit
end do
close(u)
print*, sum(n)
end program segfault
data.txt
文件包含 100 个随机数:
for i in {1..100}; do
echo $RANDOM >> data.txt;
done
当我用
编译这个程序时
gfortran -O3 -o segfault.exe segfault.f90
生成的可执行文件尽职尽责地崩溃了。但是当我启用调试编译时:
gfortran -O0 -g -o segfault.exe segfault.f90
然后它只读入前 10 个值,并打印它们的总和。对于它的价值,-O2
会导致所需的段错误,-O1
不会。
我深感忧虑。毕竟,如果在启用调试符号的情况下编译时错误消失了,我该如何正确调试?
有人可以解释这种行为吗?
我正在使用GNU Fortran (MacPorts gcc5 5.3.0_1) 5.3.0
段错误是 undefined behaviour。该程序不符合 Fortran 标准,因此您不能期待任何特定的结果。它可以做任何事情。你不能指望段错误会发生,当它没有发生时,你越不担心。
编译器检查 (fcheck=
) 和清理 (-fsanitize=
) 可用是有原因的。等待段错误不能保证有效。不在 Fortran 中,不在 C 中,不在任何类似的语言中。
不合格程序的结果可能取决于许多因素,例如变量在内存或寄存器中的位置。内存中变量的对齐,堆栈帧的位置......你根本无法计算任何东西。这些细节显然取决于优化级别。
如果程序越界访问数组,但内存中的地址恰好是属于进程的内存的一部分,则可能不会发生段错误。它只是内存中的一些字节,允许进程读取或写入(或两者)。您可能正在覆盖其他一些变量,您可能正在从一些旧的堆栈框架中读取一些垃圾,您可能正在覆盖 malloc
的内部簿记数据并破坏堆。崩溃可能在其他地方等待发生,或者可能只是程序的数字结果会有点错误。什么事都有可能发生。
所以,我想帮助我的研究人员调试 Fortran 程序,出于演示目的,我创建了一个 故意 导致段错误的程序。
来源如下:
program segfault
implicit none
integer :: n(10), i
integer :: ios, u
open(newunit=u, file='data.txt', status='old', action='read', iostat=ios)
if (ios /= 0) STOP "error opening file"
i = 0
do
i = i + 1
read(u, *, iostat=ios) n(i)
if (ios /= 0) exit
end do
close(u)
print*, sum(n)
end program segfault
data.txt
文件包含 100 个随机数:
for i in {1..100}; do
echo $RANDOM >> data.txt;
done
当我用
编译这个程序时gfortran -O3 -o segfault.exe segfault.f90
生成的可执行文件尽职尽责地崩溃了。但是当我启用调试编译时:
gfortran -O0 -g -o segfault.exe segfault.f90
然后它只读入前 10 个值,并打印它们的总和。对于它的价值,-O2
会导致所需的段错误,-O1
不会。
我深感忧虑。毕竟,如果在启用调试符号的情况下编译时错误消失了,我该如何正确调试?
有人可以解释这种行为吗?
我正在使用GNU Fortran (MacPorts gcc5 5.3.0_1) 5.3.0
段错误是 undefined behaviour。该程序不符合 Fortran 标准,因此您不能期待任何特定的结果。它可以做任何事情。你不能指望段错误会发生,当它没有发生时,你越不担心。
编译器检查 (fcheck=
) 和清理 (-fsanitize=
) 可用是有原因的。等待段错误不能保证有效。不在 Fortran 中,不在 C 中,不在任何类似的语言中。
不合格程序的结果可能取决于许多因素,例如变量在内存或寄存器中的位置。内存中变量的对齐,堆栈帧的位置......你根本无法计算任何东西。这些细节显然取决于优化级别。
如果程序越界访问数组,但内存中的地址恰好是属于进程的内存的一部分,则可能不会发生段错误。它只是内存中的一些字节,允许进程读取或写入(或两者)。您可能正在覆盖其他一些变量,您可能正在从一些旧的堆栈框架中读取一些垃圾,您可能正在覆盖 malloc
的内部簿记数据并破坏堆。崩溃可能在其他地方等待发生,或者可能只是程序的数字结果会有点错误。什么事都有可能发生。