调试
本页面主要介绍如何收集有关错误报告的更多信息。尽管使用了“debug”(调试)一词,但它并非旨在指导如何在开发过程中调试程序。
检查核心转储可用性
核心转储(core dump)是一个文件,它包含了进程意外终止时进程的地址空间(内存)。如果应用程序以易于调试的方式编译,则“core”文件可用于找出问题所在。
核心转储的位置可能因操作系统配置而异。请参阅 core dump 以了解您的系统是否启用了核心转储文件的生成以及它们去了哪里。
段错误
有几种技术可用于弄清楚哪里出了问题。请戴上您的侦探帽。
GDB
gdb 是一个古老且经过充分测试的应用程序,用于调试应用程序。有关如何使用它获取跟踪信息的更多说明,请参阅 Debugging/Getting traces#Getting the trace。在从 gdb 运行时,您可能需要等待段错误发生。之后,将跟踪信息发布到 粘贴服务,并在错误报告中包含 URL。
如果您有一个“core”文件,可以将其与 GDB 配合使用以获取回溯信息 (backtrace)
$ 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 值等。如果应用程序慢得无法忍受,尝试不同的组合是值得的。一个极端的例子是,使用 GCC 9 和 ASan 编译的 Cataclysm: Dark Days Ahead 加载一个简单的存档需要 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 文件)的名称。然后,您可以在 shell 脚本中,在可执行文件名称之前,临时加上“gdb”以进行调试。请参阅上方关于 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 build system 完成,以了解其打包方式。有关更多信息,请参阅 Bug reporting guidelines#Upstream or Arch?。
如果错误与 Arch Linux 无关,并且可以在其他地方重现,则仅向上游报告。Arch Linux 无法神奇地修复上游错误。向 Arch 错误跟踪器报告它无济于事,甚至可能适得其反,因为它倾向于浪费错误管理员的时间。