如何调试MPI程序?

问题描述 投票:115回答:16

我有一个编译和运行的MPI程序,但我想逐步完成它以确保没有任何奇怪的事情发生。理想情况下,我想要一种简单的方法将GDB附加到任何特定的进程,但我不确定这是否可行或如何执行。另一种方法是让每个进程将调试输出写入单独的日志文件,但这并不能提供与调试器相同的自由。

有更好的方法吗?你如何调试MPI程序?

debugging mpi
16个回答
58
投票

正如其他人所说,TotalView是这方面的标准。但它会花费你一条胳膊和一条腿。

OpenMPI网站有一个很棒的FAQ on MPI debugging。 FAQ中的第6项描述了如何将GDB附加到MPI进程。阅读整篇文章,有一些很棒的提示。

但是,如果您发现有太多的流程需要跟踪,请查看Stack Trace Analysis Tool (STAT)。我们在利弗莫尔使用它来收集可能数十万个正在运行的进程的堆栈跟踪,并智能地向用户表示它们。它不是一个功能齐全的调试器(一个功能齐全的调试器永远不会扩展到208k核心),但它会告诉你哪些进程组正在做同样的事情。然后,您可以在标准调试器中逐步查看每个组的代表。


2
投票

我使用这个小的homebrewn方法将调试器附加到MPI进程 - 在代码中的MPI_Init()之后调用以下函数DebugWait()。现在,当进程正在等待键盘输入时,您有足够的时间将调试器附加到它们并添加断点。完成后,提供单个字符输入,您就可以开始使用了。

tmux

当然,您只想为调试版本编译此函数。


2
投票

还有我的开源工具padb,旨在帮助进行并行编程。我将其称为“作业检查工具”,因为它不仅可以用作调试器,还可以用作例如并行顶级程序。在“完整报告”模式下运行,它将显示应用程序中每个进程的堆栈跟踪以及每个级别上每个函数的局部变量(假设您使用-g编译)。它还会显示“MPI消息队列”,即作业中每个等级的未完成发送和接收列表。

除了显示完整报告之外,还可以告诉padb放大作业中的各个信息位,有无数选项和配置项来控制显示哪些信息,有关详细信息,请参阅网页。

TotalView


1
投票

mpirun -gdb

感谢static void DebugWait(int rank) { char a; if(rank == 0) { scanf("%c", &a); printf("%d: Starting now\n", rank); } MPI_Bcast(&a, 1, MPI_BYTE, 0, MPI_COMM_WORLD); printf("%d: Starting now\n", rank); }


1
投票

将gdb附加到mpi进程的命令是不完整的,应该是

Padb

有关mpi和gdb的简短讨论可以在http://www.ncsa.illinois.edu/UserInfo/Resources/Hardware/CommonDoc/mpich2_gdb.html找到


1
投票

我使用日志跟踪进行一些与MPI相关的调试,但是如果你使用的是mpich2:mpirun -np <NP> xterm -e gdb ./program ,你也可以运行gdb。当您处理从调试器启动时很难处理的过程时,这种技术通常是一种很好的做法。


1
投票

调试MPI程序的简单方法。

在main()函数中添加sleep(some_seconds)

像往常一样运行程序

here

程序将启动并进入睡眠状态。

所以你将有几秒钟的时间来通过ps找到你的进程,运行gdb并附加到它们。

如果您使用像QtCreator这样的编辑器,您可以使用

Debug-> Start debugging-> Attach to running application

并在那里找到你的流程。


0
投票

另一种解决方案是在SMPI(模拟MPI)中运行代码。这是一个我参与的开源项目。每个MPI排名都将转换为同一UNIX进程的线程。然后,您可以轻松使用gdb来执行MPI排名。

SMPI为MPI应用的研究提出了其他优势:clairevoyance(您可以观察系统的每个部分),重现性(几次运行导致完全相同的行为,除非您指定),没有heisenbugs(因为模拟平台保持不同来自主持人)等

有关更多信息,请参阅MPICH2 and gdb$ mpirun -np <num_of_proc> <prog> <prog_args>


74
投票

我发现gdb非常有用。我用它作为

mpirun -np <NP> xterm -e gdb ./program 

这将启动我可以做的xterm窗口

run <arg1> <arg2> ... <argN>

通常工作正常

您还可以使用以下命令将这些命

mpirun -n <NP> xterm -hold -e gdb -ex run --args ./program [arg1] [arg2] [...]

23
投票

这里的许多帖子都是关于GDB的,但是没有提到如何从启动时附加到进程。显然,您可以附加到所有进程:

mpiexec -n X gdb ./a.out

但是,由于你不得不反弹以启动所有进程,因此效果极差。如果您只想调试一个(或少量)MPI进程,可以使用:运算符在命令行上将其添加为单独的可执行文件:

mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out

现在只有一个进程可以获得GDB。


17
投票

正如其他人所提到的,如果您只使用少数MPI流程,您可以尝试使用multiple gdb sessions,更可靠的valgrind或滚动您自己的printf /日志记录解决方案。

如果你使用的是更多的进程,那么你真的需要一个合适的调试器。 OpenMPI FAQ推荐Allinea DDTTotalView

我在Allinea DDT工作。它是一个功能齐全的图形源代码调试器,所以是的,您可以:

  • 调试或附加到(超过200k)MPI进程
  • 逐步或单独暂停它们
  • 添加断点,监视和跟踪点
  • 捕获内存错误和泄漏

...等等。如果你使用过Eclipse或Visual Studio,那么你就会在家里。

我们添加了一些专门用于调试并行代码的有趣功能(无论是MPI,多线程还是CUDA):

  • 标量变量在所有进程中自动进行比较:
  • 您还可以在进程和时间上跟踪和过滤变量和表达式的值:

它广泛用于top500 HPC网站,如ORNLNCSALLNLJülich等。人。

界面非常活泼;作为Oak Ridge Jaguar集群验收测试的一部分,我们计划在0.1秒时步进和合并220,000个过程的堆栈和变量。

@tgamblin提到了与STAT集成的优秀Allinea DDT,以及其他几个流行的开源项目。



6
投票

http://github.com/jimktrains/pgdb/tree/master是我写的一个实用程序来做这件事。有一些文档,请随时向我提问。

你基本上调用一个包装GDB的perl程序,并将它的IO汇集到一个中央服务器上。这允许GDB在每个主机上运行,​​并允许您在终端的每个主机上访问它。


5
投票

使用screengdb来调试MPI应用程序很有效,特别是如果xterm不可用或者你正在处理多个处理器。随之而来的堆栈溢出搜索有许多陷阱,所以我将完整地重现我的解决方案。

首先,在MPI_Init之后添加代码以打印出PID并暂停程序以等待您附加。标准解决方案似乎是一个无限循环;我最终选择了raise(SIGSTOP);,这需要额外调用continue来逃避gdb。

}
    int i, id, nid;
    MPI_Comm_rank(MPI_COMM_WORLD,&id);
    MPI_Comm_size(MPI_COMM_WORLD,&nid);
    for (i=0; i<nid; i++) {
        MPI_Barrier(MPI_COMM_WORLD);
        if (i==id) {
            fprintf(stderr,"PID %d rank %d\n",getpid(),id);
        }
        MPI_Barrier(MPI_COMM_WORLD);
    }
    raise(SIGSTOP);
}

编译后,在后台运行可执行文件,并捕获stderr。然后你可以在某个关键字(这里是文字PID)的qderxswpoi stderr文件中获取每个进程的PID和等级。

grep

可以使用MDRUN_EXE=../../Your/Path/To/bin/executable MDRUN_ARG="-a arg1 -f file1 -e etc" mpiexec -n 1 $MDRUN_EXE $MDRUN_ARG >> output 2>> error & sleep 2 PIDFILE=pid.dat grep PID error > $PIDFILE PIDs=(`awk '{print $2}' $PIDFILE`) RANKs=(`awk '{print $4}' $PIDFILE`) 将gdb会话附加到每个进程。在屏幕会话中这样做可以轻松访问任何gdb会话。 gdb $MDRUN_EXE $PID以分离模式启动屏幕,-d -m允许您命名屏幕以便以后轻松访问,并且bash的-S "P$RANK"选项以交互模式启动它并使gdb不立即退出。

-l

一旦gdb在屏幕中启动,您可以使用屏幕的for i in `awk 'BEGIN {for (i=0;i<'${#PIDs[@]}';i++) {print i}}'` do PID=${PIDs[$i]} RANK=${RANKs[$i]} screen -d -m -S "P$RANK" bash -l -c "gdb $MDRUN_EXE $PID" done 命令将输入​​脚本输入屏幕(这样您就不必输入每个屏幕并键入相同的内容)。命令末尾需要换行符。在这里,-X stuff使用之前给出的名称访问屏幕。 -S "P$i"选项很关键,否则命令间歇性失败(根据您之前是否已连接到屏幕)。

-p 0

此时,您可以使用for i in `awk 'BEGIN {for (i=0;i<'${#PIDs[@]}';i++) {print i}}'` do screen -S "P$i" -p 0 -X stuff "set logging file debug.$i.log " screen -S "P$i" -p 0 -X stuff "set logging overwrite on " screen -S "P$i" -p 0 -X stuff "set logging on " screen -S "P$i" -p 0 -X stuff "source debug.init " done 附加到任何屏幕,并使用screen -rS "P$i"分离。可以将命令发送到所有gdb会话,类似于前一段代码。


5
投票

如果您是Ctrl+A+D用户,使用Benedikt Morbach的脚本会感觉很舒服:tmux

原始来源: tmpi

福克:https://github.com/moben/scripts/blob/master/tmpi

有了它,您可以同步多个面板(进程数)(每个命令同时复制到所有面板或进程上,因此与https://github.com/Azrael3000/tmpi方法相比,您可以节省大量时间)。此外,您可以在不需要移动到另一个面板的情况下知道您想要执行xterm -e的过程中的变量值,这将在每个面板上打印每个过程的变量值。

如果您不是print用户,我强烈推荐尝试并查看。


3
投票

调试MPI程序的“标准”方法是使用支持该执行模型的调试器。

在UNIX上,据说qazxsw poi对MPI有很好的支持。

© www.soinside.com 2019 - 2024. All rights reserved.