12.3. 使用串行调试器调试Open MPI应用程序

由于GNU调试器(gdb)几乎随处可得,通常使用串行调试器来调试Open MPI应用程序。并行调试器通常更好,但gdb是免费的,因此相当常见。

使用串行调试器有两种常见方法。

12.3.1. 附加到单个运行的MPI进程

您可以使用多次调用串行调试器来单独附加到每个应用程序进程,或应用程序进程的子集。

如果您正在使用gdb,则需要登录到要调试应用程序进程的节点,并通过指定gdb的--pid选项以及要附加的每个应用程序进程的PID来调用gdb。

您可以使用类似的技术附加到刚刚由mpirun(1)调用的应用程序中的进程。

一种虽然不够优雅但实用的常用技术是,在您希望暂停应用程序进程直到调试器附加的位置(例如在MPI初始化之后)插入以下代码。

{
    volatile 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);
}

这段代码会向标准输出打印一行信息,显示进程运行所在的主机名以及需要附加的进程ID(PID)。随后它会无限循环执行sleep(3)函数,等待您使用调试器进行附加。在循环内部使用sleep(3)意味着在等待附加期间,处理器不会持续处于100%负载状态。

当您附加调试器后,沿着函数调用栈向上查找,直到进入这段代码块(您很可能在sleep(3)期间附加),然后将变量i设为非零值。使用GDB时的语法如下:

(gdb) set var i = 7

然后在代码块后设置断点并继续执行,直到触发断点。此时您就可以控制正在运行的MPI应用程序,并充分利用调试器的全部功能。

你甚至可以添加条件判断,仅允许应用程序中特定的MPI进程(例如MPI_COMM_WORLD排名为0的进程,或任何出现异常行为的进程)使用这个"暂停"功能。

12.3.2. 使用 mpirun 启动独立实例的串行调试器

该技术为MPI_COMM_WORLD中的每个MPI进程启动一个独立窗口,每个窗口运行一个串行调试器(如gdb)来启动和运行您的MPI应用程序。为每个MPI进程设置独立窗口对于低进程数的MPI作业非常方便,但需要一些Open MPI之外的设置和配置才能正常工作。简单假设以下方法能直接生效是不明智的:

shell$ mpirun -n 4 xterm -e gdb my_mpi_application

如果在个人电脑上运行,这很可能可行。你也可以使用tmpi来在独立的tmux面板而非单独的xterm窗口中启动调试器,这样做的好处是可以同步所有调试器实例之间的键盘输入。

遗憾的是,tmpixterm方法在计算集群上很可能无法使用。需要考虑以下几个因素:

  1. Open MPI 使用什么启动器?在基于ssh的环境中,Open MPI 默认会使用ssh。但请注意,出于可扩展性考虑,Open MPI 会在 MPI 作业启动时关闭ssh会话。这意味着内置的 SSH X 转发隧道会在xterms启动前被关闭。虽然可以强制 Open MPI 保持其 SSH 连接活跃(以保持 X 隧道可用),但我们建议尽可能使用非 SSH 隧道的 X 连接(见下文)。

  2. 在非ssh环境(例如使用资源管理器时),调用mpirun的进程环境可能会被复制到所有节点。在这种情况下,DISPLAY环境变量可能不适用。

  3. 某些操作系统默认禁用X11服务器监听远程/网络流量。例如,参阅Open MPI用户邮件列表中的这篇帖子了解如何在Fedora Linux上启用X11服务器的网络访问。

  4. 可能存在中间防火墙或其他网络阻断,阻止X流量在运行MPI进程(和xterm)的主机与连接输出显示的主机之间传输。

让远程X应用程序(如xterm)显示在本地屏幕上的最简单方法是放弃SSH隧道X转发的安全性。在高性能计算集群等封闭环境中,这种做法可能是可以接受的(实际上,如果集群节点的SSH登录被禁用,您甚至可能无法使用SSH X转发),但请与您的安全管理员确认以确保安全。

如果允许使用非加密的X11转发,我们建议如下:

  1. 对于每个将运行MPI进程的非本地主机,使用xhost命令将其添加到X服务器的权限列表中。例如:

    shell$ cat my_hostfile
    inky
    blinky
    stinky
    clyde
    shell$ for host in `cat my_hostfile` ; do xhost +host ; done
    
  2. 使用-x选项传递给mpirun来导出合适的DISPLAY环境变量,这样启动的X应用程序就能知道将输出发送到哪里。通常(但不总是)合适的值是包含目标显示设备的主机名加上:0(或:0.0)后缀。例如:

    shell$ hostname
    arcade.example.come
    shell$ mpirun -n 4 --hostfile my_hostfile \
        -x DISPLAY=arcade.example.com:0 xterm -e gdb my_mpi_application
    

    警告

    X流量相当"重"——如果您在慢速网络连接上操作,可能需要一些时间才能在屏幕上看到xterm窗口。

  3. 如果你的xterm支持该功能,-hold选项可能会很有用。 -hold会告诉xterm即使在应用程序 运行结束后也保持打开状态。这意味着如果出现问题(例如, gdb执行失败,或意外终止,或...), xterm窗口将保持打开,让你能够查看发生了什么, 而不是立即关闭并丢失可能输出的任何错误信息。

  4. 完成后,您可能希望禁用之前使用的主机的X11网络权限。再次使用xhost来禁用这些权限:

    shell$ for host in `cat my_hostfile` ; do xhost -host ; done
    

注意

mpirun 在所有 xterm 实例完成之前不会结束。