原文链接:http://www.aosabook.org/en/gdb.html
作者:Stan Shebs
GDB, 即GNU调试器(GNU Debugger)。它诞生自开源软件基金会(Free Software Foundation)成立之初的第一批程序,并一直是免费和开源软件系统中的主要成员。最初GDB只是Unix系统上一个简单的源码层次的调试器,代码量不过数千行C代码,后来逐步发展壮大,拓展到包括嵌入式系统在内多个平台,代码量也达到了上百万行。
GDB在发展,不断地满足着新的用户需求并增加新的功能。这一章将我们将介绍GDB的整体内部结构,探讨一下GDB是如何做到这一点的。
GDB的设计目标是一个针对使用命令式(imperative)语言(例如C,C++,Ada,Fortran等)编写的程序的符号调试器。使用GDB原始命令行界面的一个示例如下:
% gdb myprog
[...]
(gdb) break buggy_function
Breakpoint 1 at 0x12345678: file myprog.c, line 232.
(gdb) run 45 92
Starting program: myprog
Breakpoint 1, buggy_function (arg1=45, arg2=92) at myprog.c:232
232 result = positive_variable * arg1 + arg2;
(gdb) print positive_variable
$$1 = -34
(gdb)
GDB能显示程序中的错误,开发者据此判断错误的类型并找到解决的方案。
设计GDB最需要考虑的是调试工具的交互性,因为用户在调试时提交的请求是不可预测的。此外,GDB还需要深入到系统最底层,因为编译器会充分利用硬件的各种选项来优化程序的性能。
GDB还要求能够调试不同编译器编译的程序(不仅仅是GNU C编译器),能够调试过时编译器编译的程序,能够调试符号信息丢失、过时或错误的程序。所以,另外一个设计要求是,即使程序中的数据丢失、损坏或干脆无法理解,GDB也能够继续工作并发挥作用。
接下来的几章假定读者熟悉GDB基本的命令行使用方法。如果你还是新手,建议先用一用GDB并细读一下手册[SPS+00]。
GDB程序历史悠久,早在1985年就已经存在。它的作者是Richard Stallman,这个人还编写了GCC,GNU Emacs和其它一些早期的GNU软件。(由于当时并没有软件仓库,GDB开发过程的细节已不为人所知。)
GDB的最早的稳定版本在1988年发布,但在今天的GDB源码中已经找不到多少相似的地方了,GDB被完全重写过至少一次。 令人惊讶的是,早期的GDB并没有太大的野心,后来的平台移植和功能扩展并没有包括在GDB最初的计划之中。
set print elements 80"使用了3个表格,第一个是包含了所有命令的表格,第二个是包含了set
选项的表格,第三个是print
选项的表格,其中elements
选项用于控制打印一个集合体(如字符串或数组)中输出对象的个数。最后瀑布型表格将控制权交给一个实际的命令处理函数,命令的参数将传递给这个函数来解析。一些命令, 比如run
, 处理参数的方式和传统C语言的argc/argv
标准类似, 而其它一些命令, 比如print
, 则假定参数是一个程序表达式, 并将其完整传递给源码解析器。
机器界面
一种GUI调试器方案是将GDB作为图形用户界面程序的后端,将鼠标点击翻译成GDB命令,然后将打印的结果显示在窗口中。这种方案已经在一些软件中实现,比如KDbg和DDD(Data Display Debugger)。但这个方法仍然不理想,因为有时候显示结果时为了可读性会省略掉一些细节,前端提供上下文的能力也会影响到结果的显示。
为解决这个问题,GDB提供了一个被称为机器界面(Machine Interface,MI)的接口。本质上MI仍然是一个命令行界面,但是命令和结果都增加了额外的语法,使得其意义更为显然:每个参数都使用了引号,复杂输出则使用定界符来分组,使用参数名来分块。此外, MI的命令还可以加上顺序标识符作为前缀, 并在结果中返回,保证了结果和命令的匹配。
为了比较两种界面, 分别给出它们对于同一命令的使用情况。下面是正常的step命令及GDB的响应
(gdb) step
buggy_function (arg1=45, arg2=92) at ex.c:232
232 result = positive_variable * arg1 + arg2;
With the MI, the input and output are more verbose, but easier for other software to parse accurately:
下面是MI的输入和输出,虽然显得有些冗余,但更加精确,便于第三方软件进行解析。
4321-exec-step
4321^done,reason="end-stepping-range",
frame={addr="0x00000000004004be",
func="buggy_function",
args=[{name="arg1",value="45"},
{name="arg2",value="92"}],
file="ex.c",
fullname="/home/sshebs/ex.c",
line="232"}
Eclipse[ecl12]开发环境是最著名的使用MI的调试环境。
其它用户界面
其它GDB前端软件包括基于tcl/tk的GDBtk或Insight,基于文字界面的TUI(最初由Hewlett-Packard开发)。GDBtk是一个传统的多面板图形用户界面,使用tk软件库开发,而TUI是一个在终端中使用的分屏文字界面。
维护者
作为一个GNU程序,GDB的开发遵循"大教堂(cathedral)"开发模型。GDB最初由Stallman编写,随后维护者几易其人,每个人都是身兼设计师,补丁审查员,发布管理员数职,他们有权访问仅向少数Cygnus雇员开放的源码仓库。
1999年,GDB被迁移到一个公共源码仓库,维护团队也扩展到了几十人,并且还有一些拥有签入(commit)权限的个人从旁协助。这个模式显著加速了GDB的开发,从原来的每周10个签入增加到了100个以上。
测试,测试
由于GDB高度依赖于特定平台,几乎涵盖全系列的计算设备,而且包含了数以百计的命令,选项以及使用风格,即使是一个经验丰富的GDB黑客也难以完全预料一个修改所产生的后果。
于是,测试套件变得举足轻重。GDB的测试套件包含了众多测试程序以及expect
脚本,使用一个基于tcl被称为DejaGNU的测试框架。其基本模式是, 每个脚本驱动GDB去调试一个测试程序, 然后向其发送命令, 并使用模式匹配来判断结果正确与否。
这个测试套件还能进行交叉调试,既支持真实硬件也支持模拟器,它还能对于特定平台或配置进行测试。
到2011年底,GDB测试套件包含了大约18000个测试用例,包括了基本功能测试,语言特性测试,体系特性测试,和MI测试。所有这些测试都是通用的,适用于所有配置。GDB需要志愿者来测试打补丁后的源码,新的功能也需要新的测试。但是,因为没有人能在所有平台上测试同一修改,要实现测试的完全通过是不现实的。对于本地调试来说, 主干GDB测试时失败10-20次左右是可以授受的, 嵌入式系统则更容易出错。
开放是王道
GDB是"大教堂"开发模型的典范,在该模式下,维护者严密控制源码,而外部用户则跟踪其进度。补丁提交数目较少,封闭的开发过程实际上并不鼓励补丁。自从采用开放模式之后,补丁数量显著增多,而软件质量则一如既往,甚至更好。
制订计划, 但计划赶不上变化
开源软件开发过程实际上会比较混乱,因为开发者之间是松散的,流动性很大。
但是,制订开发计划并发布仍然很有意义。这有助于指导开发者完成相关任务,而且能够吸引潜在的赞助者,另外志愿者在尝试做出贡献时也能有一定的依据。
但是不要尝试设置截止时间,即使是每个人都热情地朝着一个方向努力,也不要指望大家都能全身心地投入并按时完成任务。
鉴于此,不要坚持一个已经过时的计划。长期以来,GDB都有重构为软件库libgdb
的计划,这样, 别的程序就可以通过使用libgdb
来实现一个拥有GUI的调试器。开发人员甚至尝试过将构建libgdb.a
作为整个构建过程的一个中间步骤。虽然这个想法一直存在,但随着Eclipse和MI的成功,libgdb
被搁置了起来,到2012年1月这个想法最终寿终正寝。
无比聪明该多好
看到曾经提交的修改,我们也许会想:为什么一开始不这么做呢? 唉,只因为我们不够聪明。
我们本可以预料到GDB会如此流行,并且会移植到数以百计的平台上,还支持本地和交叉调试。如果事先知道这些,说不定一开始就会使用gdbarch
对象,而不会数年来都在用陈旧的宏和全局变量,目标向量也早该出现。
我们本可以预料到GDB将会被用到GUI中, 毕竟1986年Mac和X窗口系统已经出现了2年。与其设计一个传统的命令行界面,我们更应该让其支持异步事件处理。
然而,真正的教训不在于GDB开发者们有多蠢,而是我们不可能如此聪明地未卜先知。1986年, 窗口-鼠标风格的界面的未来还并不清晰, 我们预料不到它会像今天这样流行,如果第一个版本的GDB就设计为在GUI下使用,我们就可以称得上天才了,但这种好运不是人人都能有的。相反,在一个有限的范围内让GDB有所作为,我们已经为今后的扩展和重构打下了用户基础。
学会接受缺陷
尽力完成过渡,但是时间总是太快,你只能接受缺陷。
在2003年的GCC峰会上, Zack Weinberg哀叹GCC的"不完整过渡",新的底层结构已经引入,但是旧的却尾大不掉。GDB有着同样的问题,但是我们应该看到积极的一面,因为毕竟一些过渡已经完成,比如目标向量,gdbarch
等等。虽然过渡需要多年来完成,调试却要一直继续。
谨防着迷于代码
当你遇到一个对你非常重要的项目,你会花费大量时间在单个代码上, 你会很容易沉迷其中,甚至为了迎合代码而改变自己的想法。但是,很有可能你已经误入歧途,退一步说不定海阔天空。
这样的事情要杜绝发生。
所有代码都源自于一系列清醒的判断:有些来自灵感,有些则不是。1991年节省空间的小伎俩对于2011年的数个G的内存来说是毫无意义的。
GDB曾经支持Gould超级计算机。当他们在2000年关闭最后一台机器时,保留对这种机器的支持已是毫无意义。那些代码只是GDB过往历史中的一些小小篇章,然而现在大部分的发行版中仍然有些"怀旧"。
事实上,很多激进的修改已经摆上日程或已经开展,包括对Python脚本的支持,对并行多核平台的支持,重编码为C++等。这些修改可能要花费数年,但其动机却来自于今天(等到它们完成时说不定已经过时)。
原文链接: http://www.aosabook.org/en/gpsd.html作者:Eric Raymond译者:Liuli Chen(陈旒俐)GPSD是一系列管理GPS设备和其他...
jQuery 杂项 data() 方法jQuery 杂项方法实例 向 div 元素附加数据,然后取回该数据:$(#btn1).click(function(){ $(div).data(g...
jQuery event.stopImmediatePropagation() 方法jQuery 事件方法实例 执行第一个事件处理程序,并阻止剩下的事件处理程序被执行:...
jQuery event.which 属性jQuery 事件方法实例 返回哪个键盘键被按下:$(input).keydown(function(event){$(div).html(Key:+ even...
jQuery wrapInner() 方法jQuery HTML/CSS 方法实例 在每个 p 元素的内容上包裹 b 元素:$(button).click(function(){ $(p).wrapI...