调试一直是软件开发过程中“最痛苦”的环节,本书有望改变这一现状,因为它将调试的科学原理与业界的实践经验有机地融合起来,阐释了有关发现和修正程序错误的最佳方法和实践过程。\r\n 本书一共分为15章,以系统化的方式向读者介绍了整个调试过程,从跟踪和重现故障开始,一直到自动化和简化测试用例,寻找故障最可能的来源,分离故障的起因和结果,并最终修正程序缺陷。本书不仅涵盖了delta调试、程序切片、观察、监视、断言、检测反常等多种基本的静态和动态程序分析技术,还用浅显的语言说明如何使用一些调试领域最前沿的高水平调试工具。\r\n 本书适于那些希望掌握如何以系统化和自动化的方式调试程序的计算机编程人员、及相关专业的研究生以及高年级本科生。
第1章 故障从哪里来 \r\n 1.1 我的程序罢工了 \r\n 1.2 从缺陷到故障 \r\n 1.3 迷失在时空之中 \r\n 1.4 从故障到修正 \r\n 1.5 自动调试技术 \r\n 1.6 BUG、失误、还是缺陷?\r\n 1.7 概念 \r\n 1.8 工具 \r\n 1.9 进一步阅读指南\r\n 1.10 习题 \r\n第2章 跟踪问题 \r\n 2.1 啊!这么多问题 \r\n 2.2 报告问题\r\n 2.3 管理问题 \r\n 2.4 问题分类 \r\n 2.5 处理问题 \r\n 2.6 管理问题跟踪过程\r\n 2.7 把需求看作问题 \r\n 2.8 管理重复问题 \r\n 2.9 关联问题和修正\r\n 2.10 关联问题和测试 \r\n 2.11 概念 \r\n 2.12 工具 \r\n 2.13 进一步阅读指南 \r\n 2.14 习题 \r\n第3章 让程序出错 \r\n 3.1 调试测试 \r\n 3.2 控制程序 \r\n 3.3 在表现层测试 \r\n 3.4 在功能层测试 \r\n 3.5 在单元层测试 \r\n 3.6 分离单元 \r\n 3.7 为调试而设计 \r\n 3.8 预防未知问题 \r\n 3.9 概念 \r\n 3.10 工具 \r\n 3.11 进一步阅读指南 \r\n 3.12 习题 \r\n第4章 重现问题\r\n 4.1 调试过程的第一步\r\n 4.2 重现问题环境 \r\n 4.3 重现程序运行过程 \r\n 4.4 重现系统交互\r\n 4.5 专注于单元\r\n 4.6 概念 \r\n 4.7 工具 \r\n 4.8 进一步阅读指南\r\n 4.9 习题 \r\n第5章 简化问题 \r\n 5.1 简化问题 \r\n 5.2 GECKO BUG马拉松\r\n 5.3 手工简化 \r\n 5.4 自动简化 \r\n 5.5 简化算法 \r\n 5.6 简化用户交互 \r\n 5.7 简化随机输入 \r\n 5.8 快速简化 \r\n 5.9 概念 \r\n 5.10 工具 \r\n 5.11 进一步阅读指南\r\n 5.12 习题 \r\n第6章 科学调试 \r\n 6.1 如何成为一个调试专家 \r\n 6.2 科学方法 \r\n 6.3 应用科学方法 \r\n 6.4 明确调试 \r\n 6.5 记录日志 \r\n 6.6 快速而杂乱的调试 \r\n 6.7 算法调试 \r\n 6.8 构造假设 \r\n 6.9 程序推理技术\r\n 6.10 概念 \r\n 6.11 进一步阅读指南 \r\n 6.12 习题 \r\n第7章 推演错误 \r\n 7.1 分离取值的来源 \r\n 7.2 理解控制流\r\n 7.3 跟踪依赖关系\r\n 7.4 程序切片\r\n 7.5 推演代码的坏味道 \r\n 7.6 静态分析的局限性\r\n 7.7 概念 \r\n 7.8 工具 \r\n 7.9 进一步阅读指南\r\n 7.10 习题 \r\n第8章 观察事实 \r\n 8.1 观察状态 \r\n 8.2 记录运行情况 \r\n 8.3 使用调试器\r\n 8.4 查询事件\r\n 8.5 可视化显示状态 \r\n 8.6 概念 \r\n 8.7 工具 \r\n 8.8 进一步阅读指南 \r\n 8.9 习题 \r\n第9章 跟踪错误来源\r\n 9.1 回溯推理\r\n …… \r\n第10章 断言预期结果\r\n第11章 检测反常 \r\n第12章 起因与结果 \r\n第13章 分离故障起因 \r\n第14章 分离因果链 \r\n第15章 修正缺陷 \r\n附录A 规范定义\r\n术语表\r\n参考文献 \r\n索引
Andreas Zeller是德国Saarland大学的计算机科学教授。他的研究方向集中在提高程序员的生产力方面:即哪些事情可以使程序员的生活和工作更轻松?在Linux和UNIX程序员中,Zeller因GNU DDD——一个拥有内嵌数据可视化机制的调试器前端——而享有盛名。在研究人员和高级程序员中,Zeller因为delta调试——一种可以自动分离计算机程序故障起因的技术——而声名显赫。
他的工作时间被平均地分给教学、阅读、写作、编程,以及在大西洋两岸飞来飞去。他与家人一起生活在德法边界德国一侧的Saarbrücken。
这是一本关于计算机程序中的Bug的书——如何重现Bug?如何定位Bug?以及如何修正Bug,使它们不再出现?本书将教会你很多技术,使你能以系统的甚至是优雅的方式调试任何程序。而且,这些技术在绝大多数情况下都可以自动运行,这样,就可以让你的计算机来完成大部分的调试工作。本书试图解决以下问题:
怎样如实地再现故障?
怎样分离与故障相关的所有因素?
故障是从哪里来的?
怎样用最好的方式修正程序?
一旦理解了调试的工作原理,你就会以不同的方式来看待调试。你会考虑起因和结果,你会以系统化的方式构造和完善假设以跟踪故障起因,而不是只看到一大堆混乱的代码。你对调试的理解甚至能帮助你创建自己的自动化调试工具。所有这些将会使你在调试上花费更少的时间,这也就是你对自动化调试感兴趣的主要原因,不是吗?
写作本书的缘由
尽管我是一个研究人员,但是我更愿意把自己看作是一名程序员,因为我的大部分时间都是在编写程序。在编程过程中我会犯错误,因此不得不调试代码。我很希望自己是那种被称为“超级程序员(überprogrammer①)”的程序员——永远不会犯错——但我和你们一样,只是一个普通人。
在学习过程中,我了解到少量的预防措施要比大量的治疗措施有价值得多。我还学会了许多种预防错误的方法。今天,我又把它们传授给我的学生。但是,在努力预防的同时也不能忘了治疗。如果我们是医生,不能仅仅因为病人没有采取所有可能的预防措施,就拒绝进行治疗。
所以,与其设计一种终极的预防措施,不如寻求一个好的治疗方法。现在,遍及全球的其他研究人员已经接受了这种务实的观点。我很高兴地宣布我们已经成功了。今天,许多先进的调试技术已经被广泛地应用于自动化调试过程中。
这些技术不仅可以使调试过程自动化,而且可以把调试从一种“旁门左道”转变为一门系统的、组织严密的学科,该学科可以像任何一门软件工程科目一样被纳入教学计划。因此,我开设了一门关于自动化调试的课程,并把该课程的讲稿整理成了一本书。现在,它就呈现在大家的面前。
读者对象
本书适于那些希望掌握如何以系统化和自动化的方式调试程序的计算机专业人员、研究生以及高年级本科生。我们假定读者熟悉编程和手工测试,无论这些知识是从介绍性课程还是从工作经验中获得的。
本书的涵盖范围
本书的重点是Bug的治疗——即,在出现程序故障后进行的分离和修正程序代码中的缺陷的活动。本书只会涵盖一部分预防缺陷的话题,你可以从很多其他书籍中找到关于预防的深入探讨。实际上,可以说大部分计算机科学都是与如何预防Bug相关的。但是,当预防措施失效时就必须进行治疗,这就是本书的主旨。
内容提要
本书共分为15章和一个附录。其中第1章、第6章和第12章是阅读各自后续章节的先决条件。
在每一章的最后都有一个被称为“概念”的小节,它概括了这一章的关键概念。其中的一些概念前面标有“How To”,它们概括了一些很容易遵循的技巧。(目录中有一张表列出了所有的“How To”。)此外,每一章的结尾还有一些练习题,用来检验你学到的知识,以及一个被称为“进一步阅读指南”的小节。本书的内容组织形式如下所示:
第1章:故障从哪里来
你的程序出故障了,为什么会这样?答案是程序员写了一段有缺陷的代码。程序运行时,有缺陷的代码造成了程序状态中的感染,随后引发了一个可感知的故障。为了寻找有缺陷的代码,你必须以这个故障作为起点回溯追踪起因。本章定义了和调试相关的基本概念,并且介绍了后续章节将会讨论的一些技术——希望能像正餐前的开胃酒,增进大家的食欲。
第2章:跟踪问题
本章阐述如何管理用户报告的问题:如何跟踪和管理问题报告?如何组织调试过程?如何跟踪多个版本?以上问题构成了整个调试过程的基本框架。
第3章:让程序出错
在调试程序前必须首先配置好程序的测试环境——即,带着使程序出错的意图运行程序。本章会回顾一些基本的测试技术,并且重点讨论自动化和分离技术。
第4章:重现问题
调试过程的第一步是重现当前的问题——即创建一个测试用例使程序以某种特定的方式发生故障。这么做的第一个目的是使故障处于我们的掌控之中,这样才能更好地观察它。第二个目的是验证修正是否正确。本章的主题是关于如何重现问题的操作环境、历史和症状的典型策略。
第5章:简化问题
重现了问题之后,下一步就必须简化它——即,找出与该问题无关的环境因素,并忽略它们,其结果就是得到一个只包含相关环境因素的测试用例。在最好的情况下,简化的测试用例产生的报告能立刻精确地定位缺陷。本章介绍一种能自动简化测试用例的自动化调试方法,即delta调试技术。
第6章:科学调试
重现和简化问题之后,还必须理解故障的来源。我们可以通过某些方法得到能解释宇宙的某个方面的理论,这一过程被称为科学方法,它同样适用于获得问题的诊断结果。本章介绍了很多关于科学方法的基本技术,包括怎样构造和验证假设、怎样进行试验、怎样系统地实施整个过程,以及怎样使调试过程清晰明了。
第7章:推演错误
本章开始研究第6章介绍的构造假设的技术。首先从推演技术开始——这是一种从抽象的程序代码到具体的程序运行过程的推理技术。我们还要特别讨论程序切片技术,一种能确定变量值的潜在来源的自动化方法。我们可以使用程序切片技术有效地缩小可疑的错误状态感染点的数量。
第8章:观察事实
尽管使用推演技术无需考虑具体的运行过程,但观察技术则可以用来确定具体的运行过程中的事实,即,具体运行过程中发生的情况。本章将会深入探究实际的程序运行过程,并且介绍一些被广泛应用的分析程序运行过程与状态的技术,其中包括传统的日志记录技术、交互式调试技术、事后调试技术,以及很有启发的可视化与概括技术。
第9章:跟踪来源
一旦在调试过程中观察到一个被感染的错误状态之后,就必须揭示出它的来源。本章将讨论全知调试,这是一种记录整个运行历史的技术。使用这种技术,用户不需要重新运行程序,就能探查运行过程中任意时刻的情况。除此之外,本章还会探讨动态切片技术,通过这种技术可以跟踪某些特定取值的来源。
第10章:断言预期结果
仅仅使用观察技术还不足以进行调试,我们还必须把观察到的事实与期望的程序行为进行比较。本章会讨论如何使用著名的断言技术来自动进行比较,除此之外还会展示如何确保内存等重要的系统组件的正确性。
第11章:检测反常
虽然我们能从一次程序运行过程中得到相当多的信息,但是比较多次运行过程能给我们提供更多定位正常和反常的机会——反常通常能帮助我们定位缺陷。本章会讨论如何检测代码覆盖和数据访问中的反常,还会展示如何从多个测试运行过程中自动推断不变量,其目的是检测随后的不变量违例。所有这些反常都是潜在的感染点。
第12章:起因与结果
推演、观察以及归纳技术在搜寻潜在缺陷方面都表现优异。然而,单独使用其中任何一种技术来定位故障起因都是不够的。那么我们如何识别起因?又如何做到分离出的不仅仅是某一个起因,而是与故障相关的那个真实起因?本章的基本内容是为系统地、自动地寻找故障起因打基础的技术。
第13章:分离故障起因
本章的主要内容是如何使大部分调试过程自动化。我们演示了如何使用delta调试自动分离故障起因——包括程序输入、程序的线程调度以及程序代码中的故障起因。在最好的情况下,使用delta调试技术得到的起因能立刻定位缺陷的位置。
第14章:分离因果链
本章介绍一种进一步缩小故障起因的方法。即,通过抽取和比较程序状态,应用delta调试技术自动分离引发故障的变量及其取值,最终可以得到关于故障的因果链,例如:“变量x的值为42,因此变量p的值变为null,于是程序发生了故障。”
第15章:修正缺陷
一旦理解了故障的因果链,我们就可以确定故障来源。但是我们还必须找出错误状态开始感染的位置——也就是缺陷的实际位置。本章会讨论如何系统地缩小缺陷的范围——以及如何修正已定位的缺陷。
附录:规范定义
为了便于阅读,附录中收录了本书所有的规范定义及其证明。
参考书目
参考书目列出了大量与本书主题相关的可供进一步阅读的资料。
索引
本书的结尾是一张关键词索引表。
补充材料、资源及官方网站
本书涉及的大部分材料以前从来没有在哪本教科书中出现过。后面一些章节讨论的内容甚至还没有被实践广泛验证。像那些涉及正在发展的领域的书籍一样,本书也有待于从更多的理论研究和实践工作中得到完善。换句话说,书中肯定会有很多错误,欢迎大家对本书提出自己的意见。你可以写信给Morgan Kaufmann出版公司并由他们转交给我,或者直接发E_mail给我:zeller@whyprogramsfail.com。本书还有一个官方网站:
http://www.whyprogramsfail.com
从那里可以找到新近发布的信息或更新(包括修正)。
给教师的建议
我已经用本书作教材给研究生讲授了三次关于自动调试的课程。每次课程大约包括15堂课,每堂课90分钟。基本上,本书每章对应于一堂课。后面的习题也来自于这些课程(以及测试题)。为了便于你的教学,我还专门为这门课程制作了幻灯片,以Keynote和Powerpoint两种格式发布。你可以从以下网址下载:
http://www.whyprogramsfail.com。
如果你喜欢自己制作幻灯片,也可以从该网站下载本书的所有原始图片。
给读者的建议
版式
为了简化问题,本书的大部分例子程序采用标准输入/输出机制——即,使用命令行和控制台。在所有例子中,印刷体代表程序输出,粗印刷体代表用户输入。美
无封面