调试

默认的 Ruby 调试器允许你在程序执行时设置断点和监视点并计算变量。要在调试器中运行程序,请在启动 Ruby 解释器时使用 -r debug 选项(其中 -r 表示 ‘require’,debug 是调试库的名称)。例如,这是调试一个名为 debug_test.rb 的程序的方法:

  1. ruby r debug debug_test.rb

Ubygems?什么是 Ubygems …?

在某些情况下,如果运行上述命令,你可能会看到类似于以下令人费解的消息: c:/ruby/lib/ruby/site_ruby/1.8/ubygems.rb:4:require ‘rubygems’当你开始调试时,你会发现自己试图调试文件 ‘ubygems.rb’ 而不是你的程序!这似乎是一个困扰使用一键安装程序安装 Ruby 的 Windows 用户的主要问题:(http://rubyforge.org/projects/rubyinstaller/)此安装程序设置环境变量 RUBYOPT=-rubygems。在大多数情况下,这具有允许 Ruby 程序使用 ruby gems “打包系统”来安装 Ruby 库的理想效果。但是,当你尝试使用 -r 选项时,会将其解释为 -r ubygems,这就是加载文件 ubygems.rb 的原因。Ruby 顺便(可能令人困惑?)提供了一个名为 ubygems.rb 的文件,它除了引入(requiring)rubygems.rb 之外什么都不做!有两种方法可以解决这个问题。你可以永久删除 RUBYOPT,也可以暂时禁用它。但是,如果你选择永久删除它,则以后使用 ruby gems 时可能会遇到副作用。要永久删除它,请加载“开始”菜单,(如果使用 XP 则为“设置”)“控制面板”;(如果使用 Vista,则为“系统和维护”);单击系统(在 Vista 上,你现在应该单击“高级系统设置”);在“系统属性”对话框中,选择“高级”选项卡;单击环境变量;在“系统变量”面板中,找到 RUBYOPT 并删除它。更安全的替代方法是在加载调试器之前在命令提示符处禁用该变量。为此,请输入: set RUBYOPT=这将仅为此命令会话禁用 RUBYOPT 环境变量。你可以输入以下命令验证这一点: set RUBYOPT你应该会看到以下消息: Environment variable RUBYOPT not defined但是,打开另一个命令窗口并输入 set RUBYOPT,你将看到此处的环境变量保留其默认值。

一旦调试器启动后,你可以输入各种命令来逐步执行代码,设置断点以使执行暂停在特定行,设置监视以监视变量值等等。在下一页是可用的调试命令列表:

命令 解释说明
b[reak] [file|class:]<line|method> 在某个位置设置断点
b[reak] [class.]<line|method> 在某个位置设置断点
wat[ch] <expression> 为某个表达方式设置监视点
cat[ch] <an Exception> 为异常设置捕获点
b[reak] 列出断点
cat[ch] 显示捕获点
del[ete][ nnn] 删除部分或全部断点
disp[lay] <expression> 将表达式添加到显示表达式列表
undisp[lay][ nnn] 删除一个特定或所有显示表达式
c[ont] 运行到结束或遇到断点
s[tep][ nnn] 前进(代码)1 行或 nnn 行
n[ext][ nnn] 跨越一行或直到 nnn 行
w[here] 显示帧
f[rame] where 别名
l[ist][ (-|nn-mm)] 程序列表,- 向后列出给定行 nn-mm 的列表
up[ nn] 移到更大的帧
down[ nn] 移到更小的框帧
fin[ish] 回到外部帧
tr[ace] (on|off) 设置当前线程为跟踪模式
tr[ace] (on|off) all 设置所有线程为跟踪模式
q[uit] 退出调试器
v[ar] g[lobal] 显示全局变量
v[ar] l[ocal] 显示局部变量
v[ar] i[nstance] <object> 显示对象的实例变量
v[ar] c[onst] <object> 显示对象的常量
m[ethod] i[nstance] <obj> 显示对象的方法
m[ethod] <class|module> 显示类或模块的实例方法
th[read] l[ist] 列出所有线程
th[read] c[ur[rent]] 列出当前线程
th[read] [sw[itch]] <nnn> 将线程上下文切换为 nnn
th[read] stop <nnn> 停止线程 nnn
th[read] resume <nnn> 恢复线程 nnn
p expression 计算表达式并打印其值
h[elp] 打印帮助信息
<everything else> 执行计算

让我们看看如何在真正的调试会话中使用其中一些命令。打开系统提示符并导航到包含文件 debug_test.rb 的目录。输入以下命令启动调试器:

debug_test.rb
  1. ruby r debug debug_test.rb

现在,让我们尝试一些命令。 在这些示例中,我写了 [Enter] 以显示你应该在每个命令后按 Enter 键。首先让我们看一下代码列表:

  1. l [Enter]

这显示了该程序的前几行。l(小写 “L”)或 list 命令列会出小块代码。实际行数将随调试代码而变化。列出更多:

  1. l [Enter]
  2. l [Enter]

或列出特定行数(此处字母 ‘l’ 后跟数字 1,连字符和 100):

  1. l 1-100 [Enter]

我们在第 78 行放一个断点(breakpoint):

  1. b 78 [Enter]

Ruby 调试器应该回复:

  1. Set breakpoint 1 at debug_test.rb:78

我们也可能设置一个或多个监视点(watchpoints)。监视点可用于触发简单变量的中断(例如,当创建 @t2 对象时,输入 wat @t2 会中断);或者它可以设置为匹配特定值(例如 i == 10)。在这里,我想设置一个在 @t4name 属性为 “wombat” 时中断的监视点:

  1. wat @t4.name == "wombat" [Enter]

调试器应该确认这一点:

  1. Set watchpoint 2:@t4.name == "wombat"

请注意观察点编号为 2.如果你随后决定删除监视点,则需要该编号。好的,现在让我们继续执行:

  1. c [Enter]

程序将一直运行,直到它到达断点。你将看到类似于以下内容的消息:

  1. Breakpoint 1, toplevel at debug_test.rb:78
  2. debug_test.rb:78: puts( "Game start" )

这里显示了它停在的行号和该行的代码。让我们继续:

  1. c [Enter]

这次它在这里中断了:

  1. Watchpoint 2, toplevel at debug_test.rb:85
  2. debug_test.rb:85: @t5 = Treasure.new("ant", 2)

这是在成功计算监视点条件之后的行。通过列出指示的行号来查看:

  1. l 85

调试器高亮显示了一组行,在当前执行(86):

  1. [80, 89] in debug_test.rb
  2. 80 # i) Treasures
  3. 81 @t1 = Treasure.new("A sword", 800)
  4. 82 @t4 = Treasure.new( "potto", 500 )
  5. 83 @t2 = Treasure.new("A dragon Horde", 550)
  6. 84 @t3 = Treasure.new("An Elvish Ring", 3000)
  7. 85 @t4 = Treasure.new("wombat", 10000)
  8. => 86 @t5 = Treasure.new("ant", 2)
  9. 87 @t6 = Treasure.new("sproggit", 400)
  10. 88
  11. 89 # ii) Rooms

如你所见,第 85 行包含与监视点条件匹配的代码。请注意,在最初创建 @t4 的第 82 行之后,执行没有停止,因为那里没有满足监视点条件(它的 name 属性是 “potto”,而不是 “wombat”)。如果要在断点或监视点处暂停时查看变量的值,只需输入其名称即可。试试这个:

  1. @t4 [Enter]

调试器将显示:

  1. #<Treasure:0x315617c @value=10000, @name="wombat">

你可以同样输入要执行的其它表达式:

  1. @t1.value [Enter]
  2. 10+4/2 [Enter]

现在删除监视点(回想一下它的编号是 2):

  1. del 2 [Enter]

并继续,直到程序退出:

  1. c [Enter]

还有更多的命令可用于以这种方式调试程序,你可能想要尝试上表中显示的那些。你还可以通过输入 helph 在调试会话期间查看命令列表:

  1. h [Enter]

要退出调试会话,请输入 quitq

  1. q [Enter]

虽然标准的 Ruby 调试器有其用途,但它不如使用集成开发环境提供的图形调试器简单或方便。而且,它很慢。在我看来,调试简单脚本很好,但不建议用于调试大型和复杂的程序。