<2019年10月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

文章分类

导航

订阅

《软件调试》导读之提纲挈领

拙作《软件调试》出版两个月了,有热心读者建议我讲些阅读这本书的方法。有读者愿意读自己的书,当然是好事,再说读者是客户,他们的意见就是命令,不能怠慢。粗略思考一番,计划先为《软件调试》的每一篇写一个导读短文。总为开篇,今日先谈谈《软件调试》这本书的篇章结构,用软件的术语就是架构,用写作的术语也就是提纲。

从最初的书名说起

早在2003年,我就萌生了写一本关于软件调试的书的念头。但是软件调试是个大话题,有很多东西可以写,必须选择好一个角度才能写出一本好书来。于是我开始搜索当时已经有的书,无论是美国出的,还是英国出的,一共找到了十来本。而后,逐一了解了已有的这些书,归纳了它们的主要内容和特色。

2004年下半年,第一个版本的规划初步成型了,书名叫Advanced System Debugging(《高级系统调试》)(简称ASD)。针对的目标问题是系统级的调试任务,简单理解,就是在系统范围找BUG,是与模块范围内的常规调试相对而言的。在当时的规划书中,我特意从以下四个方面比较了系统调试和常规调试的不同:

Ÿ         Scope

Ø       System wide vs. Module/Product wide

Ÿ         Addressed Issues

Ø       Application or OS Hang/Crash vs. Feature Failure

Ÿ         Source Code

Ø       Not depends on source code vs. Based on source code

Ÿ         Time Frame

Ø       System Debugging is more relevant with issues near or after product deployment

并强调系统调试需要不同的工具,并且更具挑战性:

Ÿ         Addressed issues are more serious

Ø       Hang, Crash, Halt,  auto Restart, etc.

Ÿ         Locate Issues in system wide

Ø       Any component, including hardware, maybe the root cause

Ÿ         Without source code and complete document

Ÿ         Very broad knowledge is needed

Ø       OS, Hardware, Firmware, etc.

当时确定的主要内容有以下几个部分:

(一)调试基础

Ÿ         How debugger works?

Ø       Break, stack trace, memory view, and variable watch

Ÿ         CPU & OS support to Debugging

Ø       Post Mortem (JIT) debug, Attach Debugger

Ÿ         General debug mechanism

Ø       Dump, asserts, event log, and debug outputs

Ÿ         Debugging in software engineering

(二)异常

Ÿ         Understand software and hardware exceptions;

Ÿ         Exception handling mechanism;

Ÿ         What if exceptions uncaught in user mode and kernel mode;

Ÿ         Frequent exceptions.

(三)方法学和工具

Ÿ         Advanced Inspecting

Ø       View system components inside;

Ø       Examine binary (program) files and process;

Ÿ         Advanced Monitoring/Spying

Ø       Monitor system activities and kernel objects;

Ø       Exploring OS boot process;

Ÿ         Advanced Tracing

Ø       Interrupt an application, driver, and OS;

Ø       Skills for WinDbg

(四)蓝屏(BSOD)

Ÿ         Why BSOD?

Ÿ         Interpret BSOD

Ø       Illustrate Stop Code one by one

Ÿ         Enable and analyze memory dump

Ÿ         Trace BSOD by WinDbg

Ø       Kernel debugging

Ÿ         Advanced topics about BSOD

Ø       Crash handler

Ø       More serious issues than BSOD

(五)调试实践

Ÿ         User mode debugging practice

Ø       Dr. Watson, MiniDump

Ÿ         Kernel mode debugging practice

Ø       Kernel debug using COM, 1394 and Virtual PC

Ø       Debug driver issues

Ÿ         Debug ACPI issues

Ø       Resolve tough S3/S1 issues by actual sample 

现在回过头来看第一版计划,可以看到,其中包含了大多数后来要写的内容。而且这种从整个计算机系统的角度来着眼的思想一直保持到最后。

2005年时的选题列选单

2005年年初,开始和电子出版社协商出版计划。我开始进一步细化写作内容和篇章结构。于是第一个版本的章节计划产生了,下面是从当时的选题列选单中摘录下来的:

软件调试是软件开发及维护中最重要且最富有挑战性的工作之一,大多数软件工程师都认为他们
50%以上的工作时间是用在软件调试上的。但是软件调试无论是在软件工程实践中还是在学术界
至今都还没有得到应有的重视,少数效率低下的调试方法仍在普遍使用,如何提高软件调试的效
率和增强软件的可调试性还很少得到关注。特别是,纵观浩如烟海的计算机图书世界,目前还找
不到一本系统全面阐述软件调试理论和实践的作品。本书正是出于这种考虑,力争填补软件领域
和国内外出版界的一大空白。本书本着深入全面和理论与实践并重的原则,多方位的向读者展现
软件调试的原理、方法和技巧。全书分为4 篇,18 章。基础篇(1~4 章)除了介绍基本的概念和
术语(第1 章)外,系统的阐述了CPU(第2 章)、操作系统(第3 章)和编译器(第4 章)是
如何支持软件调试的。开发篇(5~7 章)开创性的提出如何在软件工程的各个环节中,尤其是设
计阶段,融入软件调试策略,提高软件的可调试性,以从根本上降低软件调试的复杂度,提高调
试效率(第5 章)。该篇不仅全面归纳比较了常用的提高软件可调试性的方法(第6 章),还提出
了一些新的模型和实现,有很强的实践参考价值(第7 章)。工具篇(8~12 章)在对各类调试工
具进行概括性介绍(第8 章)后,深入的解析了三类常用调试工具的原理和用法以及一批经典工
具。第9 章在介绍微软的著名内核调试器WinDbg 的同时,深入地揭示了内核调试的原理(目前
还没有一本书包含此内容)。第10 章通过深入解析微软.Net 调试器的源代码,介绍了目前流行的
中间/脚本语言(比如.Net 和Java)调试的原理。第11 章以介绍JTAG 原理为线索,探索了极富挑
战性的嵌入式调试领域,介绍了如何调试常见的嵌入式系统(xScale 系统, ARM 系统等)。第12
章把近百个经典、小巧、免费的调试工具归纳为几类,对它们做了个大检阅(这些工具是附带光
盘工具箱的一部分)。实践篇(13~18 章)首先归纳了被国内外专家普遍认可的一些调试规则和方
法(第13 章),然后结合真实的案例,介绍了解决几类难度较大的调试问题的方法和技巧。第14
章介绍了远程调试、RPC 调试等用户态调试任务。第15 章在介绍非常热门的 Windows 内核/驱动
程序调试的同时,还向读者揭示了如何探索Windows 内核的一些技巧。第16 章全面的探讨了著
名的蓝屏崩溃问题。第17 章介绍了如何在没有源代码和文档的情况下定位系统故障。第18 章以
最富挑战性的ACPI 问题为例,探讨了如何利用前面介绍的方法和工具解决软件、硬件、固件相
结合的棘手问题,并总结全书。

归纳一下,首先当时把要写的内容分为如下四篇:基础篇、开发篇、工具篇和实践篇。另外,将书名从ASD改为《软件调试》。现在看来,这一版本的架构与AWD(Advanced Windows Debugging)颇有相通之处,特别是基础篇和实践篇与AWD的第一篇和第二篇是一个思路。

软件调试的最初300页就是按照以上架构来写作的,写的是上面规划中的第2章和第3章。

重构

动笔后,更深的感觉到写书难。本来计划两个月完成的第2章,写到2005年年底也没完成。又因为春节的大块时间用来探索Windows调试子系统,所以2006年3月才完成第2章的第一稿。2006年8月完成了第3章的初稿。这两章完成后,一个明显的问题是这两章的篇幅都很长,第2章有100页,第3章有210多页。这两章的长度让我觉得很不称心,我觉得一章太长,不易于阅读,也不方便编辑和排版。记得当时我还特意找了几本书,在Windows Internals中,有接近和超过100页的两章,第3章系统机制(98页),和第7章内存管理(110页)。

 一边写作,一边思考了几周后,我终于下定决心重新组织结构。重构的一个指导思想是更侧重调试原理,将工程实践所需的基础知识和技能融入到原理中去,不再面向问题组织篇章和“就事论事”。 根据这一思想,做了如下几个大的改动:

  • 将原来的2,3,4章升级为篇,以便更好的组织这些原理性和基础性的内容。
  • 砍去实践篇,以便使这本书具有更好的通用性。因为实践篇中本来的内容还是面向问题的,深入讨论其中的每个问题域都可以单独写一本书,如果把这些内容放在同一本书中写,那么很难深入,喜欢一个问题域的读者通常也不需要深入了解另外的问题域。
  • 在工具篇中,只集中讨论调试器,去掉本来安排的10~12章。
  • 根据以上策略调整后,新的架构便是今天的样子,全书分为6篇,30章。

    目前的架构

    下图中画出了2006年重构后的篇章结构,也就是目前使用的架构。

    在新的架构中,2、3、4篇是全书的核心,它们所描述的对象也恰好是计算机系统中的三个核心:即硬件核心CPU,软件核心操作系统和生产软件的核心工具编译器。从调试的角度来看,这三个核心所提供的调试支持是支撑软件调试大厦的三块基石。或者说,上层的很多调试技术都是这三个核心所提供支持的应用。因此,了解这些调试支持是了解调试技术的关键。

    另外,从从事计算机相关工作的技术人员(本书读者)的角度来看,了解这三个核心也是至关重要的,对于提高软件开发能力、调试能力和对计算机系统的认知力都非常有益。从这个因素出发,第2、3、4篇的开头一章,即第2章,第8章和第20章,都是介绍这篇所描述核心的基础知识。

    2~4篇的总页数为755页,占全书篇幅的70%。因此阅读和消化这三篇的内容是读懂这本书的主要任务。把这三篇内容搞懂了,就掌握了软件调试技术的基础和核心,同时也可以把对CPU、操作系统、编译器这三大核心的理解提高到一个新的水准。以后,我会分别介绍每一篇的构思,给出一些阅读建议。但读到这里,读者应该清楚,全书的重心在2-4篇。在重心之下,是第1篇,即绪论,这是其它5篇完成后,最后写的一篇,目的是把读者带进门。

    第5篇可调试性,尽管很短,只有短短的两章,总共52页,但是它的“思想地位”非常高。高效调试是学习和研究软件调试技术的初衷。如何实现高效调试呢?答案是调试过程中涉及的每一个部件都要大力支援、积极配合,至少不要抵触和反抗。2-4篇介绍了计算机系统中的三大核心的调试支持,但如果被调试程序不配合,那么调试效率也会大打折扣。因此第5篇的第一个目的是探讨被调试软件的可调试性,介绍在软件设计和开发过程中就要考虑可调试性,未雨绸缪。第5篇的另一个目的是彰显可调试性这一主题,任何精心设计的系统都应该考虑可调试性,对于CPU、操作系统、编译器这样的基础设施,不仅要考虑自身的可调试性,还要考虑如何支持应用软件的可调试性。

    如果说,第1篇是全书内容的第一轮循环,2-5篇是第二轮循环,那么第6篇便是第3轮循环,它以调试器为视角,在介绍调试器的实现方法和使用方法的同时,将前面5篇的内容“复习”了一遍。

    调试器是软件调试的最核心工具,是每个软件高手必备的武器,深谙调试器兵法是《软件调试》这本书的核心目标,有人可能想,为什么不直接按照调试器来组织内容呢?我的确考虑过在第1篇就详细介绍调试器。但是后来没有这么做,原因有二。第一,对于今天的大多数调试器来说,它是与系统密切耦合的,夸张一点说,它只不过是底层调试功能的一个用户接口。因此即使把一个调试器的所有代码都分析一遍,那么还有很多东西搞不清楚。以Windows系统的用户态调试为例,调试器是建立在调试API之上的(《软件调试》第18章),很多调试功能不过是一个API调用就进入到系统代码了。以内核调试为例,WinDBG不过是内核调试引擎(KD)的一个客户端(《软件调试》第18章)。第二,调试器有很多种,如果正面围绕调试展开,那么选择哪种调试器呢?如果选WinDBG,那么整本书就变为《WinDBG调试器XXX》,这是我不希望的,不符合写作这本书的一般性原则。

    在现在的架构中,调试器被安排在最后一篇,并不是降低它的地位,而是让其坐享前面的基础。对于读者,有了前面的基础再理解调试器会觉得很自然,有水到渠成之感。对于作者,写作这一篇时,也非常轻松,不时感觉到前面发散的内容在这里收敛了。另外,把调试器安排在其它5篇的上面也与它的“接口”身份更吻合。

     

    归纳一下,《软件调试》的架构经历了三个版本。第一个版本侧重系统调试,书名为ASD。第二个版本将写作范围扩大,书名推而广之为《软件调试》,内容划分为基础、开发、工具四篇。第三个版本提高第二版本中基础篇的位置,将重心明确在一般原理和具有共性的知识技巧上,缩减开发篇、工具和实践篇的内容。纵观这三个版本,版本1到版本2是扩张,版本2到版本3是精选和淘汰,前两个版本中的核心内容被细化,非原理性和适用面狭窄的内容被去掉了。特别是实践篇被去除了,其中的有些内容被融入到其它篇中,比如蓝屏崩溃被放入到第3篇,纳入到错误提示机制中讨论;栈溢出和内存泄漏被放入到第4篇,与编译器的有关支持一起讨论。而目标读者较窄的RPC调试和ACPI调试只好放弃了。放弃这些内容的一个长远规划是,在完成《软件调试》后,分专题写一系列实战性的短篇,自称为调试战役系列。概而言之,《软件调试》的着眼点是软件调试的一般原理,其目标是为所有喜欢调试技术的读者打下一个宽广而且坚实的基础,这个基础对于做调试是有用的,对于做开发也是有用的,对于解决迫在眉睫的问题有用,对于长远的职业发展也有用。为了实现通用性,那么只能多写具有共性的基础内容,舍弃细枝末节和具体问题,让读者掌握这些基础后,自己来举一反三,也就是常说的“授之以渔”。希望读者阅读《软件调试》时,能想起作者的这一良苦用心,这将有助于您理解书中的内容。

     

    posted on 2008年8月2日 10:55 由 Raymond

    # re: 《软件调试》导读之提纲挈领 @ 2008年8月4日 8:37

    谢谢张老师!
    看到张老师的这篇BLOG,上面是我的第一感觉。为了读者(it is me^_^)的一个小小的建议,占用了您这么多的宝贵时间!
    调试这个课题,除了DEBUG的作用外,也应该就是学习过程中“渔”的方式,更是一种很好的学习方式。很难想象一个完全不知所以然的情况下,去调试一个软件或系统的。
    借用张老师的一段话:《软件调试》的着眼点是软件调试的一般原理,其目标是为所有喜欢调试技术的读者打下一个宽广而且坚实的基础,这个基础对于做调试是有用的,对于做开发也是有用的,对于解决迫在眉睫的问题有用,对于长远的职业发展也有用。为了实现通用性,那么只能多写具有共性的基础内容,舍弃细枝末节和具体问题,让读者掌握这些基础后,自己来举一反三,也就是常说的“授之以渔”。
    也向所有开发人员、希望知其所以然的开发人员、需要规划自己职业发展的IT人员推荐着本书。
    学习软件调试让我想到了张无忌老师的九阳神功,有如此深厚的内功,原来IT生活可以更美的。尽管起步阶段有点难!
    归纳一下(张老师的一个十分好的学习、讲授方式),《软件调试》是中国软件调试这个课题的经典好书,也绝对是中国技术作者写的IT方面的经典好书!
    期待第二版!

    casechen

    # re: 《软件调试》导读之提纲挈领 @ 2008年8月4日 10:42

    已经看完快一半了。
    您的大作确实经典,看到精彩之处常有醍醐灌顶的感觉。
    很多其他书没有解释清楚的问题在这本书里都能找到答案。
    期待实战篇。

    neilhsu

    # re: 《软件调试》导读之提纲挈领 @ 2008年8月4日 21:17

    多谢二位的认可。

    Raymond

    Powered by Community Server Powered by CnForums.Net