使用GDB调试Fortran中的MPI程序

时间:2016-08-01 12:42:48

标签: c++ debugging fortran gdb mpi

我看了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个进程上运行程序,所以我只想检查一个进程。

2 个答案:

答案 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 printflush必要时至少在我使用的编译器中刷新。

但你绝对可以使用use iso_fortran_env flush(output_unit) 声明

write

只需在hostname打印pidgdb -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

现在在另一个终端我连接到相关的机器并使用

找到相关的进程ID
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!