调试
本页面主要介绍如何收集与错误报告相关的更多信息。即使使用了“调试”一词,它也不是作为开发时如何调试程序的指南。
检查核心转储的可用性
核心转储是一个文件,其中包含进程意外终止时的进程地址空间(内存)。如果应用程序以调试友好的方式编译,则可以使用“core”文件来找出哪里出了问题。
核心转储的位置可能因操作系统配置而异。请参阅 core dump 以查找是否在您的系统上启用了核心转储文件的生成以及它们的去向。
段错误
有几种技术可以用来找出哪里出了问题。戴上你的侦探帽。
Gdb
gdb 是一个古老且经过充分测试的应用程序调试工具。请参阅 Debugging/获取追踪#获取追踪 以获取有关如何使用它来获取追踪的更多说明。从 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 软件包指南/安全。
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 编译的版本无法重现该错误。因此,记住编译器版本也很重要。
要查找内存错误,只需正常运行应用程序即可。ASan 会在发生堆溢出甚至释放后使用等情况时自动崩溃应用程序。发生这种情况时,可以在输出中找到详细且有用的追踪。ASan 的行为可以在运行时通过 ASAN_OPTIONS
环境变量 来影响。此外,还有一些编译标志可以改变其行为。
此环境变量的常见用途是告诉 ASan 当它发现内存泄漏以外的东西时不要致命地崩溃应用程序
ASAN_OPTIONS=halt_on_error=0
更多信息可以在以下位置找到:
- https://github.com/google/sanitizers/wiki/AddressSanitizer
- https://github.com/google/sanitizers/wiki/AddressSanitizerFlags
- https://clang.llvm.net.cn/docs/AddressSanitizer.html
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
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_name
或 bash -xv script_name
。
对于 Python 应用程序,输出通常会说明崩溃发生的文件和行号。如果您精通 Python,您可以尝试修复此问题并将修复包含在错误报告中。
报告错误
首先检查所讨论的错误是否是打包错误。如果错误是由于 Arch Linux 如何打包此应用程序而引入的,请将其报告给 https://gitlab.archlinux.org/groups/archlinux/packaging/-/issues。这也包括库或依赖项的问题(例如,如果其中一个库或依赖项没有使用所需的特定功能构建)。检查软件包的 PKGBUILD,这可以通过 Arch 构建系统 来完成,以查看它是如何打包的。有关更多信息,请参阅 错误报告指南#上游还是 Arch?。
如果该错误与 Arch Linux 无关,并且可以在其他任何地方重现,则仅将其报告给上游。Arch Linux 无法神奇地修复上游错误。将其报告给 Arch 错误跟踪器无济于事,甚至可能适得其反,因为它往往会浪费错误处理人员的时间。