HowToDebug

tags: Method

问题的核心

  • 山重水复疑无路(One Main Method)
    • 调试不仅是排除缺陷
      • Steps
        • 弄清楚软件为什么会运行有问题
        • 修复问题
        • 避免破坏其他部分
        • 保持或者提高代码的总体质量
          • 可读性
          • 架构
          • 测试覆盖比率
          • 性能
        • 确保同样的问题不会在其他地方发生,也不会再次发生
      • Important
        • 查明问题的根本原因
    • 实证方法
      • 构建实验,观察結果
    • 核心调试过程
      • 问题重现
        • 找一个可靠并简洁的方式来按需求重现问题
      • 问题诊断
        • 提出假设,并通过实验来测试它们,直到找出引起缺陷的潜在原因
      • 缺陷修复
        • 设计和进行一些修改来修复问题,不要引入回归问题,保持和提高软件的整体质量
      • 反思
        • 吸取教训
          • 哪里出了问题
          • 是否还有其他类似问题
          • 如何才能确保同样的问题不再发生
      • 调试是一个反复的过程
    • Before you debug
      • 需要明白的问题
        • 需要知道要找的是什么
          • 发生了什么,应该发生什么
        • 一次一个问题
        • 先检查简单的事情
    • HowToDo
      • 一定需要做到
        • 找到软件运行异常的原因
        • 修复问题
        • 避免破坏其他程序
        • 保持或提高软件的整体质量
        • 确保不在其他地方发生同样的问题,确保这种问题不重复发生
      • 利用软件自身来告诉你发生了什么
      • 每次解决一个问题
      • 确保你知道自己要找的是什么
        • 正在发生什么
        • 应该发生什么
    1. 重现问题
    2. 重现第一,提问第二
      • 明确开始要做的事
      • 抓住重点
        • 软件本身
        • 软件的运行环境
        • 提供的输入
    3. 控制软件
      • 自动化构建
      • 版本正确
    4. 控制环境
      • 影响软件运行的各种要素
      • Tools
        • 硬件抽象
        • 虚拟机
    5. 控制输入
      • 推測可能的输入
        • 回溯工作
        • 探测可能的输入
          • 边界值分析
          • 分支覆盖
        • 利用错误的条件
        • 引入随机性
          • 生成模糊器
          • 变异模糊器
      • 记录输入值
        • Log
      • 负载与压力
        • Log
        • 负载测试工具
    6. 改进问题的重现
      • 最小化反馈周期
        • 目标:快速地运行大量的实验
        • 尽可能简单
        • 最大限度地减小所需的时间
      • 将不确定的缺陷变为确定的
        • 开始于不可知的初始状态
          • AllocTool
        • 与外部系统进行交互
          • 精确控制从外部系统接收了什么
            • Log
        • 故意使用随机性
          • Log
        • 多线程
      • 自动化
        • Test
        • Log
      • 迭代
        • 改进重现
        • http://pic.yupoo.com/qianjigui/CNPWDYCM/medish.jpg
    7. 如果真的不能重现问题该怎么办
      • 缺陷真的存在吗
        • 与提出问题的成员交流
      • 在相同的区域解决不同的问题
      • 让其他人参与其中
      • 充分利用用户群体
      • 推测法
    8. HowToDo
      • 在做任何事情前找到问题重现的方法
      • 确认你运行的版本和提出缺陷的版本是相同的
      • 复制报告缺陷时使用的环境
      • 确定重现缺陷需要的输入数据
        • 推測数据
        • log
      • 通过反复优化确保你重现问题的程序既方便又可靠
        • 减少运行步骤、数据量或需要的时间
        • 去除不确定性
        • 自动化
    1. 诊断
    2. 不要急于动手——试试科学的方法
      • 兼顾创造性与严密性
      • 不同类型的实验
        • 检查软件内部状态的某个方面
          • Run
          • Run by debug
        • 改变软件运行的某个方式
          • IO
          • Arguments
        • 改变软件本身编码的逻辑
      • 实验必须起到验证的作用
      • 每次只做一个修改
      • 记录所做的调试
        • 定期回顾已经尝试的实验和学到的东西
      • 不要忽略任何细节
    3. 相关策略
      • 插桩
        • log
      • 分而治之
      • 利用源代码控制工具
      • 聚焦差异
      • 向他人学习
      • 奥卡姆的剃刀
        • 其它条件相同的情况下,最简单的解释是最好的
    4. 调试器
    5. 陷阱
      • 你做的修改是正确的吗
      • 验证假设
      • 多重原因
      • 流沙
        • 面对一个不断变化的系统,停下手头工作并弄清楚是什么在变化,为什么变化
    6. 思维游戏
      • 旁观调试法
        • 解释问题会帮助你理清思路
      • 角色扮演
      • 换换脑筋
        • 让潜意识帮助你
      • 做些改变,什么改变都行
      • 福尔摩斯原则
        • 当你排除了一切不可能后,无论结论是什么,它也一定是真相
      • 坚持
    7. 验证诊断
      • 向其他人解释你的诊断。
      • 检查源代码的原始副本
      • 用其它方式验证你的诊断
      • 多和他人讨论
    8. HowToDo
      • 构建假设,并用实验来测试它们
        • 确保自己明白你的实验要说明什么
        • 每次只做一个修改
        • 把你尝试的工作记录下来
        • 不要忽略任何事物
      • 当事情并不顺利的时候
        • 如果你做的修改似乎没有产生效果,那么你没有改到点子上
        • 验证你的假设
        • 你是否面临多个有内存联系的原因或一个不断变化的基本系统
      • 验证你的诊断
    1. 修复缺陷
      1. 清除障碍
      2. 从一个干净的代码树开始
      1. 测试
      2. 首先确保所有的测试都是通过的
      3. 在设计你的修复前,确保你已经知道如何进行测试了
      1. 修复问题产生的原因,而非修复现
      1. 重构
      2. 修改与重构不要同时进行
      1. 签入
      2. 一次逻辑修改只做一次签入
      3. 在checkin前进行比较
      1. 审查代码
    2. HowToDo
      • 缺陷修复的目标
        • 修复问题
        • 避免引入回归
        • 维持或提高代码的整体质量
      • 从一个干净的源代码树开始
      • 确保通过测试后再做修改
      • 明确在做出更改前如何进行测试
      • 针对缺陷的原因而不是现象进行修复
      • 要做重构,但不要与功能修改同时进行
      • 一次逻辑修改只做一次签入
    1. 反思
    2. 这到底是怎么搞的
    3. 哪里出了问题
      • 我们已经做到了吗
      • 根本原因分析
        • 原因归属
          • 需求
          • 架构和设计
          • 测试
          • 构造
            • Code
            • library使用
            • 工具使用
              • IDE
              • Compiler
    4. 它不会再发生了
      • 自动验证
      • 重构
      • 过程
    5. 关闭循环
      • 反馈问题
      • 记录备案
    6. HowToDo
      • 花时间进行根本原因分析
        • 在过程的哪个点上产生了错误
        • 出什么错了
      • 确保同样的问题不会再发生
        • 自动检查是否存在问题
        • 重构代码以避免不当使用
        • 和同事交谈,适当修改进程
      • 多方反馈,与其他利益相关者形成闭环

从大局看调试

  • 发现代码存在问题
    • 追踪缺陷
      • 缺陷追踪系统
      • 怎样才能写出一份出色的缺陷报告
        • 具体、明确、详细
        • 最小化的、唯一的
      • 环境和配置报告
        • AutoGain
    • 与用户合作
      • 简化流程
        • 明确说明如何报告一个流程
        • 自动化
        • 提供多种选择
          • Email
          • Talk
        • 要尽量简单
        • 模板不要太死板
        • 尊重用户的隐私
      • 有效的沟通
        • 心智模式
          • 从用户的角度去沟通
        • 和非技术人员沟通
        • 发布你的缺陷数据库
        • 提供反馈
        • 与用户交流
    • 与支持人员协同工作
    • HowToDo
      • 充分利用缺陷跟踪系统
        • 复杂度要适中,根据具体情况作出选择
        • 直接面向用户
        • 自动化环境和配置报告,以确保报告的准确性
      • 缺陷报告要达到如下目标
        • 具体的
        • 明确的
        • 详细的
        • 最小化的
        • 独特的
      • 与用户合作时
        • 尽可能地简化缺陷报告流程
        • 沟通是关键,多为用户着想
      • 与客户支持和QA团队搞好关系,以便在缺陷修复的过程中得到帮助
  • 务实的零容忍策略
    • 缺陷优先
      • 早期缺陷修复可以大大降低软件运行的不确定性
      • 没有破窗户
    • 调试的思维模式
      • 需要完美也要实用
    • 自己来解决质量问题
      • 这里没有“灵丹妙药”
      • 停止开发那些有缺陷的程序
        • SCM
        • AutoBuild
        • AutoTest
        • CI
      • 从“不干净”的代码中将“干净”的代码分离出来
        • Refactor
      • 错误分类
        • 错误分级
      • 缺陷闪电战
      • 专项小组
    • HowToDo
      • 尽早地检测缺陷,在它们刚刚出现的时候进行修复
      • 把实现无缺陷的软件当成是可以实现的目标,依此而行动,但是在要求完美的同时也要实用
      • 如果你面对的是一个很差的代码库,你需要
        • 认识到没有灵丹妙药
        • 确保基本的要素已就绪
        • 从“不干净”的代码中将“干净”的代码分离出来,并保持干净
        • 使用缺陷分类,以确保你对缺陷数据库的控制
        • 通过添加测试和进行代码重构来一步一步优化质量低下的代码

深入调试技术

  • 特殊案例
    • 修补已经发布的软件
      • 需要集中精力减少风险
    • 向后兼容
      • 确定你的代码有问题
      • 解决兼容性问题
        • 提供迁移方法
        • 实现一个兼容模式
        • 提供预警
        • 不修复缺陷
    • 并发
      • 简单与控制
      • 修复并发缺陷
    • 海森堡缺陷
      • 一个当你开始寻找的时候就会"消失"的缺陷
      • 减少信息收集对系统的影响
    • 性能缺陷
      • 寻找瓶颈
        • Profile
      • 准确的性能分析
        • 版本一致
        • 运行环境一致
        • 测试数据具有典型性
    • 嵌入式软件
      • 嵌入式调试工具
        • 仿真
        • 远程调试
        • 开发硬件
        • ICE
      • 提取信息的痛苦路程
    • 第三方软件的缺陷
      • 不要太快去指责
        • 首先怀疑自己的代码
      • 处理第三方代码的缺陷
      • 开源代码
    • HowToDo
      • 在修补已经发布的软件的时候,要集中精力减少风险
      • 在修复缺陷的过程中不断监测兼容性问题
      • 确保你已经完全关闭计时窗口,而不仅仅是缩小尺寸
      • 当遇到海森堡缺陷时,尽量降低收集信息的副作用
      • 修复性能缺陷往往始于准确的特征描述
      • 即使是最有限的沟通渠道,也足以提取你所需要的信息了
      • 面对第三方代码,先怀疑自己的代码
  • 理想的调试环境
    • 自动化测试
      • 有效的自动化测试
        • 明确说明测试結果通过与否
        • 独立
        • 运行简单
        • 全面覆盖
      • 自动化测试可以作为调试的辅助
      • 模拟测试、桩测试以及其他的代替测试技术
    • 源程序控制
      • 稳定性
      • 可维护性
      • 与分支相关的问题
      • 控制分支
    • 自动构建
      • 一键构建
      • 构建机器
      • 持续集成
      • 创建版本
        • 不同的源代码,不同的版本号
      • 静态分析
      • 使用静态分析
    • HowToDo
      • 自动化你的测试
        • 明确说明测试結果通过与否
        • 独立
        • 运行简单
        • 全面覆盖
      • 在源代码控制中,谨慎地使用分支
      • 自动化你的构建过程
        • 每当软件变化时,都进行构建和测试
        • 在每次构建中都集成静态分析
  • 让软件学会自己寻找缺陷
    • 假设和断言
      • 一个例子
      • 等一下——刚才发生了什么
      • 例子,第二幕
      • 契约,先决条件,后置条件和不变量
      • 开启或关闭断言
      • 防错性程序设计
      • 断言滥用
        • 断言不是一个错误处理机制
    • 调试版本
      • 编译器选项
        • 优化
          • -O3
        • 调试信息
          • -g
        • 边界检查
      • 调试子系统
        • GUI
        • MemoryAlloc
      • 内置控制
        • 禁用相关功能
        • 提供其他实现
    • 资源泄漏和异常处理
      • 在测试中自动抛出异常
      • 一个例子
      • 测试框架
    • HowToDo
      • 使用断言做
        • 记录和自动验证假设
        • 在产品发布的时候需有鲁棒性,在调试期間要确保软件是脆弱的
      • 创建这样一个调试版本
        • 以调试友好的方式进行编译
        • 允许关键子系统被等价的调试代码取代
        • 内嵌在诊断阶段非常有用的可控性
      • 在问题出现前检测系统问题,例如资源泄漏和异常处理问题
  • 反模式
    • 夸大优先级
    • 超级巨星
    • 维护团队
    • 救火模式
    • 重写
    • 没有代码所有权
    • 魔法
    • HowToDo
      • 控制缺陷数据库,确保它准确地反映了缺陷的优先级
      • 自负自责,任何人在完成当前任务前都不允许转向其他任务。如果在他们的工作中出现了缺陷,让他们自己来修复
      • 从最初的构思到最終布署的整个过程以及以后的维护都要一个团队完成
      • 救火模式永远不会修复质量问题。花些时间找出并修复根本原因
      • 避免彻头彻尾重写
      • 确保你的代码策略是清晰的
      • 将不理解的任何事物都当作缺陷

资源

  • SCM
    • CVS
    • svn
    • git
    • mercurial
    • bazaar
  • BugTracker
    • bugzilla
    • Trac
    • Redmine
  • Code
    • sourceforge
    • github
  • Build
    • Make
    • autoconf
    • Jam
    • Boost.Build
    • SCons
    • Ant
    • Maven
    • Capistrano
  • CI
    • CruiseControl
    • Hudson
  • Libraries
    • Test
      • JUnit
      • TestNG
    • Memory Allocator
      • libcwd
      • VC
      • Mudflap
      • Dinkumware
      • ElectricFence
    • Log
      • log4j
      • Logback
  • Tools
    • Test
      • FitNesse
      • Watir
      • Selenium
      • Sahi
      • The Grinder
      • JMeter
      • QuickTest Professional LoadRunner
      • Peach Fuzzing Platform
      • RFuzz
    • Running Analysis
      • Valgrind
        • Memory
        • Performance Profile
      • BoundsChecker
        • Memory
      • Purify
        • Memory
      • DTrace
    • Network
      • TCPDUMP
      • Wireshark
    • Debug Proxy
      • Charies
      • Fiddler
    • Debug
      • gdb
      • firebug