143

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

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

4

17 回答 17

95

我发现 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] [...]
于 2010-03-02T16:35:47.030 回答
64

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

OpenMPI 站点有一个关于 MPI 调试的很好的常见问题解答。FAQ 中的第 6 项描述了如何将 GDB 附加到 MPI 进程。阅读全文,有一些很棒的提示。

但是,如果您发现要跟踪的进程太多,请查看堆栈跟踪分析工具 (STAT)。我们在 Livermore 使用它从潜在的数十万个正在运行的进程中收集堆栈跟踪,并将它们智能地呈现给用户。它不是功能齐全的调试器(功能齐全的调试器永远不会扩展到 208k 内核),但它会告诉您哪些进程组正在做同样的事情。然后,您可以在标准调试器中逐步检查每个组的代表。

于 2008-12-12T19:12:57.243 回答
32

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

mpiexec -n X gdb ./a.out

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

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

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

于 2013-07-26T12:51:11.127 回答
18

正如其他人所提到的,如果您只使用少数MPI 进程,您可以尝试使用多个 gdb 会话、redoubtable valgrind或推出您自己的 printf / 日志记录解决方案。

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

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

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

...等等。如果您使用过 Eclipse 或 Visual Studio,那么您就对了。

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

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

  • 您还可以在过程和时间上跟踪和过滤变量和表达式的值: 跟踪点随时间记录值

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

界面非常漂亮;作为 Oak Ridge 的 Jaguar 集群验收测试的一部分,我们在 0.1 秒内对 220,000 个进程的堆栈和变量进行了计时和合并。

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

于 2012-05-31T11:43:54.533 回答
8

如果你是用户,你会觉得使用Benedikt Morbachtmux的脚本很舒服:tmpi

原始来源:https://github.com/moben/scripts/blob/master/tmpi

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

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

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

于 2017-06-23T08:49:44.073 回答
7

http://valgrind.org/ nuf 说


更具体的链接:使用 Valgrind 调试 MPI 并行程序

于 2010-03-09T07:03:03.290 回答
6

http://github.com/jimktrains/pgdb/tree/master是我写的一个实用程序来做这件事。有一些文档,有问题可以私信我。

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

于 2009-04-08T23:40:41.273 回答
5

使用screenwithgdb调试 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

-X stuff一旦 gdb 在屏幕中启动,您可以使用 screen 的命令将脚本输入到屏幕(这样您就不必输入每个屏幕并键入相同的内容) 。命令末尾需要换行符。这里的屏幕是通过-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 会话。

于 2014-06-29T22:06:45.737 回答
3

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

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

于 2008-12-04T06:43:45.167 回答
3

我使用这个自制的小方法将调试器附加到 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);
}

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

于 2009-05-09T14:44:39.543 回答
3

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

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

数据库

于 2009-12-03T20:58:25.437 回答
2

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

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

可以在这里找到对 mpi 和 gdb 的简要讨论

于 2011-02-28T17:14:44.390 回答
2

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

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

像往常一样运行程序

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

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

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

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

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

并在那里找到您的流程。

于 2018-09-04T19:57:50.050 回答
1

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

于 2009-06-03T23:22:30.480 回答
1

mpirun -gdb

感谢http://www.ncsa.illinois.edu/UserInfo/Resources/Hardware/CommonDoc/mpich2_gdb.html存档链接

于 2012-06-01T13:11:05.623 回答
0

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

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

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

于 2015-10-31T14:36:34.030 回答
0

您可以使用免费且比xterm. 您复制 VS Code 窗口并将调试器手动附加到每个进程。请参阅以下视频中的说明:

YouTube

于 2020-12-02T10:43:29.017 回答