调试文件格式
了解特定平台的文件格式及其包含的调试信息。
Sentry 区分四种类型的调试信息:
调试信息(Debug Information): 提供函数名称、源文件路径、行号和内联帧。从指令地址解析这些信息的过程称为“符号化”(symbolication)。相比可执行文件,这部分信息相对较大,通常放在单独的文件中。在 Sentry 中,这些文件被称为 debug companions,并带有
debug
标签。符号表(Symbol Tables): 如果某个库没有可用的调试信息,Sentry 可以使用符号表作为回退来获取函数名称。符号表通常包含在可执行文件和调试伴随文件中。然而,它们不包含足够的信息来解析内联函数或文件名和行号。
symtab
标签表示符号表。源代码(Source Code): 按照惯例,源代码不是常规调试信息文件的一部分。Sentry CLI 可以打包应用程序的源代码并上传到 Sentry,以便在堆栈跟踪中显示源代码上下文。这些包带有
sources
标签。展开信息(Unwind Information): 使 Sentry 能够从 Minidumps 和其他优化构建的二进制崩溃格式中提取堆栈跟踪。这个过程称为“堆栈展开”或“堆栈遍历”。由于在 C++ 中抛出异常时也需要此信息,因此它通常包含在可执行文件或库中。如果上传的文件包含此信息,则会显示
unwind
标签。请注意,在某些平台上实际上不会进行展开操作。例如,WebAssembly 目前没有等同于 minidumps 的机制,这意味着在这种情况下我们不需要该类型的信息。
编译器根据目标平台、架构、构建标志或优化级别将上述调试信息放置在不同的文件中。因此,Sentry 在处理崩溃报告时可能不需要所有这些信息。不过,提供所有可用的调试信息始终是一个好主意。
sentry-cli
可用于列出支持的调试文件的属性并验证其内容。更多信息请参阅 sentry-cli 中的调试信息文件。
在所有 Apple 平台上,可执行文件、动态库和调试伴随文件使用 Mach Object 或简称 Mach-O 容器格式。这适用于 iOS、iPadOS、tvOS、watchOS、macOS 和 visionOS。
可执行文件(Executables) 没有文件扩展名。对于桌面应用程序,它们通常放置在带有
.app
后缀的应用程序包结构中。除非手动剥离,否则可执行文件包含展开信息和符号表。调试信息从不存储在可执行文件中。动态库(Dynamic Libraries) 使用
.dylib
扩展名,其他方面与可执行文件完全相同。调试伴随文件(Debug Companions) 放置在带有
.dSYM
扩展名的文件夹结构中,位于<name>.dSYM/Contents/Resources/DWARF/<name>
。它们通常包含符号表和调试信息,但很少包含展开信息。
当使用 Xcode 或 clang
编译器构建应用程序时,调试信息会自动放置在 dSYM 文件中。然而,如果手动链接,则必须使用以下命令创建 dSYM 文件:
dsymutil /path/to/output[.dylib]
在 Linux 发行版中,可执行文件和调试信息存储在 ELF 容器中。与其他平台不同,没有专门的容器或标识符用于调试伴随文件。
调试信息是二进制文件(可执行文件或库)的一部分,在生成发布构建时由于其大小会被剥离。然而,有一种方法可以将它们保留在一个单独的文件中(要么放在不同的位置,要么带有 .debug
扩展名):
# There is an executable called "binary" in the CWD
objcopy --only-keep-debug binary binary.debug
objcopy --strip-debug --strip-unneeded binary
objcopy --add-gnu-debuglink=binary.debug binary
这将导致以下结构:
可执行文件(Executables) 没有文件扩展名。如果像上面那样剥离,可执行文件包含符号表,但不包含调试信息。如果构建过程中省略了帧指针,展开信息也会被保留。使用类似
--strip-all
的标志可以进一步剥离这些信息。共享库(Shared Libraries) 使用
.so
扩展名,其他方面与可执行文件完全相同。调试伴随文件(Debug Companions) 没有标准的文件扩展名,但通常命名为
.debug
。如果像上面那样剥离,这些文件包含展开信息、调试信息和符号表。
通过包管理器安装的共享库通常会在单独的 *-dev
包中提供其调试信息,并将其放置在类似 /usr/local/debug/...
的位置。为了从这些库中获取符号化的堆栈跟踪,请确保除了上传应用程序的符号外,还上传这些库的符号。
ELF 支持调试信息的压缩,这可以显著减少将调试信息文件上传到 Sentry 所需的时间,从而提高构建速度。gcc
(版本 5 或更新)和 clang
(版本 5 或更新)通过将 -gz
标志传递给编译器和链接器来支持此功能。然而,常见的压缩调试信息的方式是在剥离可执行文件时进行:
# Note the --compress-debug-sections option
objcopy --only-keep-debug --compress-debug-sections=zlib binary binary.debug
This can be verified by checking for the C
flag in readelf
, corresponding to SHF_COMPRESSED
:
readelf -S path/to/file
...
[21] .debug_info PROGBITS 0000000000000000 00000370
000000000000e133 0000000000000000 C 0 0 1
在 Microsoft Windows 上的可执行文件和动态库,以及在所有操作系统上由 .NET 平台创建的文件,使用 Portable Executable (PE) 容器格式。关联的调试信息存储在 Program Database (PDB) 文件中。
可执行文件(Executables) 使用
.exe
扩展名。仅当编译为 64 位架构时,它们包含展开信息。否则,它们不包含任何可用信息,并且在上传到 Sentry 时会被忽略。动态库(Dynamic Libraries) 使用
.dll
扩展名,其他方面与可执行文件完全相同。调试伴随文件(Debug Companions) 存储在
.pdb
文件中。它们通常包含调试信息,在大多数情况下还包含符号表。对于 32 位程序,它们也包含展开信息。在少数情况下,它们的文件名可能与其对应的可执行文件不同。.NET 平台使用这种格式的一个变体,称为 Portable PDBs。从 Sentry 22.11.0(或 Sentry CLI 2.8.0 或 Symbolic 10.0.0)开始支持 Portable PDBs。
Google Breakpad 库建立了一个平台无关的 ASCII 格式来存储调试信息。此类文件通常为使用 Breakpad、Crashpad 或 Electron Framework 的应用程序生成。
Breakpad 仓库包含每个平台的 dump_syms
工具,可以将本地调试文件转换为 Breakpad 符号。这些转换工具将所有可用信息打包到一个文件中,因此只需要上传一个文件。
与本地调试文件不同,Breakpad 符号丢弃了很多不需要用于处理 minidumps 的信息。最显著的是,内联函数未声明,因此 Sentry 无法在堆栈跟踪中显示内联帧。
对于 WebAssembly,我们支持 DWARF in WASM containers。 请注意,我们不支持源映射(source maps),尽管它也是一种用于 WASM 调试的格式,但由于其缺点使其不适合像 Sentry 这样的崩溃报告工具。
由于 WASM 尚未指定调试/构建 ID,我们提供了一个名为 wasm-split 的工具,帮助你创建一个准备好上传到 Sentry 的调试伴随文件,同时从发布二进制文件中移除所有调试信息。
对于 Android 用户,推荐的方法是 使用 Gradle 插件。
ProGuard 映射文件允许 Sentry 将混淆的 Java 类路径和方法名称解析为其原始形式。在这个意义上,它们充当 Java 和 Android 应用程序的调试信息文件。
这样翻译后的版本保留了所有特殊标记和代码块,并确保术语如 symbol
保持英文原样。如果有更多需要调整或添加的内容,请告知!