调试/获取追踪信息
本文旨在通过提供追踪信息和调试信息来帮助调试软件。这些信息随后可用于向(上游)软件开发人员或软件包维护者提交错误报告。
简介
通常,可执行文件会去除人类可读的上下文以使其更小。在没有调试信息的情况下获取追踪信息会大大降低其有用性。例如,来自 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)
时,您应该查找声明的文件名。
要获得对程序开发人员有用的正确追踪信息,请按照以下章节操作。大多数官方 Arch 软件包都有单独的调试文件,可以使用 Debuginfod 下载(请参阅 #获取追踪信息)。如果最初没有将增强的调试信息添加到可执行文件中,则必须重新构建软件包并启用调试符号。
使用完整的堆栈追踪信息来告知开发人员您发现的错误。这将受到他们的赞赏,并将有助于改进您最喜欢的程序。
获取追踪信息
实际的回溯(或堆栈追踪)可以通过 GNU 调试器 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 应用程序参数...
传递参数,然后在 gdb 中仅使用不带参数的 run
。例如,要调试用 Python 编写的应用程序,请运行 gdb --args /usr/bin/python /path/to/application
。现在执行任何必要的操作以引发错误。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 作用域受到限制。可以使用 echo 0 > /proc/sys/kernel/yama/ptrace_scope
临时降低限制,或者您可以以特权用户身份运行 gdb,例如使用 sudo。
启动 gdb 并将其附加到进程
$ gdb --pid=PID
gdb 将询问是否应在此调试会话中启用 Debuginfod,您应该回答 y
。
请注意,附加到进程已停止该进程,并且需要显式继续。这取代了#启动程序的新实例部分中工作流程中的 run
命令
(gdb) continue
现在执行任何必要的操作以在附加的进程中引发错误。然后继续启用日志记录并获取追踪信息,与 #启动程序的新实例 中相同。
检查之前的崩溃
要调试已经崩溃的应用程序,您将需要在其核心转储上调用 gdb。有关详细信息,请参阅 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
将带有调试包的镜像放在镜像列表文件的第一个
/etc/pacman.d/mirrorlist
Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch ...
重新构建软件包
如果调试信息未通过 debuginfod 公开(例如,当软件包源自 AUR 时),则可以从源代码重新构建它。有关 官方软件仓库中的软件包,请参阅 ABS,对于 AUR 中的软件包,请参阅 AUR#获取构建文件。
要设置所需的#编译选项,如果您仅将 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
软件包中,以帮助单步调试调试器。如果软件包包含非常大的二进制文件(例如,包含调试符号的文件超过 GB),这会很有利,因为它可能会导致冻结和其他奇怪的不良行为发生。
/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
由于调试标志 -fvar-tracking-assignments'
未处理,使用 Clang 作为编译器的软件包将无法使用 debug
选项构建(例如,之前的 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