移动/游戏
从 Apple .ips 崩溃日志中符号化 .NET…
当 .NET MAUI 或 Mono 应用在 iOS/tvOS/Mac Catalyst/macOS 上崩溃并拿到 .ips 日志时,用 Mach-O UUID 匹配 dSYM 并通过 atos 把原生回溯解析成函数名、源文件和行号。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
从 Apple .ips 崩溃日志中符号通用
解决思路
- 先做格式校验:确认文件是 .ips JSON 格式(第一行必须是合法 JSON)。如果是纯文本(如 Android tombstone 的 `#NN pc` 帧行,或旧版 Apple `.crash` 文本格式),立即停止,向用户报告格式不匹配,不要尝试任何符号化。
- 按两段式 JSON 解析 .ips:第 1 行是元数据头(app_name、bundleID、os_version、slice_uuid),其余行是独立的崩溃主体 JSON,分别解析:`lines = open('crash.ips').readlines()`;`metadata = json.loads(lines[0])`;`crash = json.loads(''.join(lines[1:]))`。
- 从崩溃主体提取关键字段:`usedImages[N]` 的 `name`、`base`(加载地址)、`uuid`、`arch`;`threads[N].frames[M]` 的 `imageOffset`、`imageIndex`(帧地址 = `usedImages[imageIndex].base + imageOffset`);`exception.type`、`exception.signal`(如 `EXC_CRASH`/`SIGABRT`);`asi`(常含托管异常消息);`lastExceptionBacktrace`;`faultingThread`(threads 数组下标)。
- 解析坑:部分 .ips 存在大小写冲突的重复键(`vmRegionInfo` / `vmregioninfo`),解析前先预处理原始 JSON,把小写重复键重命名;`asi` 字段可能不存在。
- 过滤 `usedImages` 找出 .NET 运行时库:`libcoreclr`(CoreCLR)、`libmonosgen-2.0`(Mono)、`libSystem.Native`、`libSystem.Globalization.Native`、`libSystem.Security.Cryptography.Native.Apple`、`libSystem.IO.Compression.Native`、`libSystem.Net.Security.Native`。Apple 平台上它们以 `.framework` 打包,镜像名可能不含 `.dylib`,用子串匹配(如 `libcoreclr` 而非 `libcoreclr.dylib`)。应用二进制可能在 `usedImages` 中出现两次且 UUID 不同。
- 关注应用二进制中的桥接函数:`xamarin_process_managed_exception`(托管异常桥接为 ObjC NSException)、`xamarin_main`、`mono_jit_exec`、`coreclr_execute_assembly`。
- NativeAOT 情况:运行时静态链接进应用二进制,`libSystem.*` BCL 库仍独立;应用二进制需要自己构建产物中的 dSYM。
- 除非用户明确要求,跳过 `libsystem_kernel.dylib`、`UIKitCore` 等 Apple 系统框架。
- 解读崩溃:先看 `asi`(常含托管异常类型和消息,如 `XamlParseException`、`NullReferenceException`,根因可能已可见);再看故障线程 `threads[faultingThread]`,先解释 #0 和 #1 帧含义;跨线程上下文(GC 状态、线程池)仅用于验证,不作为因果证据;同时检查 `lastExceptionBacktrace` 中经过 `xamarin_process_managed_exception` 等桥接函数的托管异常路径。
- 判断 .NET 运行时版本:macOS 共享框架安装或 NuGet 包式布局下,`usedImages` 的镜像路径可能含版本(如 `.../Microsoft.NETCore.App/10.0.4/libcoreclr.dylib`);iOS 上路径通常在 app bundle 内(如 `.../Frameworks/libcoreclr.framework/libcoreclr`)不含版本,需通过 Mach-O UUID 匹配 SDK packs 或符号服务器下载来推断。
- 为每个需符号化的 .NET 库定位 UUID 匹配的 dSYM,来源依次为:① Microsoft 符号服务器(自动):从 `https://msdl.microsoft.com/download/symbols/_.dwarf/mach-uuid-sym-{UUID}/_.dwarf` 下载 `.dwarf`(UUID 小写、无连字符),再转成 `.dSYM` bundle(用 `usedImages[].name` 作为镜像名):`mkdir -p libcoreclr.dSYM/Contents/Resources/DWARF`;`cp _.dwarf libcoreclr.dSYM/Contents/Resources/DWARF/libcoreclr`;② 构建产物:`bin/Debug/net*-ios/ios-arm64/<App>.app.dSYM/`;③ SDK packs:`$DOTNET_ROOT/packs/Microsoft.NETCore.App.Runtime.<rid>/<version>/runtimes/<rid>/native/`;④ NuGet 缓存:`~/.nuget/packages/microsoft.netcore.app.runtime.<rid>/<version>/runtimes/<rid>/native/`;⑤ `dotnet-symbol`:`dotnet-symbol --symbols -o symbols-out <path-to-binary.dylib>`。
- 始终校验:`dwarfdump --uuid <dsym>` 必须与崩溃日志中的 UUID 完全一致。
- 用 atos 符号化:`atos -arch arm64 -o <path.dSYM/Contents/Resources/DWARF/binary_name> -l <load_address> <frame_addresses...>`。`-o` 指向 `.dSYM` bundle 内部的 DWARF 二进制(`Contents/Resources/DWARF/`),不是 bundle 本身;`-l` 用 `usedImages[N].base`;`arch` 用 `usedImages[N].arch`(通常 `arm64`,可能 `arm64e`);一次传多个地址批量符号化。示例:`atos -arch arm64 -o libcoreclr.dSYM/Contents/Resources/DWARF/libcoreclr -l 0x104000000 0x104522098 0x1043c0014`。
- 输出中剥离 `/__w/1/s/` CI 工作区前缀,有意义的路径从 `src/runtime/` 开始,对应 dotnet/dotnet VMR。
- 可用自动化脚本 `scripts/Symbolicate-Crash.ps1`(路径相对 SKILL.md 解析)自动完成解析、dSYM 查找、符号下载和符号化:`pwsh "$SKILL_DIR/scripts/Symbolicate-Crash.ps1" -CrashFile MyApp-2026-02-25.ips`。先用 `-ParseOnly` 快速概览(无需 atos);本地缺 dSYM 时脚本会自动从 Microsoft 符号服务器下载。可用标志:`-CrashingThreadOnly`、`-OutputFile path`、`-ParseOnly`、`-SkipVersionLookup`、`-SkipSymbolDownload`、`-SymbolCacheDir path`、`-DsymSearchPaths path1,path2`。
- 从连接的 iOS 设备拉取崩溃日志:`idevicecrashreport -e /tmp/crashlogs/`,再 `find /tmp/crashlogs/ -iname '*MyApp*' -name '*.ips'`(工具来自 libimobiledevice)。其他来源:Xcode > Window > Devices and Simulators > View Device Logs;Mac Catalyst 在 `~/Library/Logs/CrashReporter/`;macOS 在 `~/Library/Logs/DiagnosticReports/`。
- 验证结果:① `dwarfdump --uuid <dsym>` 与崩溃日志 UUID 匹配;② 至少一个 .NET 帧解析出函数名(而非裸地址);③ 解析出的路径含可识别的 .NET 运行时结构(如 `src/coreclr/`、`mono/metadata/`、`mono/mini/`)。
- 停止信号:格式错误(非 .ips JSON)→ 立即停止并报告;未发现 .NET 帧 → 报告已解析帧并停止;全部帧已解析 → 给出符号化回溯和简要崩溃分析(故障线程、异常类型、可能区域),用户要求深入再继续;dSYM 不可用/UUID 不匹配 → 报告未符号化帧及 UUID 和地址,建议查找原始构建产物;atos 不可用 → 给出手动 atos 命令让用户自行运行,不要安装 Xcode(atos 随 Xcode Command Line Tools 提供,`xcode-select --install`)。
适用边界 · 注意事项
- 纯 Swift/Objective-C 崩溃且不含任何 .NET 组件
- Android tombstone 文件或旧版 Apple `.crash` 纯文本格式
- 崩溃库不是 .NET 组件(如纯 Swift/UIKit)