我看了this并且到了here,所以现在我想我应该(如果不是这样,请告诉我)重写代码
{
int i = 0;
char hostname[256];
gethostname(hostname, sizeof(hostname));
printf("PID %d on %s ready for attach\n", getpid(), hostname);
fflush(stdout);
while (0 == i)
sleep(5);
}
在Fortran中从this answer我了解到,在Fortran中,我可以简单地使用MPI_Get_processor_name
代替gethostname
。其他一切都很简单但flush
。怎么样?
我应该把它放在哪里?在主程序之后 MPI_Init
?
然后?我该怎么办?
对于编译选项有什么问题,我提到this并使用-v -da -Q
作为mpifort
包装器的选项。
This solution不适合我的情况,因为我需要至少在27个进程上运行程序,所以我只想检查一个进程。
答案 0 :(得分:1)
我实际上经常做的是在本地运行MPI作业并查看其功能。没有上述任何代码。然后,如果它挂起,我使用PID
找出进程的 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
,通常可以很容易地猜出哪个等级来自PID(它们往往是连续的,最低的是等级0) 。等级0以下是过程1641,而它们是等级1 pid 1642,依此类推......
gdb -pid
然后我只做help stack
并检查进程中的堆栈和局部变量。 (在GDB控制台中使用bt
)
最重要的是获得回溯,所以只需在控制台中打印write
。
这在检查死锁时效果很好。当你必须在某个特定的地方停下来时,不太好。然后你必须尽早附加调试器。
我不认为Fortran需要刷新。我认为Fortran print
和flush
必要时至少在我使用的编译器中刷新。
但你绝对可以使用use iso_fortran_env
flush(output_unit)
声明
write
只需在hostname
打印pid
和gdb -pid 12345
之后放置同花顺。但正如我所说,我只会从打印开始。
您要做的是登录该节点并使用类似
之类的方式将gdb附加到righ进程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
声明为> 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
。易失性意味着值可以随时更改,并且编译器必须从内存重新加载其值以进行检查。这需要Fortran 2003。
上面的代码将打印,例如
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
你只需选择你想要的等级并执行
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]
附加等级0等。当然,在不同的终端窗口中。
然后你会得到像
这样的东西{{1}}
答案 1 :(得分:0)
发布的代码基本上只是一个无限循环,旨在在您附加调试器时“暂停”执行。然后,您可以使用调试器控件退出此循环,程序将继续。你可以在fortran中编写一个等价的循环,所以如果你很高兴从另一个方法获取主机名和pid(参见VladimirF在他的回答中提到的mpi_get_processor_name,如果你乐意使用编译器扩展,gnu和intel编译器都提供了getpid
扩展名),您可以使用以下内容(感谢this answer作为睡眠示例)。
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
现在在另一个终端我连接到相关的机器并使用
找到相关的进程IDps -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!