簡體   English   中英

使用 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 attach\n", getpid(), hostname);
    fflush(stdout);
    while (0 == i)
        sleep(5);
}

在 Fortran 中。 這個答案我了解到,在 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 writeprint刷新至少在我使用的編譯器中是必要的。

但是你絕對可以使用flush語句

use iso_fortran_env

flush(output_unit)

只需在您write后打印hostnamepid 但正如我所說,我只會從打印開始。

您要做的是登錄到該節點並將 gdb 附加到正確的進程,例如

gdb -pid 12345

對於 sleep,您可以使用許多編譯器中提供的非標准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擴展名),您可以使用以下內容(感謝對 sleep 示例的回答)。

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!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM