调试

来自 ArchWiki
(重定向自 Step-by-step debugging guide

此条目或章节需要扩充。

原因: 本条目也可能关于通用调试,以便可以在此处添加其他有用的工具,例如 ltrace。(在 Talk:Debugging 中讨论)

本页面主要介绍如何收集更多与错误报告相关的信息。即使使用了“调试”一词,它也并非旨在作为开发程序时如何调试程序的指南。

检查核心转储的可用性

核心转储是一个文件,其中包含进程意外终止时的进程地址空间(内存)。如果应用程序以调试友好的方式编译,则可以使用“core”文件来找出问题所在。

核心转储的位置可能因操作系统配置而异。请参阅 core dump 以查找您的系统上是否启用了核心转储文件的生成以及它们的去向。

段错误

有几种技术可用于找出问题所在。戴上您的侦探帽。

Gdb

gdb 是一个历史悠久且经过良好测试的应用程序,用于调试应用程序。有关如何使用它来获取追踪信息的更多说明,请参阅 Debugging/Getting traces#Getting the trace。从 gdb 运行时,您可能需要等待段错误。之后,将追踪信息发布到 pastebin 服务,并将 URL 包含在您的错误报告中。

如果您有一个“core”文件,则可以将其与 gdb 一起使用以获取回溯

$ gdb appname core
bt full

Valgrind

假设您有一个未剥离的二进制文件,没有内联函数,通常最好也通过 valgrind 运行该程序。valgrind 是一个模拟 CPU 的工具,通常会显示问题出在哪里,或者提供 gdb 之外的额外信息。

$ valgrind appname

如果发生崩溃,它将提供大量有用的调试输出。考虑使用 -v--leak-check=full 以获得更多信息。

或者,使用

$ valgrind --tool=callgrind appname

并将输出通过 kcachegrind 运行,以图形方式浏览程序使用的函数。如果程序挂起,这可以更容易地查明错误的位置。

内存错误

在某些情况下,可能有必要找出应用程序是否正确处理其内存。这仅影响以 内存不安全 语言编写的应用程序。例如,某些崩溃可能是由内存错误(例如堆溢出)引起的。

请记住,Arch Linux 上的软件包在编译时会带有额外的标志以加强应用程序的安全性,这可能会影响内存错误。请参阅 Arch package guidelines/Security

AddressSanitizer

为了使 ASan 工作,应用程序必须使用 -f sanitize=address 和调试符号进行编译。

启用 ASan 后,编译后的应用程序会变慢,但这在很大程度上取决于软件本身、使用的编译器(包括其版本)和使用的 -O 值等因素。如果应用程序慢得无法忍受,则值得尝试不同的组合。一个极端的例子是 Cataclysm: Dark Days Ahead 在启用 GCC 9 和 ASan 的情况下加载一个简单的存档需要 60 分钟。在没有 ASan 的情况下,存档加载时间不到一分钟。GCC 14 将启用 ASan 后的加载时间缩短了一半,但仍然保持在 30 分钟,这是无法接受的慢。Clang 18 与 ASan 没有此问题,并且速度下降可忽略不计。但是,强制 GCC 14 使用 -O3 与 ASan 大大加快了加载速度,但仍然需要一分钟才能加载,并且不如 clang 快。

另一个复杂之处在于,只有 GCC 9 能够触发特定的错误,即堆溢出。使用 GCC 14 编译的版本无法重现该错误。因此,记住编译器版本也很重要。

注意: 避免在调试器(例如 gdb)中运行使用 ASan 编译的应用程序。在调试器中运行它会限制 ASan。ASan 单独发出的追踪信息比调试器生成的常规回溯信息详细得多。

要查找内存错误,只需正常运行应用程序即可。ASan 会在发生堆溢出甚至释放后使用等情况时自动崩溃应用程序。发生这种情况时,可以在输出中找到详细且有用的追踪信息。ASan 的行为可以在运行时通过 ASAN_OPTIONS 环境变量 来影响。此外,还有一些编译标志可以更改其行为。

此环境变量的常见用途是告诉 ASan 在发现内存泄漏以外的其他问题时不要致命地崩溃应用程序

ASAN_OPTIONS=halt_on_error=0

更多信息可以在以下位置找到

Valgrind

Valgrind 也可用于检测这些行为,并且与 ASan 相比,它不需要编译到程序中。与 ASan 相比,它速度要慢得多,并且功能也受到更多限制。

为了查找内存错误,请使用以下命令调用 Valgrind

$ valgrind --tool=memcheck --track-origins=yes --keep-stacktraces=alloc-and-free application

另请参阅 #Valgrind

缺少文件或库

Strace

strace 详细了解应用程序实际在做什么。如果应用程序尝试打开一个不存在的文件,则可以通过 strace 发现。

要查找名为 appname 的程序尝试打开哪些文件

$ strace -eopen appname
提示: 如果您希望 grep strace 的输出,可以尝试: strace -o /dev/stdout appname | grep string

LD_DEBUG

设置 LD_DEBUG=files 可以提供应用程序正在查找哪些文件的另一个概览。对于名为 appname 的应用程序

$ LD_DEBUG=files appname > appname.log 2>&1

输出将最终出现在 appname.log 中。

有关更多信息,请参阅 ld-linux(8)

Readelf

如果您在运行应用程序时收到 no such file or directory,请尝试以下命令

$ readelf -a /usr/bin/appname | grep interp

(将 /usr/bin/appname 替换为您的可执行文件的位置)

确保相关的解释器(例如 /lib/ld-linux-x86-64.so.2)实际存在。如果需要,请安装 ld-lsb

不是二进制文件

在可执行文件上使用 file 以获取更多信息

$ file /usr/bin/appname

如果它显示 ELF,则它是二进制可执行文件。如果它显示 Python script,您就知道您正在处理用 Python 编写的应用程序。

如果是 shell 脚本,请在文本编辑器中打开 shell 脚本,并查看(通常在文件底部)是否可以找到真实应用程序(ELF 文件)的名称。然后,您可以为了调试目的,暂时将 “gdb” 放在 shell 脚本中,放在可执行文件名称之前。请参阅上面关于 gdb 的章节。如果相关的可执行文件也需要参数,请在命令前加上 gdb --args

对于纯 shell 脚本,您也可以使用 bash -x script_namebash -xv script_name

对于 Python 应用程序,输出通常会说明崩溃发生的文件和行号。如果您精通 Python,您可以尝试修复此问题,并将修复程序包含在错误报告中。

报告错误

首先检查所讨论的错误是否是打包错误。如果错误是由于 Arch Linux 如何打包此应用程序而引入的,请将其报告给 https://gitlab.archlinux.org/groups/archlinux/packaging/-/issues。这也包括库或依赖项的问题(例如,如果其中一个库或依赖项没有使用所需的功能构建)。检查软件包的 PKGBUILD,这可以通过 Arch 构建系统 完成,以查看它是如何打包的。有关更多信息,请参阅 Bug reporting guidelines#Upstream or Arch?

如果该错误与 Arch Linux 无关,并且可以在任何其他地方重现,则仅向上游报告。Arch Linux 无法神奇地修复上游错误。向上游错误跟踪器报告它不会有任何帮助,甚至可能适得其反,因为它往往会浪费错误处理人员的时间。

参见