bug-diagnosis.md 14 KB

Bug诊断

输入要求

  1. 错误信息(必填):错误日志、异常堆栈或报错截图
  2. 相关代码(必填):出错的代码片段
  3. 运行环境(可选):操作系统、语言版本、依赖版本
  4. 复现步骤(可选):如何触发这个错误

输出示例

## Bug诊断报告

**错误类型**:运行时(白屏 500)
**错误信息**:Uncaught TypeError: Cannot access offset of type bool
**发生位置**:e/class/functions.php / GetFcache / 第 204 行

## 根因分析
**问题描述**:反序列化缓存后未做类型校验,直接当下标访问。
**详细分析**:`unserialize` 失败返回 `false`(bool);旧缓存格式非数组时,直接 `$cache['key']` 在 PHP 8.1+ 触发 TypeError。
**相关代码**:
```php
$cache = unserialize($str);
return $cache['key'];   // unserialize 失败返 false(bool) 时当下标访问报 TypeError
```

## 修复方案
```php
$cache = unserialize($str);
if (!is_array($cache)) return '';
return $cache['key'] ?? '';
```

## 验证方法
1. `php -l` 语法通过
2. 用旧格式缓存触发该路径,确认不再白屏

## 预防措施
- 反序列化结果先做 is_array 校验再访问下标

执行流程

第一步:Spec场景检查

诊断前先检查相关spec中的scenario:

  • 如有spec:确认bug是否涉及spec中的scenario,检查scenario的Given/When/Then是否被正确实现
  • spec与实现不一致:可能是spec理解偏差导致的bug
  • 无spec:诊断后建议补充spec防止同类bug

    ✓ Spec场景检查:
    - 相关Scenario: [scenario名称]
    - 预期行为: [spec定义]
    - 实际行为: [观察到的行为]
    - 偏差分析: [spec vs 实现]
    

第二步:已知问题检查

检查项目记忆中的已知问题:

  • 搜索项目记忆中的类似bug记录
  • 检查是否有已知的workaround
  • 发现新问题:诊断完成后提示记录到项目记忆

第三步:错误解析

  • 识别错误类型(语法错误、运行时错误、逻辑错误、环境错误)
  • 提取关键信息(错误码、错误消息、发生位置)
  • 分析堆栈跟踪(调用链、最后执行位置)

第四步:根因分析

错误类型 常见根因 分析方法
语法错误 拼写错误、括号不匹配、缩进错误 定位到具体行号和字符
运行时错误 空指针、数组越界、类型错误 检查变量状态和数据流
逻辑错误 条件判断错误、算法缺陷 追踪执行路径和中间结果
环境错误 依赖缺失、版本不兼容、配置错误 检查环境和依赖信息
并发错误 竞态条件、死锁、资源竞争 分析线程/协程交互

推断交叉验证规则

根因分析中,AI 推断只能作为候选假设,不能直接作为结论交付。每个候选根因至少匹配一种验证证据:

证据类型 适用场景 验证方式
日志/堆栈 报错、异常、线上问题 对齐错误时间、调用链、文件行号和错误码
复现测试 稳定可触发的功能缺陷 构造最小输入,先复现失败,再验证修复后通过
源码路径 逻辑错误、数据流异常 从入口追踪到出错分支,说明变量状态变化
环境/版本 依赖、运行时、配置差异 对比语言版本、依赖版本、配置项和平台差异
外部权威源 框架/API/数据库行为不确定 引用官方文档、发行说明或已验证的项目内规范
运行证据 性能、并发、资源泄漏 使用 benchmark、profile、EXPLAIN、线程/堆/GC 日志验证

如果证据不足,必须输出"最可能原因"和"待验证动作",不得写成已确认根因。若多个候选根因都符合症状,按可能性排序并给出最小排除步骤。

根因判定门禁(先判后修)

给出修复方案前,必须先回答:这是根因(root cause)还是只打补丁(symptom patch)? 禁止未区分就直接改码。

  • 根因判定信号:修复是否能消除所有同类触发路径?还是只在当前输入/当前位置挡住了症状?
  • 打补丁的识别:若修改只针对"这个报错这次没出现"(如加一个 if (x) return),而未说明为什么会进入该状态,即为补丁,必须继续追问上游成因。
  • 强制追问链:每发现一个补丁式修复,必须写下"为何会进入该非法状态",直到追溯到真正成因或明确标注"上游根因待查"。
  • 图/状态机类 Bug:涉及图遍历、环检测、状态机、依赖解析的缺陷,先建立方向语义模型(边方向、遍历方向、状态迁移是否解耦)再判定根因,不得仅凭"跑通了"判定修复有效。

命中"只打补丁"时,在报告中单独标注「补丁风险」,并给出上游根因的最小排查动作。

第五步:定位问题

  • 指出具体的代码位置(文件、函数、行号)
  • 说明变量在错误时刻的状态
  • 追踪数据流找到问题源头

第六步:提供修复(Surgical Changes)

  • 给出修复后的代码
  • 说明修复原理
  • Karpathy约束:只修改修复Bug必需的代码,不改相邻代码、风格、注释
  • 提供验证修复的方法

第七步:定义成功标准(Goal-Driven Execution)

基于已确定的根因和修复方案,明确定义"修复成功"的标准:

  • 复现测试:写一段能稳定触发Bug的测试/步骤
  • 成功标准:修复后应达到什么状态
  • 验证方法:如何确认修复有效

示例:

成功标准:用户邮箱为空字符串时,validate_user() 抛出 ValueError 而非崩溃。
复现测试:调用 validate_user({'email': ''}) → 应抛出 ValueError。
验证方法:运行复现测试 → 通过。

第八步:记录到项目记忆

修复完成后,将关键决策和变更记录到项目记忆(参见 project-memory-management.md):

  • 记录 Bug 根因和修复方案(Decision Record 模式)
  • 记录新增的预防措施和编码规范(Convention Capture 模式)
  • 更新已知问题清单,防止同类 Bug 复发

实战请求示例

示例一:PHP/CMS 白屏或 500

帮我诊断这个 CMS 后台 500。
环境:EmpireCMS 7.5,PHP 8.2,MySQL 5.7。
现象:进入 /e/admin 后白屏,前台正常。
错误信息:贴出 php_errors.log 中最新 30 行。
最近变更:刚改过后台插件 action_xxx.php。
要求:先判断可能原因,不要直接改缓存文件;给出最小复现和修复验证命令。

优先加载:bug-diagnosis.md、cms-development.md、code-review.md。

示例二:接口偶发超时

帮我定位订单列表接口偶发超时。
环境:Laravel 10,PHP 8.2,MySQL 8.0。
现象:keyword 为空时正常,带手机号搜索偶尔超过 5 秒。
已有证据:贴出慢查询 SQL、接口参数、表行数、EXPLAIN 结果。
要求:区分已确认根因和待验证假设;不要直接加索引,先说明索引影响和回滚方案。

优先加载:bug-diagnosis.md、mysql-database.md、performance-benchmark.md。

输出格式

## Bug诊断报告

**错误类型**:[语法/运行时/逻辑/环境/并发]
**错误信息**:[核心错误消息]
**发生位置**:[文件/函数/行号]

## 根因分析

**问题描述**:[一句话说明]
**详细分析**:[逐步推理过程]
**相关代码**:
```[问题代码]```

## 修复方案

```[修复后的代码]```

**修复说明**:[为什么这样修复]

## 验证方法

1. [步骤1]
2. [步骤2]

## 预防措施

- [建议1]
- [建议2]

质量标准

  • 根因分析必须基于代码和日志证据,不能猜测
  • 修复代码必须可运行,且能解决原问题
  • 必须提供验证方法,让用户确认修复有效
  • 对于不确定的根因,标注"最可能的原因"并列出其他可能性
  • 环境相关错误必须说明具体的环境要求或版本限制
  • 不得建议用户"重启试试"或"重新安装"作为首要方案
  • Karpathy红线:修复前必须先写复现测试,修复后验证测试通过
  • Karpathy红线:只修改修复Bug必需的代码,不改相邻代码、风格、注释
  • 证伪门禁:每个结论交付前,先构造一个反例尝试推翻它;构造不出反例才允许交付。跑通 ≠ 正确。环检测三次没写对,根因不是没清单,而是每次写完都没人攻击它;本门禁把"跑通就交付"逼成"证伪后交付"

CMS 常见 Bug 模式

当诊断对象为 PHP+MySQL CMS 时,优先排查以下模式:

Bug 类型 典型表现 常见根因 修复方向
PHP 版本兼容 Fatal error: Uncaught TypeError / Deprecated: 低版本代码运行在高版本 PHP 参见 cms-development.md PHP 8.x 兼容性检查表
CMS 缓存失效 修改代码后页面无变化 CMS 模板/数据缓存未清理 清理 runtime/ e/tmp/ data/dbcache/ 后重试
模板引擎错误 标签不解析或输出原始标签 模板语法错误或引擎版本差异 检查模板标签闭合和 CMS 版本对应语法
数据库表前缀 Table 'xxx_tablename' doesn't exist 表前缀配置与实际不一致 检查 CMS 配置文件中的表前缀设置
MySQL 慢查询 页面卡顿 / SQL 超时 / CPU 飙高 缺索引、全表扫描、排序临时表 采集 SQL、参数、表行数和 EXPLAIN,参考 mysql-database.md
MySQL 死锁 Deadlock found when trying to get lock 事务顺序不一致或锁范围过大 缩短事务、统一更新顺序、检查索引命中
序列化数据 unserialize(): Error at offset WordPress wp_options 等序列化数据损坏 用 maybe_unserialize() 或修复序列化字符串长度
伪静态路由 404 或路由不生效 .htaccess / Nginx rewrite 规则缺失 检查伪静态规则和 CMS 路由配置
文件编码 中文乱码 / headers already sent UTF-8 BOM / GBK 编码混用 统一 UTF-8 无 BOM,用二进制方式读写文件

详见 cms-development.md。

关联 reference

  • karpathy-coding-guidelines(Karpathy编码规范)— 修复前可用 karpathy-coding-guidelines 确认修复策略符合精准修改原则
  • code-review(代码审查)— 修复后可用 code-review 检查是否引入新问题
  • test-generation(测试用例生成)— 修复后可用 test-generation 生成回归测试
  • code-generation(代码生成)— 复杂修复可用 code-generation 生成替代实现
  • cms-development(CMS二次开发)— PHP+MySQL CMS 场景参考 cms-development.md 的兼容性检查和常见 Bug 模式
  • mysql-database(MySQL数据库)— 数据库报错、慢查询、死锁、表结构和索引问题参考 mysql-database.md
  • project-memory-management(项目记忆管理)— 记录 Bug 根因、修复方案和预防措施

失败回退机制

步骤 失败条件 回退目标 最大重试 不可恢复时升级路径
第一步:Spec场景检查 无spec或spec与实现不一致 基于代码和错误信息诊断,标注"无spec对照" 1 诊断后建议走 Spec驱动开发 补全spec防止同类bug
第二步:已知问题检查 项目记忆不可用或无相关记录 跳过已知问题检查,标注"记忆未加载" 1 基于当前信息独立诊断,诊断完成后提示手动记录
第三步:错误解析 错误信息不完整,无法分类 基于已有信息匹配最接近的错误分类 2 告知所需最小信息集(错误消息+触发操作),等待补充
第四步:根因分析 多种可能根因均符合症状,无法唯一确定 列出候选根因,按可能性排序 2 输出诊断矩阵(候选根因×验证方法),由用户逐一排除
第五步:定位问题 涉及第三方库/外部服务,代码不可审视 标注第三方边界,定位调用链最近的可控点 1 给出workaround方案,建议向第三方提issue
第六步:提供修复(Surgical Changes) 修复方案需大规模重构(超出Bug修复范围) 给出最小修复+重构建议供后续规划 2 拆分为Bug修复(立即)和架构优化(单独任务)
第七步:定义成功标准(Goal-Driven Execution) 修复涉及多系统联调,单次验证不充分 定义分层验证标准(单元→集成→端到端) 1 给出回归测试checklist,建议灰度发布验证
第八步:记录到项目记忆 项目记忆系统不可用 输出Decision Record到本地文件 1 标注"记忆未沉淀",提示用户手动保存