调试文件格式

了解特定平台的文件格式及其包含的调试信息。

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 文件:

Copied
dsymutil /path/to/output[.dylib]

在 Linux 发行版中,可执行文件和调试信息存储在 ELF 容器中。与其他平台不同,没有专门的容器或标识符用于调试伴随文件。

调试信息是二进制文件(可执行文件或库)的一部分,在生成发布构建时由于其大小会被剥离。然而,有一种方法可以将它们保留在一个单独的文件中(要么放在不同的位置,要么带有 .debug 扩展名):

Copied
# 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 标志传递给编译器和链接器来支持此功能。然而,常见的压缩调试信息的方式是在剥离可执行文件时进行:

Copied
# 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:

Copied
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 保持英文原样。如果有更多需要调整或添加的内容,请告知!