我如何debugging一个MPI程序?
我有一个编译和运行的MPI程序,但我想通过它来确保没有奇怪的事情发生。 理想情况下,我想要一个简单的方法来将GDB附加到任何特定的进程,但我不确定这是可能的还是如何去做。 另一种方法是让每个进程都将debugging输出写入一个单独的日志文件,但是这并没有给debugging器带来同样的自由。
有更好的方法吗? 你如何debuggingMPI程序?
正如别人所说, TotalView是这个标准。 但是这将花费你一只arm和一条腿。
OpenMPI网站有关于MPIdebugging的很好的FAQ 。 FAQ中的Item#6描述了如何将GDB附加到MPI进程。 阅读整个事情,有一些伟大的提示。
如果您发现有太多的进程需要跟踪,请查看堆栈跟踪分析工具(STAT) 。 我们在Livermore使用这个工具从潜在的数十万个正在运行的进程中收集堆栈跟踪信息,并向用户智能地表示它们。 这不是一个全function的debugging器(一个function齐全的debugging器永远不会扩展到208k内核),但它会告诉你哪些进程组正在做同样的事情。 然后,您可以在标准debugging器中遍历每个组的代表。
我发现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] [...]
正如其他人所提到的那样,如果您只使用less量的MPI进程,则可以尝试使用多个gdb会话 ,可怕的valgrind或者自己的printf / logging解决scheme。
如果你使用更多的进程,你真的开始需要一个适当的debugging器。 OpenMPI FAQ推荐Allinea DDT和TotalView 。
我工作在Allinea滴滴涕 。 这是一个全function的graphics源代码debugging器,所以是的,你可以:
- debugging或附加(超过200k)MPI进程
- 步骤并分组或单独暂停
- 添加断点,手表和跟踪点
- 捕捉内存错误和泄漏
…等等。 如果你已经使用Eclipse或Visual Studio,那么你就在家里。
我们为debugging并行代码(无论是MPI,multithreading还是CUDA)增加了一些有趣的function:
-
标量variables在所有进程中自动比较:
-
您还可以跟踪和过滤过程和时间上的variables和expression式的值:
它被广泛应用于500个 HPC站点,如ORNL , NCSA , LLNL , Jülich等。 人。
界面很漂亮, 我们计划在0.1秒时间内逐步合并220,000个过程的堆栈和variables,作为Oak Ridge捷豹集群验收testing的一部分。
@tgamblin提到了与Allinea DDT集成的优秀STAT ,就像其他一些stream行的开源项目一样。
这里的许多post都是关于GDB的,但是没有提到如何附加到启动过程。 显然,你可以附加到所有的进程:
mpiexec -n X gdb ./a.out
但是,这是非常无效的,因为你必须反弹来启动你的所有stream程。 如果您只想debugging一个(或less量的)MPI进程,则可以使用:
运算符将其作为单独的可执行文件添加到命令行中:
mpiexec -n 1 gdb ./a.out : -n X-1 ./a.out
现在只有一个进程会得到GDB。
http://github.com/jimktrains/pgdb/tree/master是我写的一个实用程序来做这件事情。; 有一些文件,并随时向我提问。
你基本上调用一个perl程序来封装GDB,并将它的IO集中到一个中央服务器上。 这允许GDB在每个主机上运行,并让您在terminal的每个主机上访问它。
与gdb
一起使用screen
来debuggingMPI应用程序可以很好地工作,特别是如果xterm
不可用或者您正在处理多个处理器。 一路上有很多陷阱伴随着stackoverflowsearch,所以我将全面重现我的解决scheme。
首先,在MPI_Init之后添加代码以打印PID并暂停程序以等待您附加。 标准的解决scheme似乎是一个无限循环; 我最终决定raise(SIGSTOP);
,这需要在gdb中continue
转义的额外呼叫。
} 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。 然后你可以grep
stderr文件中的某个关键字(这里是文字PID)来获取每个进程的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 $MDRUN_EXE $PID
将gdb会话附加到每个进程。 在屏幕会话中这样做可以轻松访问任何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
命令将input脚本编写成脚本(以便您不必input每个屏幕并input相同的内容)。 在命令的末尾需要一个换行符。 这里的屏幕可以通过-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会话。
debuggingMPI程序的“标准”方法是使用支持该执行模型的debugging器。
在UNIX上, TotalView据说具有良好的MPI支持。
还有我的开源工具,padb,旨在帮助并行编程。 我称之为“工作检查工具”,因为它不仅起到debugging器的作用,还可以作为例如程序的平行顶端。 在“完整报告”模式下运行,它将显示你的应用程序中每个进程的堆栈跟踪以及每个级别的每个函数的本地variables(假设你用-g编译)。 它还会显示“MPI消息队列”,即作业中每个等级的未完成发送和接收列表。
除了显示完整的报告之外,还可以告诉padb放大作业中各个位的信息,有大量的选项和configuration项来控制显示的信息,请参阅网页以获取更多详细信息。
Padb
我使用这个小小的homebrewn方法将debugging器附加到MPI进程 – 在代码中紧跟在MPI_Init()之后调用以下函数DebugWait()。 现在,当进程正在等待键盘input时,你总是将debugging器附加到它们并添加断点。 完成后,提供一个字符input,然后就可以开始了。
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); }
当然,你只想编译这个函数来进行debugging版本。
将gdb附加到mpi进程的命令不完整,应该是
mpirun -np <NP> xterm -e gdb ./program
有关mpi和gdb的简要讨论可以在这里find
我使用日志跟踪进行了一些与MPI相关的debugging,但是如果您使用mpich2: MPICH2和gdb ,也可以运行gdb 。 这种技术通常是一个很好的练习,当你处理从debugging器启动的一个棘手的过程。
另一个解决scheme是在SMPI中运行你的代码,即模拟的MPI。 这是一个我参与其中的开源项目。 每个MPI等级将被转换成相同的UNIX进程的线程。 然后你可以很容易的使用gdb来进行MPI的排名。
SMPI提出了研究MPI应用的其他优点:clairevoyance(你可以观察系统的每个部分),可重复性(几次运行导致完全相同的行为,除非你指定),没有heisenbugs(因为模拟平台保持不同从主机一)等
有关更多信息,请参阅此演示文稿或相关答案 。
如果你是一个tmux
用户,使用Benedikt Morbach脚本: tmpi
会感觉很舒服
https://github.com/moben/scripts/blob/master/tmpi
有了它你有多个面板(进程数量)全部同步(每个命令同时在所有面板或进程上复制,所以与xterm -e
方法相比可以节省大量时间)。 此外,您可以在不需要移动到另一个面板的情况下知道variables的值,而是在每个面板上打印每个过程的variables值。
如果您不是tmux
用户,我build议您尝试一下,看看。