我有一个编译和运行的MPI程序,但我想逐步完成它以确保没有任何奇怪的事情发生.理想情况下,我想要一种简单的方法将GDB附加到任何特定的进程,但我不确定这是否可行或如何做到这一点.另一种方法是让每个进程将调试输出写入单独的日志文件,但这并不能提供与调试器相同的自由.
有更好的方法吗?你如何调试MPI程序?
我发现gdb非常有用.我用它作为
mpirun -npxterm -e gdb ./program
这将启动我可以做的xterm窗口
run...
通常工作正常
您还可以使用以下命令将这些命
mpirun -nxterm -hold -e gdb -ex run --args ./program [arg1] [arg2] [...]
正如其他人所说,TotalView是这方面的标准.但它会花费你一条胳膊和一条腿.
OpenMPI站点有一个关于MPI调试的很棒的FAQ.FAQ中的第6项描述了如何将GDB附加到MPI进程.阅读整篇文章,有一些很棒的提示.
但是,如果发现有太多的进程需要跟踪,请查看堆栈跟踪分析工具(STAT).我们在利弗莫尔使用它来收集可能数十万个正在运行的进程的堆栈跟踪,并智能地向用户表示它们.它不是一个功能齐全的调试器(一个功能齐全的调试器永远不会扩展到208k核心),但它会告诉你哪些进程组正在做同样的事情.然后,您可以在标准调试器中逐步查看每个组的代表.
这里的许多帖子都是关于GDB的,但是没有提到如何从启动时附加到进程.显然,您可以附加到所有进程:
mpiexec -n X gdb ./a.out
但是,由于你不得不反弹以启动所有进程,因此效果极差.如果您只想调试一个(或少量)MPI进程,可以使用:
运算符在命令行上将其添加为单独的可执行文件:
mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out
现在只有一个进程可以获得GDB.
正如其他人所提到的,如果您只使用少数 MPI进程,您可以尝试使用多个gdb会话,这是可靠的valgrind或滚动您自己的printf/logging解决方案.
如果你使用的是更多的进程,那么你真的需要一个合适的调试器.该的openmpi FAQ建议都Allinea DDT和TotalView软件.
我在Allinea滴滴涕上工作.它是一个功能齐全的图形源代码调试器,所以是的,您可以:
调试或附加到(超过200k)MPI进程
逐步或单独暂停它们
添加断点,监视和跟踪点
捕获内存错误和泄漏
...等等.如果你使用过Eclipse或Visual Studio,那么你就会在家里.
我们添加了一些专门用于调试并行代码的有趣功能(无论是MPI,多线程还是CUDA):
标量变量在所有进程中自动进行比较:
您还可以在进程和时间上跟踪和过滤变量和表达式的值:
它广泛应用于500强 HPC网站,如ORNL,NCSA,LLNL,Jülich等.人.
界面非常活泼; 作为Oak Ridge Jaguar集群验收测试的一部分,我们计划在0.1秒时步进和合并220,000个过程的堆栈和变量.
@tgamblin提到了优秀的STAT,它与Allinea DDT集成,其他几个流行的开源项目也是如此.
http://valgrind.org/ nuf说
更具体的链接:使用Valgrind调试MPI并行程序
http://github.com/jimktrains/pgdb/tree/master是我写的一个实用程序来做这件事.有一些文档,请随时向我提问.
你基本上调用一个包装GDB的perl程序,并将它的IO汇集到一个中央服务器上.这允许GDB在每个主机上运行,并允许您在终端的每个主机上访问它.
如果您是tmux
用户,那么使用Benedikt Morbach的脚本会感到非常满意:tmpi
原始资料: https://github.com/moben/scripts/blob/master/tmpi
叉:https : //github.com/Azrael3000/tmpi
有了它,您可以同步所有多个面板(进程数)(每个命令同时复制到所有面板或进程上,因此与该xterm -e
方法相比可以节省很多时间)。此外,您可以知道要执行的过程中变量的值,print
而不必移动到另一个面板,这将在每个面板上打印每个过程的变量值。
如果您不是tmux
用户,我强烈建议您尝试一下。
使用screen
起来与gdb
调试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编译后,在后台运行可执行文件,并捕获stderr.然后
grep
,您可以在某个关键字(此处为文字PID)的stderr文件中获取每个进程的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"
允许您命名屏幕以便以后轻松访问,-l
bash选项以交互模式启动它并使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会话,类似于前一段代码.