name: rnoh-stability-triage description: 用于分析 OpenHarmony React Native / RNOH / React Native Harmony 场景中的稳定性问题。只要用户在排查闪退、卡死、内存异常、资源泄漏、SIGSEGV、SIGABRT、UAF、空指针、死锁、生命周期异常、兼容性崩溃、sort 排序崩溃、容器越界访问,或者明确想判断"是不是框架问题""历史版本是否修过""该升级还是回捞补丁",就应使用此 skill。
RNOH 稳定性问题分析
这个 skill 用于把零散日志、版本信息和当前框架代码收敛成可执行的稳定性分析结论。
优先目标有三个:
- 先判断是否能命中历史已修复问题。
- 再判断更像是用户代码问题、框架问题,还是证据不足。
- 最后给出按优先级排序的修复建议。
对伙伴排查最重要的是两点:
- 不要停留在“像某类问题”,而是尽量收敛到具体模块、具体触发阶段和具体历史修复。
- 即使证据不足,也要告诉对方下一步最值得补哪 1 到 3 项信息,而不是泛泛要求“补更多日志”。
分析入口
使用这个 skill 时,先遵循稳定性文档里的分析口径:
- 先判断问题属于应用异常退出、应用冻屏、内存异常还是资源泄漏。
- 再基于日志、堆栈、版本和触发阶段做 AI 分析。
- 如果 AI 分析收敛到具体模块或历史修复,再结合当前代码做验证。
如果工作区中存在以下文档,优先对齐其口径:
- docs/zh-cn/稳定性/稳定性问题介绍/稳定性概览.md
- docs/zh-cn/稳定性/稳定性问题介绍/稳定性分析方法.md
如果需要读取 GitCode PR、MR 或文件列表,先检查以下可选配置文件:
- references/gitcode-config.json
当该文件存在且 gitcode_access_token 已填写为有效值时,优先使用 GitCode API 读取 PR 详情、变更文件和 patch;未填写、仍为占位值或文件不存在时,退回到公开页面抓取、git fetch 或工作区本地代码分析方式。
注意事项:
- 只将该 token 用于读取 GitCode PR / MR 相关信息,不在输出中回显 token。
- 如果仓库或 PR 为公开可读,优先走无鉴权方式;只有在需要 API 结构化数据或公开页面信息不足时再使用 token。
- 如果读取失败,要在结论中说明是“未配置 token / token 无效 / 权限不足”,不要伪造 PR 信息。
何时优先使用
遇到以下场景时,优先使用这个 skill,而不是直接给笼统结论:
- 用户提供 crash 栈、hlog、native backtrace、JSVM 错误、SIGSEGV、SIGABRT、abort、UAF、死锁、空指针、内存泄漏线索。
- 用户说自己使用的不是最新版本,想知道问题是否已经被上游或当前仓库修复。
- 用户只有日志,没有或很少有业务代码上下文。
- 用户想区分是框架缺陷、接入方式问题,还是业务调用触发的非法输入/非法生命周期。
- 用户需要升级建议、补丁建议、规避方案或最小化复现方向。
如果用户只是泛泛询问稳定性概念、想看分类定义或想了解分析方法,而不是在定位具体问题,优先引用稳定性文档,不要强行进入深度 triage。
最小必要输入
尽量收集这些信息,但缺失时不要停住,基于现有证据继续分析:
- crash 日志、backtrace、墓碑、异常消息。
- hlog、hilog、启动日志、生命周期日志。
- 使用的 RNOH / RN Harmony 版本、分支名、提交号,或者至少是大版本,例如 0.72 / 0.77 / 0.82。
- 触发场景:启动、reload、页面退出、图片加载、动画、滚动、TurboModule 调用、字体注册、文本布局等。
- 当前工作区里的框架代码。
如果输入不完整,按下面优先级补信息:
- 先补最能改变分类判断的信息,例如 FaultLog、崩溃线程、异常类型、冻屏时线程栈。
- 再补最能改变归因判断的信息,例如版本号、触发时机、模块名、复现路径。
- 最后再补用于精确命中的信息,例如符号化结果、具体提交范围、更多 hilog。
如果用户只给了很少的信息,至少也要从现象、线程、模块、触发阶段四个维度做一次初步收敛,并说明当前结论的置信度。
核心原则
- 先证据,后判断。不要因为“看起来像框架问题”就直接定性。
- 不要臆造业务代码实现细节。没有用户代码时,只能基于日志、调用栈、接口契约和框架代码推断。
- 先查历史稳定性修复,再看当前代码。很多问题在旧版本已修复,直接给升级或回捞补丁建议,价值最高。
- 相似不等于同一问题。只有当崩溃阶段、模块、异常形态、触发条件至少大体一致时,才能认为“高相似”。
- 如果证据不足,允许输出“暂不能确定”,但必须说明还缺什么。
- 伙伴定位问题时,优先给“最短路径”。不要一上来列很长排查清单,而要先指出当前最值得查的模块、最相关的历史修复和最关键的下一步。
- 如果用户提供了小版本号,历史问题排查必须以该版本为边界,只看同一分支中该版本之后的相关修复,不看更早版本,也不跨到其他大分支去找“类似问题”。
按问题类别的优先排查点
应用异常退出
- 先看是 CppCrash 还是 JS Crash,再决定主要看 FaultLog 还是 JS 异常栈。
- 优先抓崩溃线程、信号类型、顶层模块和触发阶段。
- 重点关注销毁后回调、初始化时序错误、异常逃逸、兼容性缺失和 release 构建差异。
应用冻屏
- 先确认进程是否仍存活,是否有 THREAD_BLOCK、APP_INPUT_BLOCK、LIFECYCLE_TIMEOUT。
- 优先看主线程、JS 线程和锁相关线程是否互相等待。
- 重点关注锁顺序、锁重入、同步等待、持锁回调和错误线程访问。
内存异常
- 先区分 OOM 和地址越界,不要把所有内存问题都当成泄漏。
- 如果是崩溃,优先看 UAF、悬空指针、空指针、越界读写。
- 如果是增长,优先判断是短时突增还是长期累积,再看对象生命周期和释放闭环。
资源泄漏
- 优先确认异常增长的是句柄、线程、请求、监听器还是长期持有对象。
- 重点关注 callback 未清理、listener 未解绑、cache 未释放、线程未回收、实例销毁后仍被持有。
- 要明确说明“资源泄漏”与“内存异常”可能相关,但不应混为同一结论。
建议工作流
第 1 步:提取问题画像
先从日志里提取最关键的事实:
- 崩溃线程:JS 线程、UI 线程、Worker 线程、主线程。
- 崩溃信号或错误:SIGSEGV、SIGABRT、abort、mapping error、undefined error、deadlock、null pointer、UAF。
- 顶层栈帧所属模块:如 ImageComponentInstance、JSVMRuntime、HostObjectProxy、FontRegistry、AnimatedNodesManager、ArkUINode、ModalHostView。
- 触发阶段:启动、reload、销毁、关闭、图片回调、布局提交、动画帧、滚动结束、字体注册、TM 回调。
- 版本范围:旧分支还是当前主分支。
先把这些事实整理成一个“问题画像五元组”:
- 现象:闪退 / 卡死 / OOM / 泄漏。
- 类别:应用异常退出 / 应用冻屏 / 内存异常 / 资源泄漏。
- 线程或错误:JS 线程、主线程、SIGSEGV、APP_INPUT_BLOCK 等。
- 模块:ImageComponentInstance、JSVMRuntime、FontRegistry、ArkUINode 等。
- 触发阶段:启动、reload、销毁、动画、图片回调、布局提交等。
后续所有判断都围绕这五项展开,避免分析越写越散。
第 2 步:先查历史修复
优先读取 references/history-index.md。
用途:
- 快速把问题映射到稳定性类别。
- 识别高频历史修复主题和关键词。
- 决定是否进一步打开工作区里的原始汇总文档。
如果索引显示高度相似,再按需打开工作区中的原始文档:
- docs/zh-cn/稳定性/稳定性历史修复/0.72稳定性修复汇总/
- docs/zh-cn/稳定性/稳定性历史修复/0.77稳定性修复汇总/
- docs/zh-cn/稳定性/稳定性历史修复/0.82稳定性修复汇总/
优先使用下面顺序检索,而不是无差别翻所有历史文档:
- 先看 references/history-index.md 的类别、关键词和高频模块。
- 再根据用户版本确定检索边界。
- 最后在边界内做全量扫描,而不是只挑典型条目。
如果用户同时提供了 GitCode PR / MR 链接,按下面优先级读取:
- 先检查 references/gitcode-config.json 是否存在有效
gitcode_access_token。 - 如果有有效 token,优先调用 GitCode API 获取 PR 详情、文件列表和 patch。
- 如果没有有效 token,则退回到公开 PR 页面抓取、
git fetch或本地 diff 分析。 - 如果 PR 非公开且缺少有效 token,要明确告诉用户当前无法完整读取 PR 详情,并继续基于已有日志、版本和本地代码做分析。
历史修复检索边界
按下面规则限制历史问题检索范围:
- 如果用户只提供大版本,例如 0.72 / 0.77 / 0.82,可以先看该分支的全部相关历史修复。
- 如果用户提供了小版本,例如 0.72.53 / 0.77.18 / 0.82.3,必须只看同一分支里该小版本之后的相关修复。
- 不要看该小版本之前的历史问题,因为它们通常已经包含在用户当前版本中,不能作为“后续已修复”的证据。
- 不要跨分支查找,例如用户是 0.72.53,就不要去看 0.77 和 0.82 的历史修复来直接下结论。
例如:
- 用户版本是 0.72.53,只看 0.72 分支中 0.72.53 之后的相关修复;不看 0.72.53 之前的条目,也不看 0.77、0.82。
- 用户版本是 0.77.18,只看 0.77.18 之后的 0.77 分支相关修复;不看 0.72,也不看 0.82。
只有在用户明确要求做跨分支演进分析,或者要判断“后续大版本是否继续修过同类问题”时,才允许把其他分支作为辅助参考;这时也必须明确标注为“后续分支的相似修复”,不能直接当作当前分支已命中的结论。
历史修复全量扫描要求
在确定分支和版本边界后,必须做边界内的全量相关扫描:
- 先按问题类别定位到对应 README 和分类文件。
- 再把边界内所有相关条目逐条过一遍,至少比较问题描述、影响模块、问题类型、触发时机。
- 不要只看索引里的代表性问题,也不要只挑标题最像的 1 到 2 条。
- 最终输出时,可以只列最相关的 1 到 3 条,但这个结论必须建立在已经做过全量扫描的前提上。
优先查看对应分类文件和 README 表格中的条目,确认:
- 问题描述是否相近。
- 影响模块是否重合。
- 问题类型是否一致。
- 触发时机是否一致。
- 是否存在直接可引用的提交、MR、版本号。
匹配等级按下面标准给出:
- 高匹配:模块、触发阶段、异常形态都相近,且历史条目描述能直接解释当前日志。
- 中匹配:问题家族一致,但触发条件或堆栈细节有差异。
- 低匹配:只是在大类上相似,不能当作同一问题。
第 3 步:结合当前框架代码验证
如果工作区里有框架代码,继续验证最近代码状态。重点关注:
- packages/react-native-harmony/
- packages/react-native/
- packages/react-native.patch
- 与日志命中的模块、类名、函数名直接相关的位置
验证时优先搜这些内容:
- 日志中直接出现的类名、函数名、错误字符串。
- 历史修复条目中的模块名、提交标题关键词和防护模式。
- 生命周期相关字段,例如 destroyed、isValid、weak_ptr、callback cleanup、removeListener。
- 并发相关字段,例如 mutex、scoped_lock、reportMount、runOnQueue、main thread、UI thread。
验证时重点看这些模式:
- 生命周期保护是否存在:销毁标记、弱引用、空指针保护、回调解绑、observer 反注册。
- 并发保护是否存在:锁顺序、锁作用域、线程切换、是否在错误线程访问对象。
- 资源释放是否闭环:回调、HTTP 请求、JSVM 引用、code cache、事件监听器。
- 异常边界是否完整:空值检查、越界保护、异常捕获、非法状态兜底。
- 平台兼容是否有防护:API12 兼容、混淆规避、符号冲突处理、bundle 加载路径适配。
如果最新代码里已经存在明显保护,而用户版本较旧,这通常支持“历史已修复、建议升级或回捞补丁”的结论。
第 4 步:判断归因
归因只能分成三类:用户代码问题、框架问题、证据不足。
先给倾向,再给证据,再给置信度。不要只写一句“更像框架问题”。
更像框架问题的信号
- 崩溃栈主要落在框架内部模块,且与历史稳定性修复条目高度相似。
- 问题发生在销毁、reload、回调晚到、跨线程访问、锁重入、资源释放这类框架常见薄弱点。
- 即便没有业务代码,日志已经能显示空指针/UAF/死锁发生在框架内部对象管理上。
- 当前最新代码里能看到针对同类问题新增的保护逻辑,而用户版本可能尚未包含。
更像用户代码问题的信号
- 崩溃由非法入参、错误生命周期调用、异常自定义组件命名、错误线程使用方式、重复初始化或不满足接口契约触发。
- 日志明确落到业务侧 JS/ArkTS/原生模块,并且框架栈只是被动承接。
- 历史修复中没有相似框架缺陷,反而已有约束说明或已知接入要求。
证据不足时的写法
不要硬判。应该写:
- 当前更偏向哪一侧。
- 支持这个倾向的证据。
- 缺少哪些材料会影响最终定性。
- 当前置信度是高 / 中 / 低。
- 如果当前置信度较低,要明确建议下一步补充哪些信息;如果用户有可复现的问题环境,也要建议在必要的模块、生命周期节点或回调链路上增加日志,再基于新增日志继续分析。
第 5 步:给出修复建议
建议按优先级从高到低输出,优先给可执行项:
-
升级建议:明确指出升级到哪个版本,并提供对应 PR/MR 链接。
- 例如:升级到 0.82.3 或以上版本,包含修复 !1985。
-
补丁回捞建议:如果暂时无法升级,给出具体的提交号和 MR 号,便于回捞特定补丁。
-
规避方案:如果暂时不能升级或回捞补丁,给规避方案,例如避免某类触发路径、增加判空、避免销毁后回调、修正线程模型。
-
业务接入检查:如果怀疑业务接入问题,指出需要自查的接口契约和调用时机。
-
诊断建议:如果证据不足,明确建议补哪些日志、哪些符号化信息、哪些复现场景。
- 如果存在问题环境且能够改代码,优先建议在最可疑的模块、线程切换点、生命周期边界、销毁路径、回调入口出口等必要位置增加日志,而不是无差别大量打印。
如果当前只能给出一个建议,优先给“最可能立刻缩小范围”的建议,而不是罗列所有可能动作。
输出格式
除非用户明确要求别的格式,否则按下面结构输出:
稳定性问题分析
1. 结论摘要
- 用 2 到 4 句话总结问题画像、当前倾向和置信度。
- 第一段就要明确问题类别、触发阶段和最可疑模块,不要把关键信息埋到后文。
2. 历史修复匹配
- 是否命中历史修复:命中 / 部分命中 / 未命中。
- 如果命中,列出相关版本、问题名、模块、问题类型、为什么相似。
- 必须提供对应的提交链接和 MR/PR 链接,格式为:
- 如果引用了原始汇总文档,给出对应文档路径和条目锚点。
3. 归因判断
- 结论:更像框架问题 / 更像用户代码问题 / 暂时无法定性。
- 原因:用证据支撑,不要只给结论。
4. 关键证据
- 摘出最关键的日志特征、栈帧、代码保护点或缺失点。
- 如果是冻屏或泄漏,关键证据也可以是线程状态、资源增长趋势或未释放链路,而不一定是崩溃栈。
5. 修复建议
6. 仍需补充的信息
- 只列真正会改变判断的信息。
- 最多列 3 项,并按优先级排序。
- 如果当前结论可信度较低,这一节必须给出明确下一步:补充更多信息,或者在问题环境的关键位置增加日志后重新分析。
历史修复匹配时的引用要求
如果确认或高度怀疑属于历史已修复问题,至少给出这些信息:
- 对应分支和版本。
- 问题标题。
- 提交号(带 GitCode 链接)和 MR 号(带 GitCode 链接)。
- 该修复解决的根因摘要。
- 为什么它能解释当前日志。
链接格式要求:
- 提交链接:
[提交号](https://gitcode.com/OpenHarmony-RN/ohos_react_native/commit/提交号) - MR/PR 链接:
[!MR号](https://gitcode.com/OpenHarmony-RN/ohos_react_native/merge_requests/MR号)
例如:
如果无法确认是同一问题,要明确写“相似问题家族”而不是“就是同一个问题”。
常见问题到类别的映射
- 应用异常退出:优先覆盖 CppCrash 和 JS Crash,重点关注 SIGSEGV、SIGABRT、abort、undefined error、bundle load failure、异常逃逸、生命周期结束后继续回调等问题。
- 应用冻屏:优先覆盖线程阻塞、输入阻塞、生命周期超时,重点关注死锁、锁顺序不一致、锁重入、错误线程访问、同步等待和卡死。
- 内存异常:优先覆盖 OOM 和地址越界,重点关注 UAF、悬空指针、裸指针、空指针、越界访问、mapping error、非法释放和对象生命周期错配。
- 资源泄漏:优先覆盖句柄泄漏、线程泄漏和长期未释放资源,重点关注监听器未注销、线程未回收、cache 未释放、请求回调未清理和长期持有上下文对象。
质量要求
- 用中文输出。
- 结论要分层:已知事实、推断、建议不能混在一起。
- 不要只给"建议升级"这种空话;要说明升级或补丁为什么相关,并必须包含对应的提交链接和 MR 链接。
- 如果用户没有业务代码,避免把责任直接甩给用户。
- 如果日志直接显示框架内部同类缺陷,也不要因为“用户代码不可见”就回避框架归因。
- 面向伙伴输出时,优先保证“能指导下一步动作”,其次才是面面俱到。
- 如果命中历史修复,优先给“为什么像”“差异在哪”“建议先升级还是先回捞”。
- 如果没命中历史修复,也要明确给出当前最可疑的模块或链路,避免输出停留在大类层面。
- 如果最终判断可信度较低,不能只写“信息不足”;必须给出建议补充的信息,必要时建议在问题环境中的关键位置增加日志。