如何调试MPI程序?


129

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

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

Answers:


62

就像别人说的,TotalView是为此的标准。但这会花费您一臂之力。

OpenMPI网站提供了有关MPI调试的常见问题解答。FAQ中的第6项描述了如何将GDB附加到MPI流程。阅读全文,这里有一些很棒的技巧。

但是,如果发现您要跟踪的进程太多,请签出堆栈跟踪分析工具(STAT)。我们在Livermore上使用它来收集可能来自成千上万个正在运行的进程的堆栈跟踪,并将其智能地呈现给用户。它不是功能齐全的调试器(功能全面的调试器永远不会扩展到208k内核),但它会告诉您哪些进程组正在执行相同的操作。然后,您可以在标准调试器中逐步浏览每个组的代表。


14
截至2010年,Allinea DDT是一种功能全面的调试器,可扩展至超过208k内核
标记

1
因此,我将在这里继续@Mark的回答。滴滴涕很好。也尝试一下。TotalView现在也与STAT集成在一起,因此,如果您的站点安装了TotalView,则也可以尝试安装。LLNL始终保持TotalView和DDT的优势,而且TotalView最终有一些激烈的竞争,这是很好的选择。
Todd Gamblin 2012年

我想第二次链接到MPI调试常见问题解答(open-mpi.org/faq/?category=debugging#serial-debuggers)。具体来说,项目符号6是一种很好,快速且容易(对于我来说也足够!)的方法,它至少可以用来调试单个进程。
杰夫

FAQ页面#6中的步骤非常适合我,并帮助我找出了问题所在。非常感谢。
乔恩·迪顿

86

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

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

这会启动xterm Windows,我可以在其中执行此操作

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

通常工作正常

您还可以使用以下命令将这些命令打包在一起:

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

如何将相同的输入发送到所有NP gdb xterm?例如,我想向每个进程添加两个断点,并且有16个进程。除了xterm以外,还有其他替代方法吗?我们可以将会话连接到单个屏幕实例,tmux实例或Chris Jones的终结者实例吗?
osgx

@osgx您可以通过将命令(“ break xxx”,“ break yyy”,“ run”)保存到gdb <file>并传递-x <file>给gdb来执行此操作。
eush77 '16

但是我遇到一个错误,错误消息是“文件xterm上的execvp错误(没有这样的文件或目录)”
hitwlh

当我使用jdb和OpenMPI尝试此操作时,它不起作用,即每个jdb实例看到的num_ranks为1,而不是给-np参数指定的值。知道为什么吗?
米歇尔·穆勒

26

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

mpiexec -n X gdb ./a.out

但这是非常无效的,因为您必须重新启动才能启动所有流程。如果您只想调试一个(或少数几个)MPI进程,则可以使用:运算符在命令行上将其添加为单独的可执行文件:

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

现在,只有您的进程之一将获得GDB。


我可以使用“ mpiexec -n X gdb ./a.out”,但是有什么方法可以使用gdb -tui模式?
hitwlh

16

正如其他人提到的那样,如果您仅使用少数 MPI进程,则可以尝试使用多个gdb session,可重用的valgrind或滚动自己的printf / logging解决方案。

如果您使用的进程更多,那么您真的开始需要一个合适的调试器。该的openmpi FAQ建议都Allinea DDTTotalView软件

我在Allinea DDT上工作。它是功能齐全的图形化源代码调试器,因此可以,您可以:

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

...等等。如果您使用过Eclipse或Visual Studio,那么您就可以在家了。

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

  • 标量变量会在所有过程中自动进行比较:( 来源:allinea.com迷你图显示跨流程的价值

  • 您还可以跟踪和过滤过程和时间内的变量和表达式的值: 跟踪点随时间的日志值

在前500个 HPC站点中被广泛使用,例如ORNLNCSALLNLJülich等。等

界面非常活泼;在Oak Ridge的Jaguar集群进行验收测试的过程中,我们定时在0.1s的时间内步进和合并220,000个进程的堆栈和变量。

@tgamblin提到了出色的STAT,它与Allinea DDT集成在一起,还有其他一些流行的开源项目。



7

如果您是tmux用户,那么使用Benedikt Morbach的脚本会感到非常满意:tmpi

原始资料: https://github.com/moben/scripts/blob/master/tmpi

叉:https : //github.com/Azrael3000/tmpi

有了它,您可以同步所有多个面板(进程数)(每个命令同时复制到所有面板或进程上,因此与该xterm -e方法相比可以节省很多时间)。此外,您可以在想要执行的过程中知道变量的值print而不必移动到另一个面板,这将在每个面板上打印每个过程的变量值。

如果您不是tmux用户,我强烈建议您尝试一下。


2
由于tmpi确实很棒,而且正是我想要的东西,所以我在我的github帐户上进行了分叉:github.com/Azrael3000/tmpi,因为原始作者删除了它
Azrael3000 '18

6

http://github.com/jimktrains/pgdb/tree/master是我编写的用于执行此操作的实用程序。有一些文档,随时可以发问我。

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


谢谢!下次我在MPI工作时,我一定会检查一下。
杰伊·康罗德

5

使用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。然后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会话附加到每个进程gdb $MDRUN_EXE $PID。在屏幕会话中执行此操作可以轻松访问任何gdb会话。-d -m以分离模式启动屏幕,-S "P$RANK"允许您命名屏幕以便以后访问,并且-lbash选项以交互模式启动屏幕,并防止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在屏幕中启动,您就可以使用screen的-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会话,类似于上一节的代码。


3

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

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

帕德


3

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

在UNIX上,据说TotalView对MPI具有很好的支持。


2

我使用这个小的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

3
此解决方案类似于此处的项目符号6(open-mpi.org/faq/?category=debugging#serial-debuggers)。您可以通过添加来稍微改善代码gethostname(hostname, sizeof(hostname)); printf("PID %d on host %s ready for attach\n", getpid(), hostname);。然后,您通过键入rsh <hostname_from_print_statement>,最后是来附加到流程gdb --pid=<PID_from_print_statement>
杰夫

2

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

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

可以在这里找到有关mpi和gdb的简短讨论


2

非常简单的调试MPI程序的方法。

在main()函数中添加睡眠(some_seconds)

照常运行程序

$ mpirun -np <num_of_proc> <prog> <prog_args>

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

因此,您将有几秒钟的时间来通过ps查找您的进程,运行gdb并将其附加到它们。

如果您使用QtCreator之类的编辑器,则可以使用

调试->开始调试->附加到正在运行的应用程序

并在那里找到您的流程。


1

我使用日志跟踪进行了一些与MPI相关的调试,但是如果您使用的是mpich2,也可以运行gdb:MPICH2和gdb。通常,在处理从调试器启动时比较棘手的过程时,此技术通常是一种好习惯。


更改为另一个未中断的链接,添加了一些注释。
Jim Hunziker,2015年


0

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

SMPI为研究MPI应用程序提出了其他优势:透视(您可以观察系统的每个部分),可重复性(多次运行会导致完全相同的行为,除非您指定如此),没有heisenbug(因为模拟平台保持不同)从主机一个),等等。

有关更多信息,请参阅此演示文稿相关答案

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.