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
中,您只需使用