7.5 调试与断点

P360

单主机调试的方法,重点放在并行调试上

HPC中bug的特点,调试困难的原因

追溯高性能计算机上并行应用程序执行中异常现象的起因远比调试串行应用程序困难。
在高性能计算机上调试一个应用程序常常需要相当仔细地观察超级计算机的软件和硬件栈以期恰当地诊断异常。

高性能计算(HPC)从业者在程序运行中经常碰到异常现象, 而异常的起因则多种多样,其中包括硬件故障、程序编写错误、软件技术错误,甚至在极端情况下是因为宇宙射线翻转一位而干扰了计算。即使用简单的桌面计算机或笔记本电脑执行运算, 也很难追踪 这些程序执行异常的起因。基于 HPC 资源,解决一段程序中此类异常的难度可能成倍增加,因为超级计算机的多层网络、内存、库组件以及不同执行形式之间复杂的相互作用。 本章会介绍几种调试 HPC 程序的技术和工具, 并探索从业者常见的几种 bug 类型, 其中包 括死锁、竞争、内存泄漏、段错误、非法引用等。
历史上调试一词常与葛丽丝·霍普联系起来, 1947 年她在使用 Harvard Mark II 机电式 计算机工作时发现一只飞蛾干扰了计算机的工作。这只飞蛾后来被放在工作组的日志本中 并加了一条说明 “首个被发现的真正 bug”, 如图 14-1 所示。另一个类似的故事稍早于葛丽 丝·霍普的经历, 数学家诺伯特 维纳在二战期间奉命诊断一艘战舰的枪械自动火控的异 常行为。在听取了关于某个枪口位置短路的详细描述后, 他正确预测了在该设备的短路位 置有一只死老鼠 [1]{ }^{[1]}
和这些著名的文字上的调试案例有所不同,在高性能计算机中调试一段程序常常需 要非常仔细地审阅超级计算机的软件和硬件栈以期恰当地诊断异常现象。现在有相当丰富 的工具可以辅助诊断问题。本章首先会介绍 GNU 调试器 (GDB) 和 Valgrind 测量框架的 使用, 并涉及一些更流行的商业版调试工具。之后会使用这些工具来探索在消息传递接口 (MPI) 和 OpenMPI 代码中被发现的一系列常见 bug。章节末尾会列举常见编译器选项和消 息清单, 这对调试程序非常有帮助, 还提供了可用的系统监视调试方法清单。

调试工具

调试一段代码的复杂性催生了多种开源和私有工具, 以辅助程序员单步执行程序和放 置断点使得程序执行可以在那里被暂停并查看内存。大部分常见的开源调试工具天然串行 的; 而在后续章节会展示如何将其用于并行调试。也有一些针对高性能计算机推出的商业 版调试器, 它们可以并行执行。此类调试器一般由系统管理员开放给超算用户, 然而每个 节点的授权花费可能会限制某个商业版调试器的使用规模。本节介绍了两种开源且自由使 用的调试工具, 即 GDB 和 Valgrind 测量框架, 并给出可用的更常见的商业版并行调试器的 了集中的一些信息。

1. 开源调试器:GDB

GDB 提供了调试代码的多种工具, 还允许用户单步调试代码和调用栈, 以及观察 变量和它们的值变化。
GDB 还提供了多线程代码调试支持。

GDB 是最常见的可用开源调试器之一, 某个商业版调试器 (Allinea DDT [2]^{[2]} ) 甚至还用 GDB 作为它的引擎。GDB 是一种在 Linux 和 UNIX 系统中使用的命令调用的命令行调试器。 gdb<\mathrm{gdb}< 可执行文件名称 >>, 尖括号部分用待调试的可执行文件名来替换。本节会探索 GDB 的一小部分但却很重要的功能子集, 以用在本章后面的调试示例上。为了帮助阐明 GDB 命令和用例, 用到了图 14-2 所示的代码。当对某个可执行文件运行 GDB 时, 让编译 器知道该可执行文件将会用于调试这点很重要。编译时加上 “ g-\mathrm{g} ” 标志就可以做到这一点。

断点:(GDB 中最有用的命令之一)断点是一段代码执行中的中断, 允许用户测试这时的程序状态。有几种在 GDB 中设置断点的方式, 包括指定函数名、行号、文件名 以及行号, 指定条件语句, 甚至内存地址。

 (gdb) info breakpoints  Num  Type  Disp EnbAddress What 1 breakpoint  keep y 0x0000000000400450< printf@apt > 2 breakpoint  keep y  0x00000000004005efinmainatdotprod_serial.c:173 breakpoint  keep y  0x00000000004005ef inmainatdotprod_serial.c:174 breakpoint  keep y  0x00000000004005cfinmainatdotprod_serial.c:16\begin{array}{llll}\text { (gdb) info breakpoints } & & & \\ \text { Num } & \text { Type } & \text { Disp Enb} &{Address } & \text { What } \\ 1 & \text { breakpoint } & \text { keep y } & 0x0000000000400450 & <\text { printf@apt > } \\ 2 & \text { breakpoint } & \text { keep y } & \text { 0x00000000004005ef}&{ in main at dotprod\_serial.c:17 } \\ 3 & \text { breakpoint } & \text { keep y } & \text { 0x00000000004005ef }&{in main at dotprod\_serial.c:17 } \\ 4 & \text { breakpoint } & \text { keep y } & \text { 0x00000000004005cf}&{ in main at dotprod\_serial.c:16 }\end{array}

反向跟踪:当在调试器中暂停执行时, 可用 back trace 命令展示导向执行中当前点的调用者概览。 由于图 14-2 中的示例只有一次调用 ( main), 因此使用此示例的任何反向跟踪将只会给出一 层框架, 或者一个调用栈成员。

多线程调试:对于多线程应用 (比如 OpenMP), GDB 允许线程之间切换上下文并将调试器命令应 用到所有线程上。info threads 命令将会列出带有线程标识符的线程清单。调试器可以在 thread 命令后接线程标识符来切换线程。

https://cloud.tencent.com/developer/article/1662935

2. 商业版并行调试器

几种开源和商业版调试工具和套件被开发出来辅助进程调试。有几种商业版并行调试器支持 MPI 和 OpenMP 代码。
有几种开源串行调试器和工具套件可用于调试 MPI 和 OpenMP 代码。在 MPI 案例 中, 它们可能需要将几个串行调试器附着到某个模拟器上。
多个串行调试器可以附着到一个 MPI 执行上来执行并行调试。

很多商业版并行调试器为多种编程模型和硬件架构提供了对 C、C++ 以及基于 Fortran 代码的调试支持, 包括通用图形处理单元和纵核架构。表 14-4 列举了更广泛使用的可用的 并行商业版调试器。

 商业版调试器  值得注意的能力  TotalView [4] 支持 OpenMP、MPI、OpenACC、CUDA  Allinea DDT [2] 支持 OpenMP、Pthreads、MPI、CUDA  Intel Parallel Debugger [5] 支持多核调试 \begin{array}{c|c} \hline {\text { 商业版调试器 }} & {\text { 值得注意的能力 }} \\ \hline \text { TotalView }^{[4]} & \text { 支持 OpenMP、MPI、OpenACC、CUDA } \\ \hline \text { Allinea DDT }^{[2]} & \text { 支持 OpenMP、Pthreads、MPI、CUDA } \\ \hline \text { Intel Parallel Debugger }^{[5]} & \text { 支持多核调试 } \\ \hline \end{array}

GIF 2021-11-27 21-05-10

表 14-1 中的每个调试器都有图形化用户界面 (GUI) 来检测并行执行中每个处理器或 线程的状态。有几种调试器提供了对内存泄漏和其他内存错误的检测。目前调试器提供 的重放能力也很常见, 这通过借由记录程序全部的执行状态用于后续的重放来实现。这 个功能在调试 Heisenbug 的时候特别有用, 这种 bug 会在尝试捕获它们的时候消失。针对 TotalView 的启动选项包括一个重放功能和内存调试, 如图 14-14 所示。可以查看并在每个 处理器或线程之间切换整个程序状态, 如图 14-15 所示的 TotalView。

调试系统监控器

系统监控器提供了单独的方式, 以检测程序执行并将其与程序员的预期进行匹配。

不少集群采用监控软件来探测节点硬件状态,并获取当前执行中工作负载的相关信息。 前者可能简单到只需验证节点对远程命令的响应,但也可能包括关键部件的温度测量(温 度通常随负载一起上升), 甚至访问检测硬件的其他物理特性 (供电电压、风扇转速等) 的 底层内置传感器。后者首先考虑的是可用中央处理单元 ( CPU) 的使用率 (见图 14-27), 但也可能提供其他重要的统计数据, 例如工作负载在用户态和核心态的执行比例、已用和可 用内存的数量、输人/输出操作传输的数据量、网络流量等级、磁盘可用空间,以及其他数 据。监控依赖在每个节点后端运行并按照一定间隔 (例如每隔一分钟) 采样收集所需信息的 轻量级守护进程。这些信息被聚合到一个专用服务器上并且通过公共访问接口 (比如网页) 对用户开放。常用的系统监控器有 Nagios [6]{ }^{[6]} 和 Ganglia [7]{ }^{[7]}

GIF 2021-11-27 20-29-24

好的调试方法就是不需要调试?单步+日志+断言等等任何调试手段.

[1] F. Conway, J. Siegelman, Dark Hero of the Information Age: In Search of Norbert Wiener, The Father of Cybernetics, s.l., Basic Books, 2006.2006 .
[2] Allinea, Allinea DDT, [Online]. http://www.allinea.com/products/ddt.
[3] Valgrind Tool Suite, [Online]. http://valgrind.org,
[4] RogueWave Software, TotalView for HPC, 2016 [Online]. http://www.roguewave.com/products-services/totalview.
[5] Intel, Intel Parallel Debugger Extension, [Online]. https://software.intel.com/en-us/articles/parallel-debuggerextension.
[6] Nagios: The Industry Standard in IT Infrastructure Monitoring, [Online]. https://www.nagios.org.
[7] Ganglia Monitoring System, [Online]. http://ganglia.info/.