3.4. 调试 Crashed 应用程序
有时,无法直接调试应用程序。在这些情况下,您可以在其终止时收集有关应用程序的信息,并在其终止时对其进行分析。
3.4.1. 核心转储:它们是什么以及如何使用它们
内核转储(core dump)是应用程序在应用程序停止工作时的一部分的副本,它以 ELF 格式存储。它包含所有应用的内部变量和堆栈,可启用检查应用程序的最终状态。当遵守相应的可执行文件和调试信息时,可以通过调试器来分析核心转储文件,类似于分析正在运行的程序。
如果启用了此功能,Linux 操作系统内核可以自动记录核心转储。或者,您可以向任何正在运行的应用程序发送信号来生成内核转储,而不考虑其实际状态。
有些限制可能会影响生成内核转储的功能。查看当前的限制:
$ ulimit -a
3.4.2. 使用内核转储记录应用程序崩溃
要记录应用程序崩溃,请设置内核转储保存并添加系统的信息。
步骤
要启用内核转储,请确保
/etc/systemd/system.conf
文件包含以下行:DumpCore=yes DefaultLimitCORE=infinity
您还可以添加注释,说明之前是否存在这些设置,以及前面的值是什么。如果需要,这将使您稍后撤销这些更改。注释是以
#
字符开头的行。更改该文件需要管理员级别访问权限。
应用新配置:
# systemctl daemon-reexec
删除内核转储大小的限制:
# ulimit -c unlimited
要反向更改,请使用值
0
而不是unlimited
来运行 命令。安装提供
sosreport
工具来收集系统信息的sos
软件包:# dnf install sos
-
当应用程序崩溃时,会生成核心转储,并由
systemd-coredump
处理。 创建 SOS 报告以提供系统的附加信息:
# sosreport
这会创建一个
.tar
存档包含您的系统信息,如配置文件副本。查找并导出内核转储:
$ coredumpctl list executable-name $ coredumpctl dump executable-name > /path/to/file-for-export
如果应用程序多次崩溃,则第一个命令的输出列出了更多捕获的内核转储。在这种情况下,使用其他信息为第二个命令构建更精确的查询。详情请查看 coredumpctl(1) 手册页。
将内核转储和 SOS 报告传送到进行调试的计算机。另外,也传输可执行文件(如果该文件已知)。
重要当可执行文件未知时,然后对核心文件进行后续分析会标识它。
- 可选:传输后删除内核转储和 SOS 报告,以释放磁盘空间。
其他资源
- 文档 配置基本系统设置 中的 管理 systemd
- 当应用程序崩溃或分段错误时,如何启用核心文件转储 - 知识库文章
- 什么是 sosreport 以及如何在 Red Hat Enterprise Linux 4.6 及之后的版本中创建? - 一个知识库文章
3.4.3. 使用内核转储检查应用程序崩溃状态
先决条件
- 您必须有一个内核转储文件,以及发生崩溃的系统的 sosreport。
- 在您的系统中必须安装 GDB 和 elfutils。
步骤
要识别发生崩溃的可执行文件,请运行带内核转储文件的
eu-unstrip
命令:$ eu-unstrip -n --core=./core.9814 0x400000+0x207000 2818b2009547f780a5639c904cded443e564973e@0x400284 /usr/bin/sleep /usr/lib/debug/bin/sleep.debug [exe] 0x7fff26fff000+0x1000 1e2a683b7d877576970e4275d41a6aaec280795e@0x7fff26fff340 . - linux-vdso.so.1 0x35e7e00000+0x3b6000 374add1ead31ccb449779bc7ee7877de3377e5ad@0x35e7e00280 /usr/lib64/libc-2.14.90.so /usr/lib/debug/lib64/libc-2.14.90.so.debug libc.so.6 0x35e7a00000+0x224000 3ed9e61c2b7e707ce244816335776afa2ad0307d@0x35e7a001d8 /usr/lib64/ld-2.14.90.so /usr/lib/debug/lib64/ld-2.14.90.so.debug ld-linux-x86-64.so.2
输出中包含一行中每个模块的详细信息,用空格分开。该信息按以下顺序列出:
- 模块映射的内存地址
- 模块的 build-id 以及它在找到的内存中的位置
-
模块的可执行文件名称,如果未知显示为
-
,如果模块没有从文件中加载则显示为.
。 -
调试信息的来源,在可用时显示为一个文件名,当包括在可执行文件本身中时显示为
.
,或在不存在时显示-
-
主模块的共享库名称(soname)或
[exe]
在本例中,重要的细节是文件名
/usr/bin/sleep
以及在包括文本[exe]
的行中的 build-id2818b2009547f780a5639c904cded443e564973e
。使用这些信息,您可以识别分析内核转储所需的可执行文件。获取崩溃的可执行文件。
- 如果可能,在发生崩溃的系统中复制它。使用从核心文件中提取的文件名。
您还可以在系统中使用相同的可执行文件。Red Hat Enterprise Linux 上构建的每个可执行文件都包含带有唯一 build-id 值的备注。确定相关本地可用可执行文件的构建 ID:
$ eu-readelf -n executable_file
使用此信息将远程系统中的可执行文件与您的本地副本匹配。内核转储中列出的本地文件和 build-id 的 build-id 必须匹配。
-
最后,如果应用程序从 RPM 软件包安装,您可以从软件包中获取可执行文件。使用
sosreport
输出来查找所需软件包的确切版本。
- 获取可执行文件使用的共享库。对可执行文件使用的步骤相同。
- 如果应用程序以软件包的形式发布,在 GDB 中加载可执行文件,以显示缺少 debuginfo 软件包的提示。如需了解更多详细信息,请参阅 第 3.1.4 节 “使用 GDB 获取应用程序或库的 debuginfo 软件包”。
要详细检查核心文件,使用 GDB 加载可执行文件和内核转储文件:
$ gdb -e executable_file -c core_file
有关缺失的文件和调试信息的进一步消息可帮助您识别调试会话中缺少什么。如果需要,返回到上一步。
如果应用程序的调试信息可作为文件而不是软件包提供,使用
symbol-file
命令在 GDB 中载入该文件:(gdb) symbol-file program.debug
使用实际文件名替换 program.debug。
注意对于核心转储中包含的所有可执行文件,可能不需要安装调试信息。其中大多数可执行文件都是应用程序代码使用的库。这些库可能不会直接为问题贡献,您不需要为它们包含调试信息。
使用 GDB 命令在应用程序崩溃时检查应用程序状态。请参阅使用 GDB 检查应用程序内部状态。
注意在分析内核文件时,GDB 不会附加到正在运行的进程。用于控制执行的命令无效。
其他资源
- 使用 GDB 进行调试 - 2.1.1 Choosing Files
- 使用 GDB 进行调试 — 18.1 Commands to Specify Files
- 使用 GDB 进行调试 — 18.3 Debugging Information in Separate Files
3.4.4. 使用 coredumpctl 创建和访问内核转储
systemd
的 coredumpctl
工具可显著简化崩溃发生的机器中的核心转储。此流程概述了如何捕获无响应进程的内核转储。
先决条件
该系统必须配置为使用
systemd-coredump
进行内核转储处理。验证这一点是否正确:$ sysctl kernel.core_pattern
如果输出以以下内容开头,则配置是正确的:
kernel.core_pattern = |/usr/lib/systemd/systemd-coredump
步骤
根据可执行文件名称的已知部分,查找挂起进程的 PID:
$ pgrep -a executable-name-fragment
这个命令会以以下形式输出一行
PID command-line
使用 命令行 值来验证 PID 属于预期进程。
例如:
$ pgrep -a bc 5459 bc
向进程发送中止信号:
# kill -ABRT PID
验证
coredumpctl
是否捕获了内核:$ coredumpctl list PID
例如:
$ coredumpctl list 5459 TIME PID UID GID SIG COREFILE EXE Thu 2019-11-07 15:14:46 CET 5459 1000 1000 6 present /usr/bin/bc
可以根据需要进一步检查或使用核心文件。
您可以根据 PID 和其他值指定内核转储。详情请查看 coredumpctl(1) 手册页。
显示核心文件的详情:
$ coredumpctl info PID
要在 GDB 调试器中载入核心文件:
$ coredumpctl debug PID
根据调试信息的可用性,GDB 将会建议运行命令,例如:
Missing separate debuginfos, use: dnf debuginfo-install bc-1.07.1-5.el8.x86_64
有关此过程的详情,请参阅使用 GDB 获取应用程序或库的 debuginfo 软件包。
导出核心文件以便进一步处理其他位置:
$ coredumpctl dump PID > /path/to/file_for_export
使用您要放置内核转储的文件替换 /path/to/file_for_export。
3.4.5. 使用 gcore
转储进程内存
核心转储调试的工作流可以离线分析程序的状态。在某些情况下,您可以将此工作流与仍然运行的程序搭配使用,比如很难通过进程访问环境。您可以使用 gcore
命令在仍在运行时转储任何进程的内存。
先决条件
- 您必须了解什么是核心转储,以及如何创建它们。
- 在系统中必须安装 GDB。
步骤
查找进程 ID(pid)。使用
ps
、pgrep
和top
等工具:$ ps -C some-program
转储这个进程的内存:
$ gcore -o filename pid
这会创建一个文件 filename 并转储进程内存。转储内存时,进程的执行将停止。
- 在内核转储完成后,进程会恢复正常执行。
创建 SOS 报告以提供系统的附加信息:
# sosreport
这会创建一个 tar 存档,其中包含您的系统信息,如配置文件的副本。
- 将程序的可执行文件、内核转储和 SOS 报告传送到进行调试的计算机。
- 可选:传输后删除内核转储和 SOS 报告,以释放磁盘空间。
其他资源
- 如何在不重启应用程序的情况下获取核心文件? - 知识库文章
3.4.6. 使用 GDB 转储受保护的进程内存
您可以将进程内存标记为不转储。这可节省资源并确保进程内存包含敏感数据时的其他安全:例如,在银行或核算应用程序或整个虚拟机上。内核内核转储(kdump
)和手动内核转储(gcore
、GDB)都不会转储标记为这种方式的内存。
在某些情况下,无论这些保护是什么,您必须转储进程内存的整个内容。此流程演示了如何使用 GDB 调试器进行此操作。
先决条件
- 您必须了解什么是核心转储。
- 在系统中必须安装 GDB。
- GDB 必须已附加到带有受保护内存的进程。
步骤
将 GDB 设置为忽略
/proc/PID/coredump_filter
文件中的设置:(gdb) set use-coredump-filter off
将 GDB 设置为忽略内存页标记
VM_DONTDUMP
:(gdb) set dump-excluded-mappings on
转储内存:
(gdb) gcore core-file
使用您要转储内存的文件名替换 core-file。