使用GDB远程调试MPI

时间:2020-06-02 20:20:45

标签: c debugging raspberry-pi gdb mpi

我正在尝试调试pi的远程访问组中使用MPI编写的代码。我无法直接访问Pis,以便能够使用GUI调试代码。

我已经尝试过question中显示的使用屏幕,但每次尝试使用屏幕时 我收到此消息:

There are not enough slots available in the system to satisfy the 2 slots
that were requested by the application:
  screen

Either request fewer slots for your application, or make more slots available
for use.

如果我尝试告诉它仅使用1个屏幕,则mpiexec失败

mpiexec -N 16 --host 10.0.0.3 -np 1 screen -oversubscribe batSRTest3 shortpass.bat
--------------------------------------------------------------------------
mpiexec was unable to find the specified executable file, and therefore
did not launch the job.  This error was first reported for process
rank 0; it may have occurred for other processes as well.

NOTE: A common cause for this error is misspelling a mpiexec command
      line parameter option (remember that mpiexec interprets the first
      unrecognized command line token as the executable).

Node:       node1
Executable: screen

我看过openMPI常见问题解答,但该信息不适用于远程访问。我尝试关注this部分,但是当我输入

gdb --pid

在代码未运行的情况下什么也没有发生。该部分中的方法2也不起作用,因为使用Putty访问PI时无法打开多个窗口。

理想情况下,我希望能够在所有节点上运行时对其进行调试,并且当前要运行我的程序,我必须使用:

$ mpiexec -N 4 --host 10.0.0.3,10.0.0.4,10.0.0.5,10.0.0.6 -oversubscribe batSRTest shortpass.bat

这也引起了混乱,因为我什至不确定我是否正确添加了额外的参数。

我确实尝试使用gdb similiar调试共享的here答案,但是由于没有给它多个任务,导致MPI失败。

(gdb) exec-file batSRTest3
(gdb) run
Starting program: /home/pi/progs/batSRTest3 mpiexec -N 16 --host 10.0.0.3 -oversubscribe batSRTest3 shortpass.bat
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/arm-linux-gnueabihf/libthread_db.so.1".
[Detaching after fork from child process 17157]
[New Thread 0x7691a460 (LWP 17162)]
[New Thread 0x75d3d460 (LWP 17163)]
[node1:17153] *** An error occurred in MPI_Group_incl
[node1:17153] *** reported by process [141361153,0]
[node1:17153] *** on communicator MPI_COMM_WORLD
[node1:17153] *** MPI_ERR_RANK: invalid rank
[node1:17153] *** MPI_ERRORS_ARE_FATAL (processes in this communicator will now abort,
[node1:17153] ***    and potentially your MPI job)
[Thread 0x7691a460 (LWP 17162) exited]
[Thread 0x76ff5010 (LWP 17153) exited]
[Inferior 1 (process 17153) exited with code 06]
(gdb) q

1 个答案:

答案 0 :(得分:1)

调试MPI应用程序的问题在于它们以多个进程的形式运行,并且通常您没有直接访问这些进程的权限。因此,存在特殊的并行调试器,它们能够将自身集成到MPI作业中。最受欢迎的两种是TotalView和Arm DDT(以前称为Allinea DDT)。两者都是昂贵的商业产品,但是许多学术机构都购买了许可证,因此请检查您的情况是否如此。穷人的解决方案是使用GDB,它本身不是并行调试器,因此必须发挥创造力。

简而言之,该想法是在GDB的监督下启动MPI流程。但首先,让我们看一下Open MPI如何在多个节点上执行作业。下图应对此进行说明:

mpiexec <--+--> orted on node1 <--+--> rank 0
           |                      |
           |                      +--- rank 1
           |                      :
           |                      +--- rank N-1
           |
           +--- orted on node2 <--+--- rank N
           |                      |
           |                      +--- rank N+1
           |                      :
           :                      +--- rank 2N-1

mpiexec是MPI程序启动器,它负责读取诸如MPI等级数,主机列表,绑定策略等信息,并使用该信息来启动作业。对于与执行mpiexec的主机位于同一主机上的进程,它只是多次生成可执行文件。对于远程节点上的进程,它使用RSH,SSH或某种其他机制(对于SLURM,TM2等,使用srun)在每个远程主机上启动orted帮助程序,该程序随后会生成许多根据需要在其特定主机上排名。

与常规Unix程序不同,您永远不会通过控制台或Unix信号直接与MPI进程进行交互。相反,MPI运行时提供了I / O转发和信号传播的机制。您与mpiexec的标准输入和输出进行交互,然后使用一些基础结构将输入发送到等级0,并显示从所有等级接收到的输出。类似地,发送到mpiexec的信号被转换并传播到MPI等级。 MPI标准中没有完全指定I / O重定向或信号传播,因为它们是特定于平台的,但是一般的集群实现共识是所有等级的标准输出都转发到mpiexec的标准输出中而只有0级来自标准输入;其余等级的标准输入连接到/dev/null。上图中的有向箭头显示了这一点。实际上,Open MPI允许您通过将--stdin rank传递到mpiexec来选择哪个等级将接收标准输入。

如果执行gdb mpiexec ...,则不是在调试MPI应用程序。相反,您将调试未运行代码的MPI启动器本身。您需要在MPI运行时和MPI排名之间插入GDB,即上图应转换为:

mpiexec <--+--> orted on node1 <--+--> gdb <---> rank 0
           |                      |
           |                      +--- gdb <---> rank 1
           |                      :
           |                      +--- gdb <---> rank N-1
           |
           +--- orted on node2 <--+--- gdb <---> rank N
           |                      |
           |                      +--- gdb <---> rank N+1
           |                      :
           :                      +--- gdb <---> rank 2N-1

现在的问题变成了如何与众多GDB实例进行交互,主要是因为您只能直接与其中一个进行对话。使用TotalView和DDT,有一个GUI使用网络套接字与调试器组件进行通讯,因此可以解决此问题。对于许多GDB,您有两种选择(或更确切地说,就是骇客)。

第一个选择是仅调试单个行为不佳的MPI等级。如果错误总是在同一级别发生,则可以让它在GDB的控制下运行,而其余错误则独自运行,然后使用--stdin rank告诉mpiexec让您与如果等级不为0,则调试器。您需要一个简单的包装器脚本(称为debug_rank.sh):

#!/bin/sh
# Usage: debug_rank.sh <rank to debug> <executable> <arguments>

DEBUG_RANK=$1
shift
if [ $OMPI_COMM_WORLD_RANK == $DEBUG_RANK ]; then
   exec gdb -ex=run --args $*
else
   exec $*
fi

-ex=run告诉GDB在加载可执行文件后自动执行run命令。如果需要先设置断点,则可以忽略它。像这样使用包装器,例如调试等级3:

$ mpiexec ... --stdin 3 ./debug_rank.sh 3 batSRTest shortpass.bat

一旦等级3做得不好或达到断点,您将进入GDB命令提示符。您也可以不使用包装程序脚本而直接运行gdb,希望它不会在您期望调试的级别上落入其命令提示符中。如果发生这种情况,GDB将退出,因为它的标准输入将连接到/dev/null,从而降低了整个MPI作业,因为mpiexec会注意到一个等级退出而没有调用MPI_Finalize()

如果您不知道哪个特定的行列行为不正确,或者每个行之间的行列是否不同,或者您想在多个中断点中设置一个以上的断点,则需要解决输入重定向问题。而“最简单”的解决方案是使用X11终端仿真器,例如xterm。这里的窍门是GUI程序从窗口系统而不是标准输入获取输入,因此尽管标准输入已连接到{{1},您仍可以愉快地键入并将输入发送到运行在xterm内部的命令}。另外,X11是可以通过TCP / IP运行的客户端/服务器协议,允许您远程运行/dev/null,并在运行某些X11实现(例如X.org或XWayland)时将其显示在本地系统上。这正是“打开MPI”页面上显示的命令的作用:

xterm

这将启动$ mpiexec ... xterm -e gdb -ex=run --args batSRTest shortpass.bat 的多个副本,每个副本执行xterm。因此,您可以在自己的终端窗口中获​​得许多GDB实例,这使您可以与所有它们进行交互。为此,您需要做一些事情:

  • 每个Pi上应安装gdb -ex=run --args batSRTest shortpass.bat的副本
  • 您的网络应该是低延迟的网络,因为X11协议在具有较长延迟的网络上运行速度非常慢
  • 您的X11服务器应该可以从所有Pi到达,并且应该配置为接受来自它们的连接
  • xterm环境变量应相应设置

任何X11客户端应用程序(例如DISPLAY)都使用xterm环境变量中的值来确定如何连接到X11服务器。其值的一般格式为DISPLAY。对于管理单个显示器的本地服务器,<optional hostname>:<display>[.<screen>]通常是DISPLAY甚至只是:0.0。如果缺少:0,则意味着特殊值<optional hostname>,这意味着X11服务器正在侦听host/unix中的Unix域套接字。默认情况下,出于安全原因,X11服务器仅在Unix域套接字上侦听,这使得它们对于网络客户端而言是不可访问的。您需要启用对TCP / IP套接字的侦听并覆盖绑定地址(默认情况下为/tmp/.X11-unix/),并确保从Pis可以访问您的主机,即,它们可以直接连接到您的IP地址X11服务器侦听的TCP端口上。如果您采用这种方式,则它的工作方式如下:

  1. 为X11启用TCP连接,并使其在网络接口上侦听
  2. 检查系统上127.0.0.1的值
  3. 添加您的IP地址
  4. 像这样运行MPI作业:

DISPLAY

其中$ mpiexec ... -x DISPLAY=your.ip:d.s xterm -e gdb -ex=run --args batSRTest shortpass.bat是本地d.s设置为的显示和屏幕值。确保您的防火墙允许端口DISPLAY上的入站TCP连接。

并非总是建议甚至不可能从网络启用TCP连接,尤其是在使用NAT的情况下。因此,另一种解决方案是使用通过SSH的X11转发。为此,在连接到SSH服务器时,您需要将6000+d-X传递给SSH客户端:

-Y

$ ssh -X username@server 而非-Y启用了一些不受信任的扩展,对于某些X11应用程序可能是必需的。 X11转发仅在服务器端启用时才有效。还需要在服务器上安装-X。但是仅在服务器上启用X11转发是不够的,因为默认情况下,SSH服务器将在回送接口上侦听要转发的X11连接。对于OpenSSH,必须相应地设置以下两个配置参数:

xauth

如果SSH服务器配置正确且存在X11Forwarding yes # Enable X11 forwarding X11UseLocalhost no # Listen on all network interfaces 命令,则在SSH进入系统时,xauth的值应类似于DISPLAY并运行{{1} }应该会产生以下内容:

hostname:10.0

表示X11转发套接字已绑定到所有网络接口。然后,您应该像这样启动MPI作业:

netstat -an | grep 6010

其中tcp 0 0 0.0.0.0:6010 0.0.0.0:* LISTEN tcp6 0 0 :::6010 :::* LISTEN 是服务器在将服务器连接到Pis的网络中拥有的IP(我怀疑您的情况是$ mpiexec -x DISPLAY=server.ip:10.0 xterm -e gdb -ex=run --args batSRTest shortpass.bat )。另外,应在服务器的防火墙中启用以server.ip开头的一系列TCP端口。实际值取决于有多少个X11转发会话。默认情况下,10.0.0.1设置为6010,因此SSH服务器将以显示X11DisplayOffset开始并向上运行,直到找到未分配的显示编号。另外,如果您在Pis上的主目录未以某种方式与服务器共享(例如,通过NFS挂载),则还需要将在服务器的主目录中找到的10文件复制到主目录中在所有Pis上。该文件包含通过X11转发器进行身份验证所需的MIT Magic cookie,并且每次在启用X11转发的情况下通过SSH进入服务器时都会重新生成,因此请确保在每次SSH登录后再次将其复制到所有Pi。

现在,如果所有这一切似乎都过于复杂,则GDB还具有远程调试功能。您可以在服务器上启动GDB,然后在GDB服务器程序10的监督下运行远程MPI进程,然后在本地GDB中使用远程调试命令连接到其中一台GDB服务器。这很麻烦。您需要告诉每个GDB服务器在不同的端口上进行侦听。包装脚本(.Xauthority)可能会有所帮助:

gdbserver

像这样运行:

debug_server.sh

它将打印不同GDB服务器实例正在侦听的主机名和端口的列表。在不带参数的情况下触发GDB并发出以下命令:

#!/bin/sh
# Usage: debug_server.sh <executable> <arguments>

GDB_HOST=$(hostname)
GDB_PORT=$(( 60000 + $OMPI_COMM_WORLD_RANK ))
echo "GDB server for rank $OMPI_COMM_WORLD_RANK available on $GDB_HOST:$GDB_PORT"
exec gdbserver :$GDB_PORT $*

其中$ mpiexec ... ./debug_server.sh batSRTest shortpass.bat (gdb) target remote hostname:port 是目标Pi的IP(或主机名,如果可以解析)。 GDB服务器自动在可执行文件的入口处中断,该入口很有可能位于动态链接器中的某个位置,因此您需要发出hostname命令以使其运行。您需要为每个GDB服务器实例执行该操作,而且我不知道一种在不停止当前目标的情况下与其断开连接的方法,因此您可能也需要启动GDB的加载。

GDB可能有一些GUI可以简化此操作。您可以研究提供并行调试器的Eclipse PTP项目,并查看它是否对您有用。您可能会发现these slides有用。我个人从未使用过PTP,也不知道它能做什么。


基本上,这就是为什么除了最复杂的情​​况以外,大多数MPI调试都使用port完成的原因。将continue添加到printf()参数列表中,以使其在每行输出之前加上其来自的作业ID和等级ID,因此您不必自己打印该信息。