关于 apple-crash-symbolication
用于分析 Apple 平台上 .NET MAUI 和 Mono 应用崩溃的技能。它解析 .ips 报告、查找 UUID 匹配的 dSYM 符号,并通过 atos 将原生栈帧解析为函数及源码位置。
主要功能
- 01解析双段式 .ips 崩溃报告
- 02识别 .NET 运行时栈帧
- 03查找 UUID 匹配的 dSYM 符号
- 04使用 atos 解析栈帧
- 05通过 PowerShell 自动符号化
适用场景
- 01追踪 iOS 上的 .NET MAUI 崩溃
- 02检查 macOS 上的 Mono 崩溃报告
- 03将原生栈帧解析到源码位置
- 04在符号化前快速检查崩溃报告
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill apple-crash-symbolication安装后在 Claude Code 中输入/apple-crash-symbolication确认已加载。
附带文件references/ips-crash-format.mdscripts/Symbolicate-Crash.ps1
详细介绍
Apple 平台崩溃日志 .NET 符号化
使用 Mach-O UUID 和 dSYM 调试符号包,将 Apple 平台(iOS、tvOS、Mac Catalyst、macOS)上 .NET MAUI 和 Mono 应用崩溃的原生回溯帧解析为函数名称、源文件和行号。
输入: 崩溃日志文件(.ips JSON 格式,iOS 15+ / macOS 12+)、atos(来自 Xcode),以及可选的已连接 iOS 设备(用于提取崩溃日志)。
不适用情形: 崩溃库不是 .NET 组件(例如纯 Swift/UIKit),或崩溃日志是 Android tombstone。
工作流
步骤 1:解析 .ips 崩溃日志
格式检查: 继续之前,请确认文件是 .ips JSON 格式。第一行必须是有效的 JSON。如果文件是纯文本(例如带有 #NN pc 帧行的 Android tombstone,或旧版 Apple .crash 文本格式),请立即停止——此工作流不适用。请向用户说明格式不匹配,不要尝试进行符号化。
.ips 文件是由两部分组成的 JSON:第 1 行是元数据头;其余行是独立的 JSON 崩溃正文。请分别解析:
lines = open('crash.ips').readlines()
metadata = json.loads(lines[0]) # app_name, bundleID, os_version, slice_uuid
crash = json.loads(''.join(lines[1:])) # Full crash report
崩溃正文中的关键字段:
usedImages[N]包含每个已加载二进制文件的name、base(加载地址)、uuid和archthreads[N].frames[M]包含imageOffset和imageIndex;帧地址 =usedImages[imageIndex].base + imageOffsetexception.type、exception.signal(例如EXC_CRASH/SIGABRT)asi(应用专属信息)通常包含托管异常消息lastExceptionBacktrace包含触发崩溃的异常帧faultingThread是threads数组中的索引
解析注意事项: 部分 .ips 文件中存在大小写冲突的重复键(vmRegionInfo / vmregioninfo)。解析前,请预处理原始 JSON,将小写的重复键重命名。asi 字段可能不存在。
步骤 2:识别 .NET 运行时库
筛选 usedImages 中的 .NET 运行时库:
| 库 | 运行时 |
|---|---|
libcoreclr | CoreCLR 运行时 |
libmonosgen-2.0 | Mono 运行时 |
libSystem.Native | .NET BCL 原生组件 |
libSystem.Globalization.Native | .NET BCL 全球化 |
libSystem.Security.Cryptography.Native.Apple | .NET BCL 加密 |
libSystem.IO.Compression.Native | .NET BCL 压缩 |
libSystem.Net.Security.Native | .NET BCL 网络安全 |
在 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 系统框架。
第 3 步:解读崩溃
从 asi(特定于应用的信息)开始查看——对于 .NET 崩溃,其中通常包含托管异常类型和消息(例如 XamlParseException、NullReferenceException)。根本原因可能已在此处显现。
然后检查故障线程(threads[faultingThread])。在查看其他线程之前,先说明帧 #0 和 #1 的含义。跨线程上下文(GC 状态、线程池)有助于验证,但不能作为因果关系的证据。
还要检查 lastExceptionBacktrace,查看托管异常经过 xamarin_process_managed_exception 等桥接函数的路径。
有时可以从 usedImages 中的映像路径看出 .NET 运行时版本,尤其是在 macOS 上使用共享框架安装或 NuGet 包布局时(例如 .../Microsoft.NETCore.App/10.0.4/libcoreclr.dylib)。不过,在 iOS 上,映像路径通常位于应用包内(例如 .../Frameworks/libcoreclr.framework/libcoreclr),不会包含运行时版本,因此通常需要通过 Mach-O UUID 与 SDK 包或符号服务器下载的文件进行匹配来推断版本,而不能只依赖路径。
第 4 步:查找 dSYM
对于每个需要符号化的 .NET 库,查找 UUID 匹配的 dSYM:
- Microsoft 符号服务器(自动):通过
https://msdl.microsoft.com/download/symbols/_.dwarf/mach-uuid-sym-{UUID}/_.dwarf下载.dwarf(UUID 使用小写,不带连字符)。将其转换为.dSYM包(使用usedImages[].name中的映像名称,例如libcoreclr):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 包:
$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 完全匹配。
第 5 步:使用 atos 进行符号化
atos -arch arm64 -o <path.dSYM/Contents/Resources/DWARF/binary_name> -l <load_address> <frame_addresses...>
-o指向.dSYM包内部的 DWARF 二进制文件(Contents/Resources/DWARF/),而不是包本身-l是usedImages[N].base中的加载地址- 使用
usedImages[N].arch中的arch(通常为arm64,也可能是arm64e) - 每次调用可传入多个地址,以批量进行符号化
# Example: symbolicate libcoreclr frames
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 可自动执行完整工作流(解析、查找 dSYM、下载符号和符号化)。请根据此 SKILL.md 文件的位置解析路径。
# $SKILL_DIR is the directory containing this SKILL.md
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。
获取崩溃日志
使用 idevicecrashreport(来自 libimobiledevice)从已连接的 iOS 设备提取崩溃日志:
idevicecrashreport -e /tmp/crashlogs/
find /tmp/crashlogs/ -iname '*MyApp*' -name '*.ips'
也可在 Xcode > Window > Devices and Simulators > View Device Logs 中查看,或者在 ~/Library/Logs/CrashReporter/(Mac Catalyst)、~/Library/Logs/DiagnosticReports/(macOS)中查找。
验证
dwarfdump --uuid <dsym>与崩溃日志中的 UUID 匹配- 至少有一个 .NET 帧解析出函数名称(而非原始地址)
- 解析出的路径包含可识别的 .NET 运行时结构(例如
src/coreclr/、mono/metadata/、mono/mini/)
停止信号
- 文件格式错误:如果文件不是
.ipsJSON(例如带有#NN pc堆栈帧的 Android tombstone,或旧版.crash文本格式),请立即停止——向用户报告格式不匹配,不要继续进行任何符号化。不要尝试使用其他工具或工作流进行符号化。 - 未找到 .NET 帧:报告已解析的帧并停止。
- 所有帧均已解析:提供符号化后的回溯,并简要分析崩溃情况(故障线程、异常类型、可能的问题区域)。如果用户要求进一步调查,则继续处理。
- dSYM 不可用 / UUID 不匹配:报告未符号化的帧及其 UUID 和地址。建议查找原始构建产物。
atos不可用:提供供用户运行的手动atos命令。不要安装 Xcode。atos随 Xcode Command Line Tools 提供(xcode-select --install)。
参考资料
- IPS 格式详情:有关更多
.ips解析详情和 macOS 符号包差异,请参阅 references/ips-crash-format.md。