Bug Detective 是一个系统化调试工作流技能,用于调查和解决代码错误、异常与失败。它提供结构化的调试方法和常见错误模式识别。
核心哲学
调试是一个科学的问题解决过程,需要:
- 理解问题:清晰定义症状和预期行为
- 收集证据:收集错误消息、日志、堆栈跟踪
- 形成假设:根据证据推断可能原因
- 验证假设:通过实验确认或排除原因
- 解决问题:应用修复并验证
调试工作流
第 1 步:理解问题
开始调试前,明确以下信息:
- 完整错误消息内容
- 错误的确切位置(文件名和行号)
- 复现步骤(如何触发错误)
- 预期行为与实际行为
- 环境信息(操作系统、版本、依赖)
问题模板:
- 确切的错误消息是什么?
- 错误发生在哪个文件和行?
- 如何复现此问题?提供详细步骤。
- 预期结果是什么?实际发生了什么?
- 最近哪些更改可能引入了此问题?
第 2 步:分析错误类型
根据错误类型选择调试策略:
- 语法错误:代码无法解析,检查语法、括号匹配、引号
- 导入错误:ModuleNotFoundError,检查模块安装、路径配置
- 类型错误:TypeError,检查数据类型、类型转换
- 属性错误:AttributeError,检查对象属性是否存在
- 键错误:KeyError,检查字典键是否存在
- 索引错误:IndexError,检查列表/数组索引范围
- 空引用:NoneType/NullPointerException,检查变量是否为 None
- 网络错误:ConnectionError/Timeout,检查网络连接、URL、超时设置
- 权限错误:PermissionError,检查文件权限、用户权限
- 资源错误:FileNotFoundError,检查文件路径是否存在
第 3 步:定位问题源
使用以下方法定位问题:
- 二分查找法:注释掉一半代码,检查问题是否仍然存在;逐步缩小范围直到找到问题代码
- 日志追踪:在关键位置添加 print/logging 语句;跟踪变量值变化;确认代码执行路径
- 断点调试:使用调试器断点功能;单步执行代码;检查变量状态
- 堆栈跟踪分析:从错误消息的堆栈跟踪中找到调用链;确定错误的直接原因;追溯到根本原因
第 4 步:形成并验证假设
假设框架: 假设:[问题描述] 导致 [错误现象] 验证步骤:
- [验证方法 1]
- [验证方法 2] 预期结果:
- 如果假设正确:[预期现象]
- 如果假设错误:[预期现象]
第 5 步:应用修复
修复后验证:
- 原始错误已解决
- 未引入新错误
- 相关功能仍正常工作
- 添加测试以防止回归
Python 常见错误模式
- 缩进错误
- 可变默认参数
- 循环中的闭包问题
- 迭代时修改列表
- 使用 is 进行字符串比较
- 忘记调用 super().init()
JavaScript/TypeScript 常见错误模式
- this 绑定问题
- 异步错误处理
- 对象引用比较
Bash/Zsh 常见错误模式
- 空格问题
# ❌ 赋值中不允许有空格
name = "John" # 错误:尝试运行 'name' 命令
# ✅ 正确的赋值
name="John"
# ❌ 条件测试中缺少空格
if[$name -eq 1]; then # 错误
# ✅ 正确
if [ $name -eq 1 ]; then
- 引号问题
# ❌ 单引号内变量不会展开
echo 'The value is $var' # 输出:The value is $var
# ✅ 使用双引号
echo "The value is $var" # 输出:The value is actual_value
# ❌ 使用反引号进行命令替换(容易混淆)
result=`command`
# ✅ 使用 $()
result=$(command)
- 未加引号的变量
# ❌ 未加引号的变量,空值会导致错误
rm -rf $dir/* # 如果 dir 为空,删除当前目录下所有文件
# ✅ 始终引用变量
[ -n "$dir" ] && rm -rf "$dir"/*
# 或使用 set -u 防止未定义变量
set -u # 或 set -o nounset
- 循环中的变量作用域
# ❌ 管道创建子 shell,外部变量不变
cat file.txt | while read line; do
count=$((count + 1)) # 外部 count 不会改变
done
echo "Total: $count" # 输出 0
# ✅ 使用进程替换或重定向
while read line; do
count=$((count + 1))
done < file.txt
echo "Total: $count" # 正确输出
- 数组操作
# ❌ 不正确的数组访问
arr=(1 2 3)
echo $arr[1] # 输出 1[1]
# ✅ 正确的数组访问
echo ${arr[1]} # 输出 2
echo ${arr[@]} # 输出所有元素
echo ${#arr[@]} # 输出数组长度
- 字符串比较
# ✅ 在 POSIX `[` 测试中使用 `=`,在 Bash `[[ ]]` 测试中使用 `==`
if [ "$name" = "John" ]; then
if [[ "$name" == "John" ]]; then
# ❌ 使用 -eq 进行数值比较而不是 =
if [ $age = 18 ]; then # 错误
# ✅ 使用算术运算符进行数值比较
if [ $age -eq 18 ]; then
if (( age == 18 )); then
- 命令失败后继续执行
# ❌ 命令失败后继续执行
cd /nonexistent
rm file.txt # 删除当前目录中的 file.txt
# ✅ 使用 set -e 在错误时退出
set -e # 或 set -o errexit
cd /nonexistent # 脚本在此退出
rm file.txt
# 或检查命令是否成功
cd /nonexistent || exit 1
常用调试命令
Python pdb 调试器
python -m pdb script.py
pytest -x -vv tests/test_target.py
Node.js Inspector
node --inspect-brk app.js
node --trace-warnings app.js
Git Bisect
git bisect start
git bisect bad
git bisect good <known-good-commit>
Bash 调试
# 以调试模式运行脚本
bash -x script.sh # 打印每个命令
bash -v script.sh # 打印命令源代码
bash -n script.sh # 语法检查,不执行
# 在脚本内启用调试
set -x # 启用命令跟踪
set -v # 启用详细模式
set -e # 错误时退出
set -u # 未定义变量时报错
set -o pipefail # 管道中任何命令失败则失败
预防性调试
- 使用类型检查
- 输入验证
- 防御性编程
- 日志记录
调试检查清单
开始前
- 获取完整错误消息
- 记录错误的堆栈跟踪
- 确认复现步骤
- 理解预期行为
调试中
- 检查最近的代码更改
- 使用二分查找定位问题
- 添加日志跟踪变量
- 验证假设
解决后
- 确认原始错误已修复
- 测试相关功能
- 添加测试以防止回归
- 记录问题和解决方案
附加资源
参考文件
- references/python-errors.md - Python 错误详情
- references/javascript-errors.md - JavaScript/TypeScript 错误详情
- references/shell-errors.md - Bash/Zsh 脚本错误详情
- references/debugging-tools.md - 调试工具使用指南
- references/common-patterns.md - 常见错误模式
示例文件
- examples/debugging-workflow.py - 完整调试工作流示例
- examples/error-handling-patterns.py - 错误处理模式
- examples/debugging-workflow.sh - Shell 脚本调试示例