将 FP 异常陷阱 (-ffpe-trap/-fpe0) 用于链接到 SIGFPE 不安全库 (libxml2) 的代码

Use FP exception traps (-ffpe-trap/-fpe0) for code linked against SIGFPE-unsafe library (libxml2)

提问人:H. Weirauch 提问时间:11/15/2023 更新时间:11/15/2023 访问量:73

问:

我有 Fortran 代码,我想为此启用浮点异常捕获(按照编译器手册页的建议)。但是当我这样做时,二进制文件将不再在 Slurm 队列中运行。

问题显然位于(Slurm 的运行时依赖项,它为 MPI 运行时提供包装器,称为 )。这会触发 FPE,因此在我自己的代码有机会运行之前,执行就停止了。libxml2MPI_Init

理想情况下,我想告诉编译器/链接器创建二进制文件,以捕获由我自己的代码创建的 FPE,但对来自外部库的 FPE 条件不敏感。

最小示例:

program mwe
  use mpi_f08
  integer :: ierror
  call MPI_Init(ierror)
  print*,"MPI_Init returned", ierror
end program

编译方式

  • gfortran 作为mpif90 -ffpe-trap=invalid,zero,overflow mwe.F90
  • ifort 作为mpifort -fpe0 mwe.F90

给我留下了一个运行良好的二进制文件

  • 直接调用时:./a.out
  • 当使用并行包装器(或任何更高的数字)调用时。mpirun -np 1

但是,当从 Slurm 批处理脚本调用或直接通过 运行时,会从堆栈下方的某个位置触发浮点异常。ifort回溯告诉我在哪里:srun ./a.out

 $ srun ./a.out 
forrtl: error (65): floating invalid
Image              PC                Routine            Line        Source             
libc.so.6          000014609708A520  Unknown               Unknown  Unknown
libxml2.so.2.9.13  000014609407C723  Unknown               Unknown  Unknown
libxml2.so.2.9.13  0000146094054A4F  xmlCheckVersion       Unknown  Unknown
hwloc_xml_libxml.  0000146094A100C6  Unknown               Unknown  Unknown
libhwloc.so.15.6.  00001460967BBF33  Unknown               Unknown  Unknown
libhwloc.so.15.6.  00001460967A83A9  Unknown               Unknown  Unknown
libopen-pal.so.40  0000146096D538FA  opal_hwloc_base_g     Unknown  Unknown
libopen-rte.so.40  0000146096E4C630  orte_ess_base_pro     Unknown  Unknown
libopen-rte.so.40  0000146096E53705  Unknown               Unknown  Unknown
libopen-rte.so.40  0000146096EE05AD  orte_init             Unknown  Unknown
libmpi.so.40.30.4  00001460975F2125  ompi_mpi_init         Unknown  Unknown
libmpi.so.40.30.4  0000146097414200  MPI_Init              Unknown  Unknown
libmpi_mpifh.so.4  00001460976F0D38  PMPI_Init_f08         Unknown  Unknown
libmpi_usempif08.  0000146097734242  mpi_init_f08_         Unknown  Unknown
ifort              000000000040B1F8  Unknown               Unknown  Unknown
ifort              000000000040B19D  Unknown               Unknown  Unknown
libc.so.6          0000146097071D90  Unknown               Unknown  Unknown
libc.so.6          0000146097071E40  __libc_start_main     Unknown  Unknown
ifort              000000000040B0B5  Unknown               Unknown  Unknown
srun: error: localhost: task 0: Aborted

罪魁祸首在(我从 gdb 获得了 gfortran 二进制文件的相同指针)。xmlCheckVersionlibxml2


我更愿意在编译和运行时控制软件的迂腐级别,而不是在此过程中收集一些随机依赖项来限制我可以选择什么和不能选择什么。但我想没有前景让编译器/链接器将标准应用于我自己的代码,而不是在运行时应用于来自共享对象的东西?

这特别烦人,因为所谓的除以零错误的来源是 libxml2,因为大约 2.9.11 版本。只要许多主要/稳定的平台仍在 2.9 版本上(这种情况将持续很长时间),浮点陷阱对于旨在与 Slurm 一起使用的项目来说基本上是无法使用的。

由于 2.10 版本大约在两年前发布,并且它的开发人员似乎已经找到了一种更聪明的方法来处理这种情况,因此它并不完全是上游错误(在积极开发/维护的分支中),有必要说服 LTS/稳定操作系统发行商介入。libxml2

操作平台:Ubuntu 22.04 LTS、x86_64、gcc 12.2.0、OpenMPI 4.1.4、libxml2 2.9.13

(这是 Fortran 中 MPI_Init() 中 SIGFPE 的后续 - 错误的算术运算。FWIW,这个问题更侧重于根本原因,并避免了像 CMake 或 Fortran 编译器的特定选择这样的干扰。

异常 fortran slurm libxml2

评论

4赞 francescalus 11/15/2023
“我更愿意在编译和运行时控制软件的迂腐程度”。鉴于这种偏好,您是否尝试过 IEEE 例外功能?
3赞 Ian Bush 11/15/2023
@francescalus打败了我。Fortran IEEE 异常处理似乎完全符合您在该句子中的要求。

答:

4赞 Vladimir F Героям слава 11/15/2023 #1

正如 francescalus 和 Ian Bush 所评论的那样,自然而然的方式是 Fortran 2003 及更高版本的标准 IEEE 异常处理。我最近的问题 为 OpenMP 线程设置 IEEE FPE 停止模式中的代码实际上几乎完全相同。它最初是为了禁用某个子例程的异常而精确派生的。

在程序启动时启用异常捕获(在 FPE 异常时停止)。很有可能,您也可以改用编译器标志,但它的可移植性较差。

use ieee_exceptions 

call ieee_set_halting_mode(ieee_overflow, .true.)
call ieee_set_halting_mode(ieee_invalid, .true.)
call ieee_set_halting_mode(ieee_divide_by_zero, .true.)

在调用需要禁用 FPE 异常时停止的子例程之前

logical :: saved_fpe_mode(size(ieee_all))

call ieee_get_halting_mode(ieee_all, saved_fpe_mode)
call ieee_set_halting_mode(ieee_all, .false.)

调用子例程以恢复停止后

call ieee_set_halting_mode(ieee_all, saved_fpe_mode)

如链接的问题和答案所示,如果使用多个线程,则可能需要小心,因为停止模式可以是每个线程的本地模式。

评论

0赞 H. Weirauch 11/28/2023
可悲的是,这种方法并没有改善这种情况。它确实在我自己的代码中捕获了 FPE,是的,而且我们不再需要编译器标志绝对是件好事(谢谢你!但是程序仍然在 libxml2 代码中偶然发现了相同的 FPE,并且没有运行。
0赞 Vladimir F Героям слава 11/28/2023
@H.Weirauch:你能在运行MPI_Init之前打印anthing吗?即使您只在MPI_Init后启用异常,它也会崩溃吗?
0赞 H. Weirauch 11/28/2023
谢谢,只有在MPI_Init之后才启用异常的好点!这是缺少的位,这是显式调用ieee_set_halting_mode而不是编译器标志的实际优势:它允许我从我想要的 FPE 覆盖率级别中排除MPI_Init可能调用的任何库。