《Effective Debugging:调试软件和系统的66个有效方法》是一本关于软件和系统调试的实用指南。
作者迪欧米迪斯.斯宾奈里斯(Diomidis Spinellis)是希腊雅典经济与商业大学管理科学与技术系教授。他的研究涵盖软件工程、IT安全和云系统工程。他撰写了两本屡获殊荣的技术图书,《代码阅读方法与实践》(Code Reading: The Open Source Perspective)和《高质量程序设计艺术》(Code Quality: The Open Source Perspective)。他曾是IEEE Software杂志编辑委员会成员长达十年之久,并定期为“Tools of the Trade”专栏撰稿。他不仅为OS X和BSD UNIX贡献过代码,还是UMLGraph、CScout和其他一些开源的软件包、库和工具的开发者。他拥有英国帝国理工学院的软件工程硕士和计算机科学博士学位。他是ACM和IEEE的高级会员。在2015至2018年期间,他一直担任IEEE Software杂志主编。
《Effective Debugging:调试软件和系统的66个有效方法》详细讲解了66个调试软件和系统的有效方法,这些方法围绕着“重现bug——探查bug——解决bug”这一主线而展开。
本书分为8章,共包含66个条目。本书首先讲解了调试策略(第1章)、调试方法(第2章) 以及调试时所用的工具与技术(第3章),旨在帮助读者调试各类软件故障和系统故障。紧接着 介绍了可应用于调试工作各阶段的技术,涵盖使用调试器(第4章)、编写程序(第5章)、编译 软件(第6章)和运行系统(第7章)等阶段。本书最后一章(第8章)专注于介绍一些特定的 调试工具和调试技术,这些工具和技术用于定位多线程和并发代码中那些棘手的bug。
我们在开发和运行现代复杂计算系统的过程中,可能会遇到问题。本书所要讲解的调试,涵盖解决这些问题所需的策略、工具和方法。过去,调试主要指发现和修复单个程序的故障。然而,如今很少有程序是孤立工作的。即便是很小的程序,也会链接(通常是动态链接)外部库。更复杂的程序可能运行在应用服务器上,需要调用Web服务、使用关系数据库和NoSQL数据库、从目录服务器获取数据、运行外部程序、使用其他中间件等,并集成众多第三方软件包。许多内部开发的组件和第三方组件可能运行在遍布全球的主机上,整个系统和服务的稳定运行依赖于这些组件的可靠运行。DevOps是解决这一现实问题的过程、方法与系统的统称,它强调开发人员和其他IT 从业人员应承担的职责。本书旨在培养 读者在面对故障时也能具备全局视角,因为在具有挑战性的问题中,通常很难立即确定问题 所在的组件。
本书主要面向有一定经验的开发人员,并非入门读物,希望读者不仅能够理解使用不同 编程语言编写的小型代码示例,还能够使用高级的图形用户界面编程工具以及基于命令行的 编程工具。书中对所包含的调试技术都会进行详细的描述,因为笔者发现即便是精通某些方 法的经验丰富的开发人员,也难以全面掌握所有调试技术,可能需要在其他方面获得一些具 体的指导。此外,如果你曾花费至少数月时间调试实际软件系统的问题,那么理解书中更高 级别条目的上下文将更为容易。
如何使用本书
一种方法是从头到尾、逐页阅读直至完成。但请不要急于这样做,本书还有更有效的使 用方法。本书的内容大致可分为3类,读者在使用本书时,可以有针对性地、灵活地选择阅 读内容及阅读顺序。
● 面对故障时应了解并实践的策略与方法。第1章和第2章介绍了这些内容。此外, 第5章介绍的很多技术也属于此类。读者应阅读并理解这些条目,逐渐将应用它们 变成一种习惯。在调试过程中,我们应系统性地反思所使用的方法。当陷入僵局时, 充分理解已探索的路径对于找到其他解决方案也是有益的。
● 值得投入精力学习的技术与工具。这些内容主要集中在第3章,其他章也包含一些应 对日常问题的内容,例如条目36。读者应投入时间学习并实践这些条目中描述的技术 与工具。这可能意味着需要放弃熟悉的工具带来的舒适感,去克服学习高级工具的 困难。初期可能会面临一些挑战,但从长远来看,这有助于读者提升自身调试技能。
● 遇到困难时可运用的技术思路。这些技术并不常用,但在解决难以理解的问题时, 它们往往能够扭转局势,节省大量时间。例如,如果无法理解为什么自己的C 语 言 和C++ 代码无法编译,可以参考条目50。对于这些内容,读者可以快速浏览,大概 知道有哪些备选方案,当有具体需求时,再仔细研究。
如何让自己的开发工作更轻松
尽管本书所有条目提供的建议都涉及如何诊断故障和调试现有错误,但读者也可以利 用其中的许多建议来尽量减少bug。严格的调试与软件开发可以相互促进,形成良性循环。 无论你现在或将来在软件设计、构建和管理工作中扮演何种角色,本书的建议都可为你提 供帮助。
在设计软件时,应该做到以下几点。
● 使用适合其角色的最高级别机制
● 提供调试模式
● 提供监控和记录系统运行的机制
● 提供一个选项,使得可以用UNIX 命令行工具对组件进行脚本化
● 让内部错误显现为明显故障,而非隐含的不稳定因素
● 提供一种在事后获取内存转储的方法
● 尽量减少软件执行中的非确定性因素及其影响。 在构建软件时,应该采取以下步骤。
● 获取同事的反馈。
● 为自己编写的每个例程创建单元测试。
● 使用断言来验证自己的假设,以及代码的功能是否正确。
● 努力编写可维护的代码,即可读性强、稳定且易于分析和修改的代码。
● 在构建过程中避免引入非确定性因素。
最后,在管理软件的开发和运行时,无论是团队合作还是个人处理,都应做到以下几点。
● 使用适当的系统将问题记录下来,并进行跟踪。
● 对需处理的问题进行分类并确定优先级。
● 使用维护良好的修订控制系统正确记录软件的变动。
● 逐步部署软件,以便能够在旧版本与新版本之间进行比较。
● 努力实现所用工具和部署环境的多样性。
● 定期更新工具和库。
● 建议购买所用的任何第三方库的源代码,并购买必要的高级工具来定位 难以捉摸的错误。
● 提供用于调试硬件接口和嵌入式系统的专用工具包。
● 使开发人员能够远程进行软件调试。
● 对于要求较高的故障排查任务,确保提供充足的CPU 和磁盘资源。
● 通过代码审查和直接指导等实践,促进开发人员之间的协作。
● 鼓励进行测试驱动开发。
● 将性能剖析、静态分析和动态分析纳入软件构建 流程,同时保持快速、精简且高效的构建和测试周期。