[英]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 write
和print
刷新至少在我使用的編譯器中是必要的。
但是你絕對可以使用flush
語句
use iso_fortran_env
flush(output_unit)
只需在您write
后打印hostname
和pid
。 但正如我所說,我只會從打印開始。
您要做的是登錄到該節點並將 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.