移动/游戏
Android .NET 应用崩溃 tombstone…
当 .NET MAUI/Xamarin/Mono/CoreCLR 的 Android 应用发生原生崩溃(SIGABRT/SIGSEGV 等),需要把 tombstone 中 libmonosgen-2.0.so、libcoreclr.so 等原生帧还原成函数名和源码行号时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
Android .NET 应用崩溃 to通用
解决思路
- 解析 tombstone 回溯:每帧格式为 `#NN pc OFFSET /path/to/library.so (optional_symbol+0xNN) (BuildId: HEXSTRING)`,提取帧号、PC 偏移(已是库相对地址,十六进制)、库名和 BuildId(32–40 位十六进制)。默认对所有线程做符号化(GC/finalizer 等后台线程常有有用的 .NET 帧);崩溃线程的回溯在最前,其他线程在 `--- --- ---` 标记之后。
- 处理格式问题:脚本会自动识别带或不带 `backtrace:` 头的 `#NN pc` 帧行,并自动剥离 logcat 时间戳/tag 前缀;logcat 抓取的 tombstone 常缺 BuildId,可用 `adb shell readelf -n`、CI 构建产物或 .NET runtime NuGet 包恢复;GitHub issue 粘贴可能把 `#1 pc` 变成 issue 链接,保存文件前把 `org/repo#N pc` 替换回 `#N pc`;若脚本解析失败,手动提取 `#NN pc OFFSET library.so (BuildId: HEX)` 元组。
- 识别 .NET 运行时库:`libmonosgen-2.0.so` 对应 Mono(MAUI、Xamarin、解释器);`libcoreclr.so` 对应 CoreCLR(JIT 模式);`libSystem.*.so` 是 .NET BCL 原生组件(Native、Globalization.Native、IO.Compression.Native、Security.Cryptography.Native.OpenSsl、Net.Security.Native)。NativeAOT 没有 libcoreclr.so/libmonosgen-2.0.so,运行时静态链接进应用二进制(如 libMyApp.so),但 libSystem.*.so 仍可符号化,应用二进制本身需要自己的调试符号。除非用户特别要求,跳过 libc.so、libart.so 等 Android 系统库。
- 下载调试符号:对每个唯一的 .NET BuildId,从 `https://msdl.microsoft.com/download/symbols/_.debug/elf-buildid-sym-<BUILDID>/_.debug` 下载,例如 `curl -sL "https://msdl.microsoft.com/download/symbols/_.debug/elf-buildid-sym-1eb39fc72918c7c6c0c610b79eb3d3d47b2f81be/_.debug" -o libmonosgen-2.0.so.debug`。用 `file libmonosgen-2.0.so.debug` 验证应显示 `ELF 64-bit ... with debug_info, not stripped`;若返回 404 或 HTML 说明该构建未发布符号。不要加减库基址——tombstone 中的偏移已是库相对地址。
- 逐帧符号化:运行 `llvm-symbolizer --obj=libmonosgen-2.0.so.debug -f -C 0x222098`,输出如 `ves_icall_System_Environment_FailFast` 和 `/__w/1/s/src/runtime/src/mono/mono/metadata/icall.c:6244`。`/__w/1/s/` 是 CI 工作区根,有意义路径从 `src/runtime/` 开始,对应 dotnet/dotnet VMR。
- 呈现符号化回溯:把原始帧号与解析出的函数名和源码位置合并,例如 `#00 libc.so abort+164`、`#01 libmonosgen-2.0.so ves_icall_System_Environment_FailFast (mono/metadata/icall.c:6244)`、`#02 libmonosgen-2.0.so do_icall (mono/mini/interp.c:2457)`、`#03 libmonosgen-2.0.so mono_interp_exec_method (mono/mini/interp.c)`。未解析的帧(`??`)保留原始行及 BuildId 和 PC 偏移。
- 使用自动化脚本:`pwsh scripts/Symbolicate-Tombstone.ps1 -TombstoneFile tombstone_01.txt -LlvmSymbolizer llvm-symbolizer`。可选参数:`-CrashingThreadOnly`(仅崩溃线程)、`-OutputFile path`(写入文件)、`-ParseOnly`(只报告库/BuildId/URL 不下载)、`-SkipVersionLookup`(跳过运行时版本识别)。
- 查找 llvm-symbolizer:优先 Android NDK 的 `$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/*/bin/llvm-symbolizer` 或 `$ANDROID_HOME/ndk/*/toolchains/llvm/prebuilt/*/bin/llvm-symbolizer`;也可 `brew install llvm`、`apt install llvm` 或 macOS 上 `xcrun --find llvm-symbolizer`。若不可用,完成步骤 1–3 并把下载命令和 llvm-symbolizer 命令交给用户执行,不要花时间安装 LLVM。
- 理解 CI 源码路径前缀:`/__w/1/s/src/runtime/` 对应 dotnet/dotnet VMR 的 `src/runtime/`;`/__w/1/s/src/mono/` 对应 VMR 的 `src/mono/`(旧构建);`/__w/1/s/` 是 VMR 根。
- 识别运行时版本:脚本通过把 BuildId 与本地已安装的 runtime pack 匹配来确定精确 .NET 运行时版本,搜索 SDK packs(`$DOTNET_ROOT/packs/`)、NuGet 缓存(`~/.nuget/packages/`)以及 NuGet.org 在线回退;找到后从 `.nuspec` 的 `<repository commit="..." />` 提取版本和源码提交。传 `-SkipVersionLookup` 可禁用;需要 `llvm-readelf`(从 NDK 自动发现)。
- 验证结果:`file <debug-file>` 显示 `ELF ... with debug_info, not stripped`;至少一个 .NET 帧解析出函数名(非 `??`);解析出的路径包含可识别的 .NET 运行时结构(如 `mono/metadata/`、`mono/mini/`)。
- 遇到停止信号时停止:无 .NET 帧则报告已解析帧并停止;全部帧已解析则呈现符号化回溯,不要深入源码或尝试构建/调试运行时;符号不可用(404)则每个 BuildId 只尝试一次后停止,报告带 BuildId 和偏移的未符号化帧;llvm-symbolizer 不可用则用 `-ParseOnly` 并给出手动命令,不要安装 LLVM。
- 处理常见坑:缺 BuildId 时用 `adb shell readelf -n /path/to/lib.so`、CI 构建产物或 runtime NuGet 包(`~/.dotnet/packs/Microsoft.NETCore.App.Runtime.Mono.android-arm64/<version>/`)恢复,优先拉原始 tombstone 文件(`adb shell cat /data/tombstones/tombstone_XX`)因其总含 BuildId;符号 404 时检查构建产物或 NuGet runtime pack 中本地未剥离的 `.so`/`.so.dbg`;NativeAOT 时 tombstone 无运行时 .so,libSystem.*.so 仍可用符号服务器,应用二进制需自己的调试符号;llvm-symbolizer 用 LLVM 14+ 以获得最佳 DWARF 兼容性;多个 BuildId 时每个 .NET 库各有自己的 BuildId,需分别下载符号。
适用边界 · 注意事项
- 崩溃是托管 .NET 异常(logcat 中已有托管堆栈跟踪)时不要用。
- 崩溃库不是 .NET 组件(如 libart.so)或纯 Java/Kotlin 崩溃时不要用。
- tombstone 来自 iOS 时不要用。