调试/获取追踪信息
本文档旨在通过提供跟踪和调试信息来帮助调试软件。这些信息随后可用于向(上游)软件开发者或包维护者报告 bug。
简介
通常,可执行文件会被剥离掉人类可读的上下文,以减小文件大小。在没有调试信息可用时获取的跟踪会大大降低其有用性。例如,来自 gdb 会话且调试信息不可用的跟踪可能看起来像这样:
[...] Backtrace was generated from '/usr/bin/epiphany' (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (no debugging symbols found) [Thread debugging using libthread_db enabled] [New Thread -1241265952 (LWP 12630)] (no debugging symbols found) 0xb7f25410 in __kernel_vsyscall () #0 0xb7f25410 in __kernel_vsyscall () #1 0xb741b45b in ?? () from /lib/libpthread.so.0 [...]
?? 显示了调试信息缺失的位置,以及调用该函数的库或可执行文件的名称。同样,当出现 (no debugging symbols found) 时,您应该查找指明的 filenames。
为了获得对程序开发者有用的正确跟踪,请遵循接下来的章节。大多数官方 Arch 包都提供了单独的调试文件,可以通过 Debuginfod 下载(参见 #获取跟踪)。当可执行文件中最初没有添加增强的调试信息时,就必须 重新构建包 并启用调试符号。
请使用完整的堆栈跟踪来告知开发者您发现的 bug。开发者们会非常感激,这将有助于改进您喜欢的程序。
获取跟踪
实际的回溯(或堆栈跟踪)可以通过 GNU Debugger gdb 获取。根据是启动程序的新实例、附加到现有进程,还是检查之前的崩溃,它有几种用法。
启动程序的新实例
使用可在 $PATH 中找到的可执行程序(或完整路径)启动 gdb。
$ gdb application
gdb 会自动尝试下载 官方仓库 中包的调试信息和符号。当 gdb 询问是否要在调试会话中启用 Debuginfod 时,请回答 y。
This GDB supports auto-downloading debuginfo from the following URLs: <https://debuginfod.archlinux.org> Enable debuginfod for this session? (y or [n]) y Debuginfod has been enabled. To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit. Downloading separate debug info for /usr/bin/application Reading symbols from /home/user/.cache/debuginfod_client/fbaee841e2ed2c11ecbbda26f39eeec1da23d6c3/debuginfo...
然后,在 gdb 中,键入 run,后跟您希望程序启动时使用的任何参数。
(gdb) run arguments
gdb --args application arguments... 在启动 gdb 时传递参数,然后在 gdb 中只使用不带参数的 run。例如,要调试用 Python 编写的应用程序,请运行 gdb --args /usr/bin/python /path/to/application。现在执行任何必要的步骤来触发 bug。当应用程序崩溃时,gdb 会自动暂停它并提示输入命令。在冻结或类似问题的情况下,按 Ctrl+c 也会返回到命令提示符。
然后启用日志记录到文件。
(gdb) set logging enabled on
gdb.txt。可以在 gdb 中使用 set logging file trace.log 指定备用文件名。最后,将回溯写入当前工作目录中指定的文件。
(gdb) thread apply all backtrace full
附加到现有进程
如果需要调试的程序已在运行,您需要先找到它的进程 ID。可以使用 pidof(1) 或 pgrep(1) 等工具。例如:
$ pidof python3
907171 491909
如果输出没有给出唯一的 ID,您可以尝试更多过滤或查看 ps aux 或 pstree --show-pids 的输出。
由于受限的 ptrace scope,作为普通用户附加默认不起作用。可以临时降低限制,使用 echo 0 > /proc/sys/kernel/yama/ptrace_scope,或者您可以以特权用户运行 gdb,例如使用 sudo。
启动 gdb 并将其附加到进程。
$ gdb --pid=PID
gdb 会询问是否在此调试会话中启用 Debuginfod,您应回答 y。
请注意,附加到进程会停止该进程,需要显式继续。这取代了 #启动程序的新实例 部分工作流程中的 run 命令。
(gdb) continue
现在执行任何必要的步骤来在附加的进程中触发 bug。然后,按照 #启动程序的新实例 中的方式启用日志记录并获取跟踪。
检查之前的崩溃
要调试已崩溃的应用程序,您需要针对其 core dump 调用 gdb。有关详细信息,请参阅 Core dump#Analyzing a core dump。
如果已崩溃程序的调试信息不可用且未获取正确的堆栈跟踪,您可能需要 重新构建包 并再次重现崩溃。
手动获取调试信息
首先要做的是获取需要 重新构建 或需要 安装调试包 的包的名称。
[...] Backtrace was generated from '/usr/bin/epiphany' (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". (no debugging symbols found) [...]
例如,对于上面跟踪中的摘录,可以使用 pacman 获取相关包的名称:
$ pacman -Qo /lib/libthread_db.so.1
/lib/libthread_db.so.1 is owned by glibc 2.5-8
该包在版本 2.5-8 中名为 glibc。对每个需要调试信息的包重复此步骤。
安装调试包
一些镜像目前在可访问的仓库中分发调试包。这些是由 Arch Linux 管理的赞助镜像,并有权访问调试仓库。
- https://geo.mirror.pkgbuild.com (GeoDNS 镜像)
要安装一个包,您可以直接从仓库安装。例如:
# pacman -U https://geo.mirror.pkgbuild.com/core-debug/os/x86_64/zstd-debug-1.5.2-2-x86_64.pkg.tar.zst
debug 镜像。另一个选择是将仓库添加到您的 pacman 配置中。
/etc/pacman.conf
# Testing Repositories [core-testing-debug] Include = /etc/pacman.d/mirrorlist [extra-testing-debug] Include = /etc/pacman.d/mirrorlist [multilib-testing-debug] Include = /etc/pacman.d/mirrorlist # Stable repositories [core-debug] Include = /etc/pacman.d/mirrorlist [extra-debug] Include = /etc/pacman.d/mirrorlist [multilib-debug] Include = /etc/pacman.d/mirrorlist
将带有调试包的镜像放在 mirrorlist 文件中的第一个位置。
/etc/pacman.d/mirrorlist
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch ...
重新构建包
如果调试信息未通过 debuginfod 提供(例如,当包来自 AUR 时),则可以从源代码重建。对于 官方仓库 中的包,请参阅 ABS,或对于 AUR 中的包,请参阅 AUR#Acquire build files。
要设置所需的 #编译选项,您可以修改 makepkg 配置,如果您只使用 makepkg 进行调试目的。在其他情况下,您应该仅为要重新构建的每个包修改包的 PKGBUILD 文件。
编译选项
从 pacman 4.1 开始,makepkg.conf(5) 在 DEBUG_CFLAGS 和 DEBUG_CXXFLAGS 中具有调试编译标志。要使用它们,请启用 debug makepkg 选项,并禁用 strip。
这些设置将强制使用调试符号进行编译,并禁用从可执行文件中剥离它们。
/etc/makepkg.conf
OPTIONS+=(debug !strip)
要将此设置应用于单个包,请修改 PKGBUILD。
PKGBUILD
options=(debug !strip)
或者,您可以将调试信息放在一个单独的包中,通过启用 debug 和 strip,调试符号将被从主包中剥离,并与源代码文件一起(以帮助在调试器中单步执行)放入一个单独的 pkgbase-debug 包中。如果包包含非常大的二进制文件(例如,包含调试符号时超过 1GB),这会很有优势,因为它可能会导致冻结和其他奇怪的不必要行为。
/usr/lib/debug/ 下,源代码文件安装在 /usr/src/debug 下。有关调试包的更多信息,请参阅 GDB 文档。glibc
某些包(如 glibc)会被剥离。检查 PKGBUILD 中的类似部分:
strip $STRIP_BINARIES usr/bin/{gencat,getconf,getent,iconv,iconvconfig} \
usr/bin/{ldconfig,locale,localedef,makedb} \
usr/bin/{pcprofiledump,pldd,rpcgen,sln,sprof} \
usr/lib/getconf/*
strip $STRIP_STATIC usr/lib/*.a
strip $STRIP_SHARED usr/lib/{libanl,libBrokenLocale,libcidn,libcrypt}-*.so \
usr/lib/libnss_{compat,db,dns,files,hesiod,nis,nisplus}-*.so \
usr/lib/{libdl,libm,libnsl,libresolv,librt,libutil}-*.so \
usr/lib/{libmemusage,libpcprofile,libSegFault}.so \
usr/lib/{audit,gconv}/*.so
并在适当的地方删除它们。
Clang
使用 Clang 作为编译器的包将无法使用 debug 选项进行构建,因为调试标志 -fvar-tracking-assignments 未被处理(例如,之前的 js78 PKGBUILD)。
将以下内容添加到 build() 函数的顶部,以仅删除受影响包的标志:
build() {
CFLAGS=${CFLAGS/-fvar-tracking-assignments}
CXXFLAGS=${CXXFLAGS/-fvar-tracking-assignments}
[...]
LTO
使用 链接时优化(LTO) 会在编译和调试时使用更多内存[1][2]。根据应用程序的不同,特别是如果它是像 Firefox 或 Qt 这样的大型应用程序,可能会超出可用内存。如果发生这种情况,请在没有 LTO 的情况下构建应用程序。
官方仓库中的所有包通常都使用 LTO 构建。
构建和安装包
在 PKGBUILD 的目录中,使用 makepkg 从源代码构建包。这可能需要一些时间。
$ makepkg
然后安装构建好的包。
# pacman -U glibc-2.26-1-x86_64.pkg.tar.gz