如何调试MPI程序?
我有一个编译和运行的MPI程序,但我想通过它来确保没有奇怪的事情发生。理想情况下,我想要一个简单的方法将GDB附加到任何特定的进程,但我不确定这是可能的还是如何去做。另一种方法是让每个进程将调试输出写入一个单独的日志文件,但这并没有给调试器带来同样的自由。如何调试MPI程序?
有没有更好的方法?你如何调试MPI程序?
正如别人所说,TotalView是这个标准。但它会让你胳膊和腿。
该OpenMPI网站有一个伟大的FAQ on MPI debugging。 FAQ中的项目#6描述了如何将GDB附加到MPI流程。阅读整个事情,有一些很棒的提示。
如果您发现您有太多过程需要跟踪,请查看Stack Trace Analysis Tool (STAT)。我们在Livermore使用它来从潜在的数十万个正在运行的流程中收集堆栈跟踪信息,并向用户智能地呈现它们。这不是一个全功能的调试器(一个全功能的调试器永远不会扩展到208k内核),但它会告诉你哪些进程组正在做同样的事情。然后,您可以在标准调试器中遍历每个组的代表。
http://github.com/jimktrains/pgdb/tree/master是我写的用来做这件事情的工具。有一些文档,请随时向我提问。
你基本上调用一个perl程序来封装GDB,并将它的IO引导到中央服务器。这允许GDB在每台主机上运行,并让您在终端上的每台主机上访问它。
谢谢!下次我在MPI工作时,我一定会检查一下。 – 2009-04-09 03:45:27
我使用这个小小的homebrewn方法将调试器附加到MPI进程 - 在代码中的MPI_Init()之后立即调用以下函数DebugWait()。现在,当进程正在等待键盘输入时,您有时间将调试器附加到它们并添加断点。完成后,提供单个字符输入,然后就可以开始了。
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);
}
当然,你只想编译这个函数来调试构建只。
即使是简单的代码,MPI也需要最多的调试语句。 (哈哈)这可能非常有帮助。 – Troggy 2009-07-02 16:38:54
此解决方案与此处的第6项类似(http://www.open-mpi.org/faq/?category=debugging#serial-debuggers)。你可以通过添加`gethostname(hostname,sizeof(hostname))来改善你的代码。 printf(“主机%s上的PID%d准备挂载\ n”,getpid(),hostname);`。然后,通过键入`rsh
我使用日志跟踪进行了一些与MPI相关的调试,但是如果您使用的是mpich2,也可以运行gdb:MPICH2 and gdb。在处理从调试器启动时很棘手的进程时,这种技术通常是一种很好的做法。
链接已损坏。 – 2015-11-21 02:08:49
还有我的开源工具padb,它旨在帮助并行编程。我称它为“工作检查工具”,因为它的功能不仅仅是因为调试器也可以用作例如程序之类的平行顶端。以“完整报告”模式运行,它将向您显示应用程序中每个进程的堆栈跟踪以及每个级别的每个函数的本地变量(假设您使用-g编译)。它还会显示“MPI消息队列”,即作业中每个等级的未完成发送和接收列表。
除了显示完整的报告,还可以告诉padb放大作业中各个位的信息,有大量的选项和配置项来控制显示的信息,请参阅网页更多细节。
我发现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] [...]
正如其他人所提到的,如果您只使用MPI流程的少数您可以尝试使用multiple gdb sessions,可怕的valgrind或推出自己的printf/logging解决方案。
如果您使用的是更多的进程,那么您确实需要一个适当的调试器。 OpenMPI FAQ推荐使用Allinea DDT和TotalView。
我在Allinea DDT上工作。这是一个全功能的,图形化的源代码调试所以是的,你可以:
- 调试或附着(超过20万)MPI进程
- 步骤,并暂停它们在群体或个别
- 添加断点,手表和跟踪点
- 捕捉内存错误和泄漏
...等等。如果你已经使用Eclipse或Visual Studio,那么你就会在家。
我们添加了一些有趣的特征专门用于调试并行代码(无论是MPI,多线程或CUDA):
标量变量自动跨所有工艺相比:
您还可以跟踪和过滤变量和表达式在整个过程和时间上的值:
它广泛应用于之中top500 HPC网站,如ORNL,NCSA,LLNL,Jülich等。人。
界面非常活泼;我们在0.1秒时间内逐步合并了220,000个过程的堆栈和变量,作为Oak Ridge捷豹集群验收测试的一部分。
@tgamblin提到了优秀的STAT,它与Allinea DDT集成在一起,就像几个其他流行的开源项目一样。
这里的很多帖子都是关于广发行,但没有提到如何从启动时连接到进程。很明显,你可以连接到所有进程:
mpiexec -n X gdb ./a.out
但是,这是疯狂无效的,因为你必须反弹,启动所有进程。
mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out
现在只有你的程序将获得广发行的一个:如果你只是想调试一个(或少数)MPI过程中,可以使用:
操作者补充说,在命令行上一个单独的可执行文件。
使用screen
和gdb
一起调试MPI应用程序很好,特别是如果xterm
不可用或者您处理的处理器超过几个。伴随着stackoverflow搜索,一路上有许多陷阱,所以我将全面重现我的解决方案。
首先,在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)的stderr文件grep
获取每个进程的PID和排名。
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 $MDRUN_EXE $PID
。在屏幕会话中这样做可以轻松访问任何gdb会话。-d -m
以分离模式启动屏幕,-S "P$RANK"
允许您命名屏幕以便以后轻松访问,而bash的-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
一旦GDB的屏幕已经开始,你可以使用脚本输入到屏幕(这样你就不必每次都输入屏幕,然后键入同样的事情),使用屏幕的-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"
附加到任何屏幕并使用Ctrl+A+D
分离。类似于前面的代码段,可以将命令发送到所有的gdb会话。
另一种解决方案是在模拟MPI SMPI内运行代码。这是一个我参与其中的开源项目。每个MPI等级都将被转换为同一个UNIX进程的线程。然后,您可以轻松使用gdb来执行MPI等级。 SMPI提出了MPI应用研究的其他优点:clairevoyance(你可以观察系统的每个部分),再现性(几次运行导致完全相同的行为,除非你指定),缺少heisenbugs(如模拟平台保持不同于主机)等。
欲了解更多信息,请参阅this presentation或related answer。
如果你是一个tmux
用户,你会觉得很舒服使用贝内迪克特摩擦产品有限公司的脚本:tmpi
https://github.com/moben/scripts/blob/master/tmpi
有了它,你有多个面板(进程数)的所有同步(每一个命令被同时复制到所有面板或进程上,因此与xterm -e
方法相比,您可以节省大量时间)。此外,您可以在不需要移动到另一个面板的情况下知道变量的值,只需执行print
,这将在每个面板上打印每个过程的变量值。
如果您不是tmux
用户,我强烈建议您尝试一下并查看。
截至2010年[Allinea DDT](http://www.olcf.ornl.gov/2010/07/12/upgrade-adds-muscle-to-debugger/)是一个全功能的调试器, 208k核心 – Mark 2012-05-31 12:00:10
因此,我会继续努力,并且在这里提高Mark @的答案。滴滴涕很好。试试看吧。 TotalView现在也与STAT集成,所以如果你的站点有一个TotalView安装,你也可以尝试。 LLNL保留了TotalView和DDT,TotalView终于有了一些激烈的竞争。 – tgamblin 2012-06-07 00:13:27
我想再次链接到关于MPI调试的常见问题(http://www.open-mpi.org/faq/?category=debugging#serial-debuggers)。具体来说,子弹6是一个很好,快速和简单的(即使是我也足够了!)了解至少调试单个进程的方法。 – Jeff 2012-08-19 18:30:26