5.3 崩溃分析与符号化:没有符号的崩溃日志是废纸

核心结论

线上崩溃日志( native crash )默认只有一串地址,看不出是哪个函数。必须保留每个发布构建的符号文件并做符号化,才能把"地址 0x1234abcd"翻译成"UMyCharacter::Tick 第 37 行"。这是从"游戏崩了"到"知道为什么崩"的关键一步。

符号文件是什么

平台 符号文件 生成位置
Android .so 对应的符号(带符号的 libUE4.so) 打包时 Build 输出 / Engine/Saved
iOS .dSYM 包 Xcode Archive 时生成

铁律:每次发布构建立刻把符号归档(版本号 + 平台命名),丢了就再也符号化不了——线上崩溃来了只能干瞪眼。

Android 崩溃分析流程

  1. 拿日志:adb logcat 或崩溃收集服务导出。
  2. 定位地址:找到 Signal 11 (SIGSEGV) 那段,下面有 backtrace 与偏移地址。
  3. 符号化:用 NDK 的 addr2line 或 UE 的 CrashReportClient:
# addr2line 示例(路径与参数按实际)
llvm-addr2line -e libUE4.sym.so -f -C 0x1234abcd
  1. 看懂 backtrace:从上往下,第一个属于自己的模块/函数通常是元凶(前面的 libc 之类是异常处理路径)。

iOS 崩溃分析流程

  1. 从 TestFlight/用户设备导出 .crash 文件。
  2. 符号化:把 .crash 与对应 .dSYM 放同目录,用 Xcode 的 Organizer 或 symbolication 工具:
  3. atos -arch arm64 -o <App.dSYM> -l <loadAddress> <address>
  4. 崩溃报告结构:先看 Exception Type,再看线程回溯(标 0 MyGame 的帧)。

常见崩溃大类(按我的经验排序)

类型 典型特征 查的方向
空指针访问 SIGSEGV,集中在某个游戏逻辑 最近改动的 UPROPERTY 空检查
越界 SIGSEGV,随机出现在数组操作处 TArray 边界检查
GC 相关 间歇性、UObject 相关 UPROPERTY 是否正确标记(没标记被 GC 收走)
多线程 难复现、计时敏感 是否有非主线程操作 U 对象
内存不足 OOM 崩溃前有内存增长曲线 大贴图未卸载/泄漏(见 0303 流送池)

常见坑与解法

坑 1:符号文件缺失,线上崩溃没法查

解法:把符号归档做成 CI 强制步骤:构建结束 → 符号上传到符号库(版本/平台目录)→ 未上传则构建失败。

坑 2:符号化结果"串线"(函数名不对)

原因:符号与构建版本不匹配(同版本号重编过)。

解法:符号与包按构建号(而非版本号)配对;CI 构建号唯一递增。

坑 3:iOS 符号化只显示 hex 地址

原因:dSYM 没匹配上(.crash 的 UUID 与 dSYM 不一致)。

解法:用 dwarfdump --uuid <App.dSYM> 对比崩溃日志头部的 UUID,一致才能符号化。

坑 4:偶发崩溃无法复现

解法: - 加崩溃上报(自建或第三方服务):上传崩溃 minidump + 设备信息。 - 重视稀有机型/系统版本分布:如果 80% 崩溃集中在 2% 的设备,考虑在设备描述里降档(见第一章)。

我的"崩溃分级响应"

严重度 定义 响应
P0 启动即崩/核心流程崩 热修复/紧急版 24h 内
P1 非核心流程必崩 下个版本
P2 低于 0.1% 用户偶发 排队观察

关键指标:崩溃率(崩溃用户/日活)。移动平台底线经验:>2% 就该停手处理,>5% 是事故。

相关节点速查

  • Android 符号化:NDK 的 addr2line(NDK 目录 toolchain 下)
  • iOS 符号化:atos / Xcode
  • 崩溃上报:UE 的 CrashReportClient(PC 端收集器需另行部署)

心得:我经历过最惨的一次是上线第一天崩溃率 8%,但符号文件丢了,团队两天内对着一堆地址干瞪眼。从那以后"符号先归档再发包"成了不可动摇的铁律,写进了 CI 脚本。符号文件就是崩溃日志的翻译机。