《软件调试》与《Windows高级调试》比较之我见
我是在2007年11月听说Advanced Windows Debugging(姑且翻译为《Windows高级调试》,简称AWD)这本书的。一个朋友和我说有一本书和你在写的书很相似,已经要出版了。朋友这样说当然是好意,防止我白费功夫重复劳动。或者用英文说就是不要“Reinvent the wheel(重复发明轮子:-))”,其实中文也有很多类似的冷语,比如“吃人家嚼过的馍”,其讽刺度之深比重新发明轮子还恨。我初听这个消息当然为之一振,可不想自己写了两年多(当时已经两年多)的东西与别人的一样。当晚我便GOOGLE了这(AWD)本书,当时在AMAZON上便有该书的预报,有章节目录。把章节目录看了两三遍后,我的担心慢慢散去了。从目录框架显然可以看出,AWD与我的《软件调试》大不一样。于是这个小“震动”只影响了我几个小时后,我便继续投入到《软件调试》的工作了,当时《软件调试》的第一稿也已经完成,在编辑和审阅第二稿的过程中。
随着AWD正式出版(07年底),越来越多的人知道了这本书,于是又不时有朋友和我提起这本书。现在,《软件调试》也已经出版了,提到AWD这本书的仍很多。今日是端午假期,略有闲暇,于是想借此文谈一下我比较这两本书的结果。
概括而言,这两本书都是专门介绍调试的,都介绍了WinDBG调试器和Windows操作系统的调试支持,因此可以说这两本书是有共性的。但是,因为这两本书是不同文化背景的不同作者分别独立写作的,因此它们间的差异还是远远超过相似之处。从全局来看,二者的内容组织方式完全不同,覆盖的主题也有非常大的差异。从局部来看,两本书应该没有任何一段内容是一样的,如果排除某些程序输出可能的相同,两本书应该没有任何一行内容是相同的。
换句话来说,《软件调试》和AWD的不同之处可以说是随处可见。以下是我归纳出的几个主要不同点:
- 1)篇章结构大不同。《软件调试》全书1007页(正文),以支撑调试技术的基石和调试技术的上层建筑为主线分为6篇,共30章,两个附录(都很短,共4页)。第1篇为绪论(26页),第2篇(136页)、第3篇(376页)、第4篇(242页)分别是支撑调试技术的CPU基础、OS基础和编译器基础,第5篇(52页)探讨可调试性,谈被调试软件支持调试的重要性,第6篇(166页)为调试器。 AWD全书780页左右(包括篇头等共840页),分3篇,15章,1个附录。第1篇为概览,分四章,共197页。第2篇名为应用调试(Applied Debugging),共353页,分为6章,分别讨论了调试Windows系统中几种典型问题(内存、安全和进程间通信等)的调试方法。第3部分名为高级主题(Advanced Topics),共225页(包括介绍App Verifier设置的附录A),内容有些杂烩,分5章,分别为:编写WinDBG扩展(11章)、64位调试(12章)、Postmortem调试(13章)、Power Tools和Windows Vista基础。
- 2)侧重点不同。《软件调试》更注重原理,是按着从理论到实践的顺序逐层深入的。以调试器的工作原理为例,第1篇概要介绍了主要调试功能的特征,第2篇介绍了CPU对关键调试功能的支持,第3篇深入讨论了调试模型和调试事件的产生、分发和传递过程,第4篇介绍编译器的调试支持,第5篇介绍被调试软件对调试过程的影响(作用与反作用),第6篇解剖调试器本身,将关键调试功能的实现深入到函数一级。总之,原理性内容贯穿《软件调试》全书,篇幅占一半以上,分几个循环,从不同角度反复剖析。相对而言,AWD的原理性内容并不多,而且是零散分布的。第1篇是讲解原理比较多的地方,名为Debugger Uncovered的第3章略微讲了调试器循环和调试事件,但是根本没有深入和系统讲调试模型和调试事件的分发和传递过程。正如第1篇的题目“概览”所代表的,AWD对调试原理的介绍只是概览性的。从目录就可以看出,AWD的主要篇幅在第2篇和第3篇,第2篇谈的是调试应用。因此可以说AWD是一本面向“问题”的书,仍是针对问题展开的,而《软件调试》是面向原理的,是按原理展开的。
- 3)覆盖面不同。如书名所示,AWD是完全针对Windows的。而软件调试的出发点是软件调试的一般原理。尽管第3篇谈了很多Windows,但是只是把它当作操作系统的实例(一个比较复杂的实例),其中的很多内容对其它操作系统也是有意义的或者值得借鉴的。除了Windows,《软件调试》系统介绍了CPU的调试支持(第2篇)、编译器的调试支持(第4篇)、可调试性(第5篇)和调试器的历史、一般模型和实现方法(第5篇),这些内容在AWD中都是很少或者没有谈及的。例如,我很自豪的以下内容在AWD中都没有:
- CPU的分支监视和记录功能,这是实现分支到分支跟踪的基础。
- Windows的用户态调试模型,包括XP之前的和XP之后的。
- 内核调试调试引擎的工作原理和通信协议。
- 对异常的深入介绍,从CPU到操作系统、到编译器、再到调试器。
- CPU的JTAG支持,这是精确调试软件调试器无能为力的底层错误的最有效方法。
- 对可调试性的探讨。
- 调试技术的发展历史。
在《软件调试》与AWD都覆盖了的几个主题中,《软件调试》更加深入和全面,比如:
- 两本书都介绍了WinDBG功能和命令(用法),《软件调试》专辟一章——第30章《WinDBG用法详解》,先介绍了WinDBG的工作空间、命令的分类和常识、用户界面、表达式、寄存器、别名和循坏执行等,然后按照用途将270多条标准命令和元命令分为15个小节详细做了介绍。而AWD是将所有命令放在第2章的Basic Debugger Tasks一个小节中介绍的,这可能是全书最长的一个小节,共64页(从45页到109页)。老实说,我是不喜欢把这么多内容放在一个小节中的,至少很难找,阅读时也感觉很累。但确实是很多英文书都这么做。《软件调试》特别避开了这一点,每一章下先分节,节下还有具有明确标号的三级小节。举例来说,WinDBG的30.8节是专门讲有关调试符号的用法的,30.8.1略讲符号的重要性,30.8.2讲符号搜索路径,30.8.3讲符号服务器,30.8.4讲加载符号文件,30.8.5讲观察模块信息,30.8.6讲检查符号,30.8.7讲搜索符号(ln命令),30.8.8讲设置符号选项,30.8.9讲如何加载不严格匹配的符号。所有这些内容《软件调试》用了10页,虽然长但是还是比较清晰和易于查找。而AWD的对应内容分为在刚才所说的那一个目录里看不到的三级小节中,没有序号,题目是Setting Up and Using the Symbols。这个三级小节用较大的黑正体表示,它的长度有15页,用黑斜体分为若干个四级小节,例如What Are Symbol Files?Symbol Server等。因为AWD的所有三级小节和四级小节都是没有序号的,只是字体的差异,因此查找起来和第一次读起来还是有些晕的。
- 两本书都介绍了调试符号,或者说三个作者(AWD有两个作者)都认识到了调试符号对于调试的重要性。但是AWD基本是把调试符号的大多数内容都放在刚才说的第2章的那个三级小节中。而《软件调试》分多次来讲解。第25章(属于第4篇)是对调试符号的最直接介绍,介绍了调试符号的背景、作用、存储格式(PDB、DBG)、产生过程和有关的概念,比如名称修饰。然后在第30.8节专门介绍调试符号在调试器中的使用方法。
- 两本书都各花一章介绍了栈和堆。《软件调试》是在第22章(《栈和函数调用》)和第23章(《堆和堆检查》)。AWD是在第5章(Memory Corruption Part I—Stacks)和第6章(Memory Corruption Part II—Heaps)。不论是AWD还是《软件调试》,介绍栈和堆的主要原因应该是基本一致的,在我看来有两个:一是很多软件问题都牵涉到栈和堆;二是这两个设施对于理解软件调试、乃至软件本身都有着重要意义,这两样东西搞不清楚,那么别的就会受影响。或者说,栈和堆是软件的基础设施,与软件的正常工作和异常行为都息息相关。翻阅了一下,AWD的介绍仍是直接针对问题的,没有深入介绍很多基础的东西,比如,关于栈一章中,AWD没有介绍栈的创建和自动增长、调用协议、栈帧、FPO,关于堆的一章中,AWD也没有仔细介绍堆的数据结构。而《软件调试》是花大力气介绍了这些内容。
这篇短文已经不算短了,该停笔了。概括一下,《软件调试》和AWD是风格和内容都很不同的两本调试书籍。二者的部分主题有所交叉,但是具体内容和写法有很大不同。从读者的角度来看,即使完全阅读两本书也不会(至少很少会)有重复的感觉。从另一个角度来说,软件调试是一个非常庞大的话题。有几本书来讨论这个话题非常正常。想一想,今天已经有多少本书在讨论C语言编程、C++、.Net、Java、JavaScript、软件测试、软件项目管理、敏捷编程......可以说,关于调试的书还是太少了,而不是太多。