使用 GDB 在 Fortran 中调试 MPI 程序
Using GDB to debug an MPI program in Fortran
我读了这篇文章并到达了这里,所以现在我想我应该(如果不是这样,请告诉我(重写代码
{
int i = 0;
char hostname[256];
gethostname(hostname, sizeof(hostname));
printf("PID %d on %s ready for attachn", getpid(), hostname);
fflush(stdout);
while (0 == i)
sleep(5);
}
在福特兰。从这个答案中我明白,在 Fortran 中,我可以简单地用MPI_Get_processor_name
代替gethostname
.其他一切都很简单,但flush
。怎么样?
我应该把它放在哪里?在主程序中MPI_Init
?然后?我该怎么办?
对于有关编译选项的内容,我提到了这一点,并使用-v -da -Q
作为mpifort
包装器的选项。
这个解决方案不适合我的情况,因为我至少需要在 27 个进程上运行该程序,所以我只想检查一个进程。
最简单的方法:
我实际上经常做的是我只是在本地运行 MPI 作业,看看它的作用。没有上述任何代码。然后,如果它挂起,我使用 top
来找出进程的PID
,通常人们可以很容易地从 PID 中猜出哪个等级是哪个等级(它们往往是连续的,最低的等级是等级 0(。低于等级 0 是进程 1641,而不是等级 1 pid 1642,依此类推......
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1642 me 20 0 167328 7716 5816 R 100.0 0.047 0:25.02 a.out
1644 me 20 0 167328 7656 5756 R 100.0 0.047 0:25.04 a.out
1645 me 20 0 167328 7700 5792 R 100.0 0.047 0:24.97 a.out
1646 me 20 0 167328 7736 5836 R 100.0 0.047 0:25.00 a.out
1641 me 20 0 167328 7572 5668 R 99.67 0.046 0:24.95 a.out
然后我只做gdb -pid
,检查进程中的堆栈和局部变量。(在 GDB 控制台中使用help stack
(
最重要的是获取回溯,因此只需在控制台中打印bt
即可。
这在检查死锁时会很好地工作。当您必须在某个特定地点停下来时,情况就不太好了。然后,您必须尽早附加调试器。
您的代码:
我认为在 Fortran 中不需要冲洗。我认为 Fortran 至少在我使用的编译器中write
和print
刷新是必要的。
但是您绝对可以使用flush
语句
use iso_fortran_env
flush(output_unit)
只需将其冲洗放在打印hostname
和pid
的write
之后即可。但正如我所说,我只会从打印开始。
你要做的是登录到该节点并使用类似的东西将 gdb 附加到 righ 进程
gdb -pid 12345
对于睡眠,您可以使用许多编译器中可用的非标准sleep
内部子例程,也可以编写自己的子例程。
无论是在MPI_Init
之前还是之后?如果要打印排名,则必须在之后。也用于使用MPI_Get_processor_name
它必须是之后。通常建议在程序中尽早调用MPI_Init
。
然后代码类似于
use mpi
implicit none
character(MPI_MAX_PROCESSOR_NAME) :: hostname
integer :: rank, ie, pid, hostname_len
integer, volatile :: i
call MPI_Init(ie)
call MPI_Get_processor_name(hostname, hostname_len, ie)
!non-standard extension
pid = getpid()
call MPI_Comm_rank(MPI_COMM_WORLD, rank, ie)
write(*,*) "PID ", pid, " on ", trim(hostname), " ready for attach is world rank ", rank
!this serves to block the execution at a specific place until you unblock it in GDB by setting i=0
i = 1
do
!non-standard extension
call sleep(1)
if (i==0) exit
end do
end
重要说明:如果使用优化进行编译,编译器可以看到i==0
永远不会为真,并且将完全删除检查。您必须降低优化或将i
声明为 volatile
。易失性意味着该值可以随时更改,编译器必须从内存中重新加载其值以进行检查。这需要Fortran 2003。
附加正确的流程:
上面的代码将打印,例如,
> mpif90 -ggdb mpi_gdb.f90
> mpirun -n 4 ./a.out
PID 2356 on linux.site ready for attach is world rank 1
PID 2357 on linux.site ready for attach is world rank 2
PID 2358 on linux.site ready for attach is world rank 3
PID 2355 on linux.site ready for attach is world rank 0
在顶部,它们看起来像
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2355 me 20 0 167328 7452 5564 R 100.0 0.045 1:42.55 a.out
2356 me 20 0 167328 7428 5548 R 100.0 0.045 1:42.54 a.out
2357 me 20 0 167328 7384 5500 R 100.0 0.045 1:42.54 a.out
2358 me 20 0 167328 7388 5512 R 100.0 0.045 1:42.51 a.out
您只需选择您想要的等级并执行
gdb -pid 2355
以附加等级 0,依此类推。当然,在不同的终端窗口中。
然后你会得到类似的东西
MAIN__ () at mpi_gdb.f90:26
26 if (i==0) exit
(gdb) info locals
hostname = 'linux.site', ' ' <repeats 246 times>
hostname_len = 10
i = 1
ie = 0
pid = 2457
rank = 0
(gdb) set var i = 0
(gdb) cont
Continuing.
[Inferior 1 (process 2355) exited normally]
发布的代码基本上只是一个无限循环,旨在在附加调试器时"暂停"执行。然后,可以使用调试器控件退出此循环,程序将继续。你可以在 fortran 中编写一个等效的循环,所以只要你愿意从另一种方法获取主机名和 pid(参见 VladimirF 在他的回答中提到的mpi_get_processor_name,如果你乐于使用编译器扩展,GNU 和 Intel 编译器都提供了getpid
扩展(,你可以使用类似以下内容的内容(感谢这个答案作为睡眠示例(。
module fortran_sleep
!See https://stackoverflow.com/a/6932232
use, intrinsic :: iso_c_binding, only: c_int
implicit none
interface
! should be unsigned int ... not available in Fortran
! OK until highest bit gets set.
function FortSleep (seconds) bind ( C, name="sleep" )
import
integer (c_int) :: FortSleep
integer (c_int), intent (in), VALUE :: seconds
end function FortSleep
end interface
end module fortran_sleep
program mpitest
use mpi
use fortran_sleep
use, intrinsic :: iso_c_binding, only: c_int
implicit none
integer :: rank,num_process,ierr, tmp
integer :: i
integer (c_int) :: wait_sec, how_long
wait_sec = 5
call mpi_init (ierr)
call mpi_comm_rank (MPI_COMM_WORLD, rank, ierr)
call mpi_comm_size (MPI_COMM_WORLD, num_process, ierr)
call mpi_barrier (MPI_COMM_WORLD, ierr)
print *, 'rank = ', rank
call mpi_barrier (MPI_COMM_WORLD, ierr)
i=0
do while (i.eq.0)
how_long = FortSleep(wait_sec)
end do
print*,"Rank ",rank," has escaped!"
call mpi_barrier(MPI_COMM_WORLD, ierr)
call mpi_finalize (ierr)
end program mpitest
现在编译类似的东西
> mpif90 prog.f90 -O0 -g -o prog.exe
如果我现在在本地机器的两个内核上使用
> mpirun -np 2 ./prog.exe
在屏幕上,我只看到
rank = 0
rank = 1
现在在另一个终端中,我连接到相关机器并使用
ps -ef | grep prog.exe
这为我提供了几个对应于不同等级的进程 id 值。然后,我可以使用以下命令附加到其中之一
gdb --pid <pidFromPSCmd> ./prog.exe
现在我们gdb
可以使用 bt(回溯(看到我们在程序中的位置,很可能我们处于sleep
.然后我们使用 s(tep( 逐步完成程序,直到到达主程序。现在我们将i
设置为非零值,然后执行 c(ontinue(,这允许此排名过程继续,我们看到排名已转义消息等。gdb
部分将如下所示:
(gdb) bt
#0 0x00007f01354a1d70 in __nanosleep_nocancel () from /lib64/libc.so.6
#1 0x00007f01354a1c24 in sleep () from /lib64/libc.so.6
#2 0x0000000000400ef9 in mpitest () at prog.f90:35
#3 0x0000000000400fe5 in main (argc=1, argv=0x7ffecdc8d0ae) at prog.f90:17
#4 0x00007f013540cb05 in __libc_start_main () from /lib64/libc.so.6
#5 0x0000000000400d39 in _start () at ../sysdeps/x86_64/start.S:122
(gdb) s
Single stepping until exit from function __nanosleep_nocancel,
which has no line number information.
0x00007f01354a1c24 in sleep () from /lib64/libc.so.6
(gdb) s
Single stepping until exit from function sleep,
which has no line number information.
mpitest () at prog.f90:34
34 do while (i.eq.0)
(gdb) bt
#0 mpitest () at prog.f90:34
#1 0x0000000000400fe5 in main (argc=1, argv=0x7ffecdc8d0ae) at prog.f90:17
#2 0x00007f013540cb05 in __libc_start_main () from /lib64/libc.so.6
#3 0x0000000000400d39 in _start () at ../sysdeps/x86_64/start.S:122
(gdb) set var i = 1
(gdb) c
Continuing.
在我们原来的终端中,我们会看到类似的东西
Rank 0 has escaped!
- 当回溯以零开始时,如何调试崩溃
- 用MacOS Mojave编译C++:致命错误:mpi.h:没有这样的文件或目录
- 在linux上调试巨大的C++项目
- 为什么使用__LINE_的代码在发布模式下在MSVC下编译,而不是在调试模式下
- 如何针对特定情况调试和修复此双自由内存损坏问题
- 正在VS调试器中监视映射条目
- 使用调试/崩溃报告将应用程序部署到客户端
- MPI突然停止了对多个核心的操作
- VC++本机单元测试,找不到调试符号
- 如何在C++生成器中禁用"使用调试.dcus"
- 使用vscode调试时,GDB意外退出
- 即使使用调试编译标志,表达式也是"optimized out"
- 调试 CUDA MMU 故障
- 设置 Visual Studio for MPI: 找不到标识符错误
- 小字符串优化(调试与发布模式)
- 调试和自由执行中的信号处理
- 使用gdb回溯调试MPI代码
- 适用于Linux的免费并行(MPI)调试器
- Valgrind+gdb用MPI调试,库中有错误
- 使用 GDB 在 Fortran 中调试 MPI 程序