前 言

“嘿,还真不错呢!”我们的学生Andrew首次认真使用调试工具后惊叹道。Andrew三年前在大一上编程课时就学过了调试工具,不过那时他只是将调试工具当做应付期末考试的内容来打发的。现在他已经大四了,教授强烈要求他停止采用输出语句进行调试的方法,改为使用正式调试工具。让Andrew喜出望外的是,他发现使用恰当的工具可以大大缩短调试时间。

如今,在学生及已参加工作的程序员中,还有不少“Andrew”,但愿本书能帮助“Andrew们”发现调试工具的好处。但是,我们更希望本书能帮助那些已经使用了调试工具,但还无法确定在什么情况下该做什么事的程序员做出适当的判断。本书也适用于打算进一步学习调试工具及其幕后原理的人。

本书的编辑曾说过,很多调试知识以前都只是在圈子里口口相传,没有正式编著成书。本书将改变这一状况。本书回答了下列问题。

  • 如何调试线程代码?

  • 为什么有时候断点最终出现的位置与原来设置的位置略有偏差?

  • GDB的until命令为什么有时会跳到意想不到的地方?

  • 如何巧妙使用DDD和Eclipse?

  • 在当今普遍使用GUI的时代,像GDB这样的基于文本的应用程序还有价值吗?

  • 为什么当错误代码超出数组边界时不发生段错误?

  • 为什么要将我们的一个示例数据结构命名为nsp?(不好意思,这只是与我们的出版商No Starch Press私下开的一个玩笑。)

本书既没有美其名曰“用户手册”,也不是关于调试过程认知理论的抽象论文。本书有点介于这两者之间。一方面,确实提供了如何操作GDB、DDD和Eclipse中特定命令的信息;另一方面,讲解并频繁使用了关于调试过程的一些通用原则。

我们之所以选择GDB、DDD和Eclipse,是因为它们在Linux/开源社区中比较流行。本书的示例有点偏向于GDB,不仅仅因为GDB基于文本的性质使得显示在一个页面中更紧凑,而且正如上面的问题所暗示的,我们发现基于文本的命令在调试过程中仍然起着举足轻重的作用。

Eclipse的用途越来越广泛,其远不止仅作为我们这里的调试角色,还提供了各种有吸引力的调试工具。另一方面,DDD占用空间小,而且包括了Eclipse中不具备的某些强大功能。

第1章是概览。很多经验丰富的程序员可能想跳过这一章,但是我们强烈建议大家通读一遍,因为这一章列出了我们针对调试过程推荐的一些简单却有用的通用准则。

第2章着重介绍了调试必不可少的内容——断点,讨论了关于断点的所有细节,包括设置、删除和禁用断点,从一个断点移到另一个断点,查看关于断点的详细信息,等等。

到达断点后会出现什么情况呢?第3章回答了这一问题。我们这里采用的示例涉及遍历树的代码,重点是介绍当到达断点时如何方便地显示树中节点的内容。这里GDB相当出色,提供了一些非常灵活的功能,有助于在每次程序暂停时有效地显示感兴趣的信息。这一章还提供了一个特别优秀的用图形显示树和其他链接数据结构的DDD功能。

第4章包括了由于段错误而产生的致命运行时错误。我们首先介绍了崩溃时在底层发生了什么事,包括程序的内存分配以及硬件与操作系统的协同作用。对系统知识比较了解的读者可能会跳过这一章,但是我们相信,其他很多读者会得益于这一章介绍的基础知识。然后介绍了核心文件,包括如何创建核心文件,如何使用它们来完成“事后反思”。最后介绍了关于调试会话的一个扩展示例,其中有几个程序错误产生了段错误。

第5章不但介绍并行编程,而且包括网络代码。客户/服务器网络编程可算作并行处理,甚至我们的工具也是并行使用的。比如,在两个窗口中使用GDB,一个窗口用于客户机,另一个窗口用于服务器。由于网络代码涉及系统调用,因此我们用C/C++的errno变量和Linux的strace命令补充我们的调试工具。5.2节涉及线程编程。这里同样首先概述基本内容:分时、进程与线程、竞争条件等。这一章介绍了在GDB、DDD和Eclipse中使用线程的技术细节,并再次讨论了一些要记住的通用原则,比如发生线程上下文切换时的时间选择随机性。5.4节介绍了用流行的MPI和OpenMP程序包进行并行编程,并举了一个OpenMP的扩展示例。

第6章包括其他一些重要主题。如果代码不能编译,那么调试工具将无能为力,因此这一章讨论了处理这种问题的一些方法。然后处理由于缺少必要的库造成的连接失败问题,我们再一次觉得提供一些“理论”是有用的,比如库的类型以及如何将库与主要代码连接。如何调试GUI程序呢?为了简便起见,我们这里坚持采用“半GUI”设置——curses编程,并显示如何让GDB、DDD和Eclipse与curses窗口中的事件交互。

正如前面提到的,可以使用辅助工具使调试过程得到很大的增强,第7章介绍了部分辅助工具,还介绍了errnostracelint的一些内容,以及对于有效使用文本编辑器的一些提示。

全书主要介绍的是C/C++编程的调试,第8章则谈到了其他语言,包括Java、Python、Perl和汇编语言。

如果漏掉了读者喜欢的调试主题,我们感到抱歉,书中包括了我们自己编程时发现有用的全部内容。

感谢No Starch Press的诸位工作人员在这个项目上花了很长时间来协助我们。尤其感谢公司的创始人及总编辑Bill Pollock。他一开始就对我们这个“非常规”项目抱有信心,并宽容了我们的多次延误。

Daniel Jacobowitz对本书的初稿做了认真的审查,并提出了许多宝贵建议。还有Neil Ching,虽然他表面上是做文字编辑的,实际上却是拥有计算机学士学位的高材生。他在我们的技术讨论中提出了一些重要看法。本书质量的提高很大程度上得益于从Daniel和Neil处得到的反馈。当然,照例要做一下免责声明:如果还有错误,那一定是我们自己造成的。

Norm的致谢。感谢我的妻子Gamis和女儿Laura,这两个可爱的女子让我觉得非常幸福。尽管这本书她们一个字也没看过,但是她们解决问题的方法、幽默的性格和对生活的热爱深深地影响了我。还要感谢这么多年来我教过的学生,他们教会我的与我教会他们的一样多,是他们让我觉得我选对了职业。我一直致力于“有所作为”,但愿这本书能算作我的一个小小作为。

Pete的致谢。我还要感谢Nicole Carlson、Mark Kim和Rhonda Salzma,花了不少时间来通读本书的各章,提出了更正与建议,感谢你们阅读本书。还要感谢Davis的Linux用户组上这么多年来回答我问题的人们,认识你们使我更聪明。感谢Evelyn提升了我生活的方方面面。特别值得一提的是小猫咪Geordi,它总是趴在我的稿纸上,以免稿子被吹散,还时常为我温暖座椅,并值守书房。我每天都在想念你们。小家伙,睡吧。妈妈,看儿子棒吗?

Norm Matloff与Pete Salzman

于2008年6月9日

目录

  • 版 权 声 明
  • 前 言
  • 第1章 预备知识
  • 第2章 停下来环顾程序
  • 第3章 检查和设置变量
  • 第4章 程序崩溃处理
  • 第5章 多活动上下文中的调试
  • 第6章 特殊主题
  • 第7章 其他工具
  • 第8章 对其他语言使用GDB/DDD/Eclipse