C++ 用GDB在Fortran中调试MPI程序
我阅读并到达,所以现在我想我应该(如果不是,请告诉我)重写代码C++ 用GDB在Fortran中调试MPI程序,c++,debugging,fortran,gdb,mpi,C++,Debugging,Fortran,Gdb,Mpi,我阅读并到达,所以现在我想我应该(如果不是,请告诉我)重写代码 { 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); } 用Fort
{
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语言。据我所知,在Fortran中,我可以简单地使用MPI\u Get\u processor\u 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控制台中使用帮助堆栈)
最重要的是获得回溯,因此只需在控制台中打印bt
在检查死锁时,这将非常有效。当你不得不在某个特定的地方停下来时,情况就不那么好了。然后您必须尽早附加调试器
您的代码: 我认为在Fortran中不需要刷新。我认为至少在我使用的编译器中,Fortran
写和打印刷新是必要的
但是您肯定可以使用flush
语句
use iso_fortran_env
flush(output_unit)
只需在打印主机名和pid的write
之后刷新即可。但正如我所说的,我将只从印刷开始
您需要做的是登录到该节点,并使用类似于
gdb -pid 12345
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
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]
> mpif90 prog.f90 -O0 -g -o prog.exe
Rank 0 has escaped!
对于睡眠,您可以使用许多编译器中提供的非标准sleep
内部子例程,也可以编写自己的子例程
是在MPI_Init之前还是之后?如果要打印排名,必须在后面。同样,如果要使用MPI\u Get\u processor\u name
,它必须在后面。通常建议在程序中尽早调用MPI_Init
代码是这样的
gdb -pid 12345
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
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]
> mpif90 prog.f90 -O0 -g -o prog.exe
Rank 0 has escaped!
重要提示:如果使用优化进行编译,编译器会发现i==0
永远不会为真,并且会完全删除检查。您必须降低优化或将i
声明为volatile
。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,依此类推。当然,在另一个终端窗口中
然后你会得到这样的结果
gdb -pid 12345
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
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]
> mpif90 prog.f90 -O0 -g -o prog.exe
Rank 0 has escaped!
mpi_gdb.f90:26处的主菜单(MAIN)
26如果(i==0)退出
(gdb)本地信息
主机名='linux.site','
主机名\u len=10
i=1
ie=0
pid=2457
排名=0
(gdb)设置变量i=0
(gdb)续
持续的。
[次1(进程2355)正常退出]
发布的代码基本上只是一个无限循环,用于在附加调试器时“暂停”执行。然后可以使用调试器控件退出此循环,程序将继续。您可以用fortran编写一个等价的循环,因此如果您愿意从另一个方法获取主机名和pid(请参阅VladimirF在回答中提到的mpi_get_processor_name,如果您愿意使用编译器扩展,gnu和intel编译器都提供了getpid
扩展),您可以使用以下内容(感谢您提供的睡眠示例)
现在,使用类似
gdb -pid 12345
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
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]
> mpif90 prog.f90 -O0 -g -o prog.exe
Rank 0 has escaped!
如果我现在在本地机器的两个核心上使用
> mpirun -np 2 ./prog.exe
在屏幕上我看到的只是
rank = 0
rank = 1
现在在另一个终端中,我连接到相关的机器,并使用
ps -ef | grep prog.exe
这给了我几个对应于不同等级的进程id值
gdb --pid <pidFromPSCmd> ./prog.exe
在我们最初的终端,我们会看到
gdb -pid 12345
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
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]
> mpif90 prog.f90 -O0 -g -o prog.exe
Rank 0 has escaped!
目前程序没有挂起;我只是遇到了一个分段错误的问题,所以当我启动可执行文件时,执行会以一个错误终止。那么我该怎么做呢?gdb-pid?你从_getpid
得到的_pid?。程序打印的值或者你在top
中看到的值gelis,但我从未发现GDB对于segfaults如此常见。我使用编译器检查-fcheck=all
或清理。或者只在正确的位置打印变量。如果您有回溯,但您应该能够在没有GDB的情况下通过类似(-fbacktrace
或-traceback
)的方式获得一个.getpid?如果“没有上面的任何代码”呢?可能我遗漏了什么。在SEGFULT之后,执行被终止,我想没有什么可以看的了。这是我希望遵循一些变量的方式,以便了解错误(可能我太沉迷于MATLAB的调试).打印变量?我认为这是一种肮脏的方式。但如果你这样做,也许没那么糟糕!@EnricoMariaDeAngelis我的意思是使用答案中的代码,打印PID!并阅读如何使用它。另一个答案也将帮助你理解其用法。未绑定循环正好放在你需要停止代码并附加调试器的地方。您必须知道要检查代码的哪一部分。在top
中,您只需使用