Skip to Content
产品功能崩溃与 ANR

崩溃与 ANR(Crash & ANR)

崩溃(Crash)是移动开发者的第一痛点:一次未捕获的异常就是一次用户流失。Argus 在五个端上 自动捕获崩溃——原生信号、未捕获异常、未处理的 Promise rejection——把同一个 bug 的多次发生 聚合成一个 Issue,告诉你它影响了多少用户、集中在哪个版本 / 系统,并通过符号化 (symbolication)把十六进制地址栈还原成 file:line 源码级堆栈。

除了「进程死掉」的崩溃,Argus 还捕获「进程活着但没响应」的问题:主线程阻塞超过阈值 (默认 700ms)时,watchdog 异步抓取主线程栈上报为卡顿事件(AppHang),与应用无响应 (ANR,Application Not Responding)问题同源,走与崩溃相同的聚合 / 展示通道。

崩溃采集是 SDK 最危险的代码——它运行在进程已崩溃、内存状态可能损坏的现场。Argus 的 处理铁律是:现场只做 async-signal-safe 的原始落盘,符号化与上报推迟到下次启动 (defer-to-next-launch),并有二次崩溃保护与坏数据隔离,绝不因为「上报崩溃」而制造新的崩溃。

核心概念

Issue 与指纹(signature)

同一个 bug 会在不同设备上反复崩溃。Argus 对每个崩溃事件计算指纹 (signature,16 位十六进制,sha256[:16]):基于栈顶应用帧 + 异常类型,并做地址归一 (消除 ASLR 随机基址)与消息归一(order 12345order 67890 视为同一 bug)。 同一 signature 的全部事件 = 一个 Issue,Console 与 MCP 工具都以 signature 为聚合键。

error_type:崩溃、卡顿与 OOM 走同一通道

崩溃事件用 error_type 区分成因,常见取值:

error_type含义来源
SIGSEGV / SIGABRT / SIGBUS / SIGILL / SIGFPE原生信号崩溃iOS / Android 信号处理器
NSException 类名 / Kotlin·Java 异常类名运行时未捕获异常NSException / Thread.UncaughtExceptionHandler
JS 错误类名(如 TypeError未捕获 JS 异常 / Promise rejectionWeb / React Native
AppHang主线程阻塞 ≥ 700ms(含卡顿时的主线程栈与 hang_duration_msiOS / Android watchdog
OOM内存不足被系统杀死Android ApplicationExitInfo(API 30+)下次启动补报;iOS 启发式

Android 系统级 ANR 的判定阈值是 5 秒(系统权威、不可调);Argus 的 AppHang watchdog 阈值更敏感(700ms),能在系统弹出 ANR 对话框之前就捕获到主线程栈——大多数 ANR 在成为 ANR 之前,先是一次可观测的 AppHang。

defer-to-next-launch(崩溃落盘、重启上报)

崩溃发生的瞬间进程随时会死,SDK 只做一件事:把原始崩溃数据写入本地文件 (iOS Library/Caches/Argus/crashes/、Android <filesDir>/argus/crashes/)。 下次 Argus.init(...) / Argus.start(...) 时扫盘发现遗留文件,转成崩溃事件入队上报, 成功后删除。所以:崩溃事件出现在 Console 的时机是「用户下次打开 App」之后,而非崩溃当下。

面包屑(Breadcrumbs)

每次 Argus.log() / Argus.addBreadcrumb() 会记入端上环形缓冲,崩溃事件携带最近的 面包屑序列上报。崩溃详情页按「距崩溃的相对时间」渲染(-3s / -2m),还原用户崩溃前的操作轨迹。

符号化状态(symbolicate_status)

发布版二进制的崩溃栈是十六进制地址(iOS / Android)或压缩混淆后的 JS 栈,需要用符号文件 还原。每个崩溃事件带有符号化状态:

状态含义
ok符号化成功,展示 函数名 at 文件:行号
partial部分帧成功(详情页标「部分已 symbolicate」徽标)
no_symbol缺少匹配 build_id 的符号文件——详情页高亮提示并给出上传入口
pending已入队、符号化处理中
failed符号文件存在但解析失败
无需符号化(如可读的 JS / Kotlin 异常栈),直接展示原始栈

接入

平台捕获矩阵

自动捕获快速开始
iOSNSException + POSIX signal + Mach exception 三保险;AppHang watchdog;OOM 启发式/quickstart/ios/
AndroidThread.UncaughtExceptionHandler + sigaction 5 信号 + defer 落盘;AppHang watchdog;OOM(ApplicationExitInfo/quickstart/android/
Webwindow.onerror + unhandledrejection/quickstart/web/
FlutterFlutterError.onError + PlatformDispatcher.instance.onError/quickstart/flutter/
React NativeErrorUtils 全局 handler(同步异常,保留 red box);Hermes Promise rejection(opt-in captureUnhandledRejections/quickstart/react-native/

所有端默认开启自动捕获(captureUncaught: true),初始化即生效,无需额外代码。 不想自动捕获时显式传 captureUncaught: false

手动上报已捕获的异常

捕获后想记录但不想让 App 崩溃的异常,用 captureCrash

try { // ... } catch (err) { Argus.captureCrash({ errorType: err.constructor.name, errorMessage: err.message, stacktrace: err.stack, attributes: { component: "checkout" }, // 可选 }); }

给崩溃分组一个好版本号:初始化时设置 appVersion(如 "2.3.0"),Issue 才能按版本 下钻「这个崩溃是不是新版本引入的」。

符号化:上传 dSYM / .so / sourcemap

符号化管线基于 build_id 匹配:崩溃事件携带二进制镜像的 build_id,服务端在已上传的 符号文件中查找同 build_id 的文件完成解析。当前支持 iOS dSYMAndroid .so (NT_GNU_BUILD_ID);Web sourcemap 与 Android ProGuard mapping 的解析为规划中 (Console 已可上传 sourcemap 备用)。

方式一:Console 手动上传

进入 Console → Settings → Symbols/settings/symbols 页面):

选择平台

iOS (dSYM .zip) / Android (.so) / Web (sourcemap .map)

选择文件并上传

iOS 打包 dSYM 目录为 zip 后上传;Android 上传带调试符号的 .so。上传结果会提示成功 / 被拒绝的文件数。

核对已上传符号

页面下方列出该项目已上传的符号文件;也可用 MCP 工具 symbol.list 检查某平台是否已有符号。

方式二:fastlane CI 自动上传(推荐)

用官方 fastlane 插件把符号上传固化进发版流水线,杜绝「忘了传 dSYM」:

# Gemfile(v0 暂以本地路径引用,RubyGems 发布规划中) gem 'fastlane-plugin-argus', path: '/path/to/x-argus/sdks/fastlane-plugin-argus'
# Fastfile lane :upload_argus_symbols do gym(scheme: "MyApp") # 触发 dSYM 生成 upload_argus_symbols( dsn: ENV['ARGUS_DSN'], # argus://pk:sk@host/project-uuid project_id: ENV['ARGUS_PROJECT_ID'], dsym_path: lane_context[SharedValues::DSYM_OUTPUT_PATH], platform: 'ios', ) end
参数环境变量必填默认值说明
dsnARGUS_DSNArgus DSN
dsym_pathdSYM zip 文件路径
project_idARGUS_PROJECT_ID项目 UUID
endpointhttps://symbolicator.argusplatform.comSymbolicator 服务地址(自托管时改为自己的地址)
platformiosiosandroid

插件源码见 sdks/fastlane-plugin-argus

事后补传符号

崩溃先到、符号后传时,历史事件保持 no_symbol。补传符号后可用 MCP 工具 crash.symbolicateevent_id 手动重新触发符号化(异步执行,完成后用 crash.get 重取即可看到源码级堆栈)。

Console 使用

Issue 列表

进入 Console → Issues/issues 页面),按 signature 聚合展示:

说明
Errorerror_type + 异常消息,点击进详情
Signature16 位指纹
Events该 Issue 的事件总数
Users影响的去重用户数(未登录计为 anonymous)
First seen / Last seen首次 / 最近发生时间

Issue 详情

点击任一 Issue 进入详情页(/issues/<signature>),从上到下:

  1. 头部统计:事件数、影响用户数、首次 / 最近发生时间;最新事件带 trace_id 时 提供「查看链路 trace」跳转;
  2. Analyze with AI:一键把该崩溃交给 Platform Copilot 做根因分析 (自动携带 signature / error_type / app_version 上下文);
  3. Stacktrace:符号化成功时逐帧展示 函数名 at 文件:行号 [所属包],原始栈折叠可查; no_symbol 时高亮提示并链接到符号上传页;
  4. Affected events:每次发生的时间 / 用户 / 会话 / trace / 系统 / 版本, 用户与会话可点击跳到对应日志时间线;
  5. Breadcrumbs:最新事件携带的崩溃前操作轨迹(相对时间 + 级别 + 消息)。

SDK API

API说明
captureUncaught(初始化配置)全端自动捕获开关,默认 true
Argus.captureCrash(...)全端手动上报已捕获异常(errorType / errorMessage / stacktrace? / attributes?
Argus.addBreadcrumb(message, level)iOS / Android手动记面包屑(不上报,崩溃时随事件携带)
ArgusConfig.captureAnr(Android)/ captureAppHang(iOS)移动端AppHang watchdog 开关,默认 true
ArgusConfig.captureOom移动端OOM 检测开关,默认 true
appVersion(初始化配置)全端版本号,用于按版本聚合与回归判断

MCP 工具

工具用途关键参数
crash.recent最近 N 条崩溃(附解析后的 breadcrumbs)project_idlimit
crash.getevent_id 取单个崩溃事件project_idevent_id
crash.by_signature某个 Issue(signature 组)的全部事件project_idsignature(16 位 hex)、limit
crash.by_user某个用户的全部崩溃(「用户 X 为什么总在抱怨」)project_iduser_idlimit
crash.stats近 N 天崩溃统计:总量、按 signature 拆分、逐日趋势project_iddaysapp_version
crash.aggregatesignature / error_type / device_os / app_version / user_id 分组计数project_idgroup_bylimit
crash.recurrence判定某 Issue 在指定时间后是否复发(验证修复是否生效,确定性判定)project_idsignaturesince(ISO 8601)
crash.symbolicate手动(重新)触发某事件符号化(补传符号后回补历史栈)event_id
symbol.list列出项目已上传的符号文件,可按平台过滤project_idplatformios / android / web

配额与计费

  • 1 个崩溃事件 = 1 个 event,计入 events 配额;
  • Copilot 根因分析 = 1 次 AI Call(含其背后的多个工具调用);
  • 符号文件本体存对象存储,单文件上限 500 MB。

详见 配额与计费

常见问题

堆栈全是十六进制地址?

这是 no_symbol(缺符号文件)状态。上传该构建对应的 dSYM / .so(build_id 必须与崩溃时 加载的二进制一致),再对历史事件用 crash.symbolicate 触发回补。用 symbol.list 可先 确认该平台是否已有符号。符号与构建一一对应:每次发版都要上传当次构建的符号, 建议用 fastlane 插件固化进 CI。

触发了崩溃但 Console 里没有?

崩溃是 defer-to-next-launch 上报——再次启动 App 才会扫盘补报,稍等再刷新 /issues。 另外在 Xcode 调试器 attach 时 SIGSEGV 等信号会被 debugger 拦截,验证原生崩溃请脱离 调试器直接运行 App。

AppHang 和 ANR 是什么关系?在哪里看?

AppHang 是 Argus watchdog 检测的主线程阻塞(默认 ≥ 700ms,附卡顿时的主线程栈); ANR 是 Android 系统级判定(5 秒)。AppHang 事件走崩溃通道,在 /issues 里以 error_type = AppHang 出现,按卡顿栈聚合,hang_duration_ms 记录阻塞时长。

怎么验证接入成功?

在测试构建里主动抛一个未捕获异常(或调用 Argus.captureCrash 上报一条测试事件), 重启 App 后到 /issues 确认出现对应 Issue。示例工程 (examples/android-demo / examples/ios-demo) 内置了触发 Kotlin 异常、JNI SIGSEGV、OOM 的按钮可供参考。

同一个 bug 为什么偶尔被分成多个 Issue?

指纹按「栈顶应用帧 + 异常类型 + 归一化消息」计算。如果异常消息里包含高度动态的内容 (随机 ID、完整 URL),或崩溃发生在不同调用路径的不同栈顶,会得到不同 signature。 让异常消息保持稳定、把动态值放进 attributes 而非 message,聚合会更准。

更多排障见 故障排查