Crash 模拟与异常捕获概述

大家好,我是这门课的主讲。说实话,干了这么多年移动端稳定性测试,我最大的感触就是——Crash 这玩意儿,你越怕它,它越找你。很多团队把 Crash 率当成 KPI 来追,但真到了线上,用户骂娘的时候,你连复现都复现不出来,那才叫绝望。

我个人习惯是,在项目早期就要主动「搞破坏」。不是真的破坏,而是用可控的手段去模拟崩溃场景,再配上完善的异常捕获机制,把那些藏在犄角旮旯的 bug 提前揪出来。这套方法,我在这门课里会掰开揉碎讲清楚。

为什么我们要学 Crash 模拟?

你想想看,一个 App 上线后闪退,你能拿到什么?通常是几行堆栈、一个模糊的设备型号。够用吗?不够。我遇到过好几次,线上 Crash 堆栈指向的代码行号跟实际发布版本根本不匹配——原因就是符号表没对齐,或者异常没来得及上报。

Crash 模拟,说白了就是在可控环境里制造事故。比如凭空抛一个空指针,或者故意让线程死锁。然后观察你的异常捕获系统能不能接住、怎么接住、接住之后能吐出多少有用信息。只有反复练过这套流程,你到了线上才不会慌。

核心观点:Crash 模拟不是「没事找事」,而是用最小代价提前暴露防御系统的薄弱环节。

我们能学到什么?

这门课总共 30 章,但咱们今天先搭个骨架。我会带你走通下面几个关键点:

  • Crash 的分类与触发机制:哪些是系统级(SIGSEGV、SIGABRT),哪些是应用级(NSException、Java Exception)。
  • 常见模拟手法:代码注入、运行时修改、内存踩踏、信号触发。每个都有适用场景和坑。
  • 异常捕获的最佳实践:从 NSSetUncaughtExceptionHandler 到 signal 处理器,再到 Mach 异常端口。你该用哪一层?
  • 日志与符号化:拿到原始堆栈怎么还原成可读的类名、行号?dSYM 和 ProGuard 映射文件怎么管理?
  • 自动化测试集成:如何把 Crash 模拟塞进 CI/CD 流程,每次构建后自动跑一轮「破坏测试」?

说实话,这些内容单独拎出来都不难,但串联起来就能形成一套完整的防御体系。

知识体系一览

为了方便你快速建立全局视角,我把本章核心概念画了张图。你一看就明白 Crash 模拟和异常捕获在整个测试流程中的位置:

Crash模拟与异常捕获知识框架 Crash 模拟 信号触发 异常抛出 异常捕获 系统回调 自定义Handler 日志分析 符号化 复现验证 三个阶段层层递进:只有模拟得够真实,捕获才能不漏; 只有捕获得够完整,分析才有意义。 模拟阶段 捕获阶段 分析阶段 自动化集成(CI/CD) 将模拟用例编入流水线,每次构建自动触发

这张图把课程的主脉络串起来了。你会发现,模拟 -> 捕获 -> 分析 是一个闭环,而最下面是自动化集成,让这个闭环持续运转。我最早做这个方案的时候,光模拟就折腾了两周,结果发现捕获层根本没处理好信号嵌套问题——还好是内部演练,要是线上就惨了。

一些我的个人建议

课程正式开始之前,我想给你三个贴士:

  1. 别贪多。 别试图在一章里把所有 Crash 类型都模拟一遍。先从最典型的空指针和数组越界入手,跑通流程再说。
  2. 日志格式要统一。 我见过最头疼的场面:iOS 和 Android 的 Crash 日志字段顺序不一样,解析脚本要写两套。前期就约定好 JSON 规范,后面省大事。
  3. 符号表一定要归档。 每次发版都把 dSYM 或 mapping 文件存到固定服务器,按版本号命名。我曾经因为忘记保存,线上堆栈全是十六进制地址,两眼一抹黑。

小技巧:模拟 Crash 时最好加一个「安全开关」,比如通过 adb 命令或 Xcode Scheme 参数启用,避免误触到线上环境。我一般用编译宏 #if DEBUG 来隔离。

注意:千万不要在用户侧进行任何 Crash 模拟!所有测试必须在预发布环境或内部构建上进行。有些国家/地区对 App 崩溃率有法律要求,故意制造崩溃可能违反条款。