โปรแกรมทำงานตามที่อยากครั้งแรกบ่อยแค่ไหน? หลายครั้งโปรแกรมไม่ทำงานเหมือนคาด ต้องใช้ศิลปะ debugging ruby หาสาเหตุ
อาจคุ้นเคย error message นี้:
undefined method 'some_method' for nil:NilClass
หมายถึง nil value 溜进 代码 里 用技术 在 这 文章 你 会 学会 处理 这 问题 和 类似 问题!
理解 Errors 和 Stack Traces
Ruby interpreter 给 error 或 程序 不 做 该做 的 事 = 该 戴 debugging 了 如果 程序 crash 重要 是 注意 error message 通常 有 clues 什么 错
例子:
def method1 method2 end def method2 puts invalid_variable end method1
跑 这 代码 得 到:
/tmp/stack.rb:6:in 'method2': undefined local variable or method 'invalid_variable' for main:Object (NameError) from /tmp/stack.rb:2:in 'method1' from /tmp/stack.rb:9:in ''
这 是 stack trace 一起 分析!
从 顶部 行 开始 这 是 actual error 发生 的 地方 但 不 代表 error condition 起源 这里 但 是 好 起点 调查
详细:
| Text | 描述 |
|---|---|
| /tmp/stack.rb:6 | 文件 和 行号 |
| in `method2‘ | 方法 名 |
| undefined local variable or method ‘invalid_variable‘ | 错误 信息 |
| main:Object | 类 名 |
| (NameError) | 异常 名 |
拆解 后 不 吓人 了 顺带 exception list 在这里 现在: stack trace 下面 每 行 告诉 代码 怎么 走到 这里 method chain 往下 找 最终 找到 main method
算法 处理 stack trace:
- 读 顶部 行 stack trace
- 如果 文件 是 你 的 project: 打开 faulting file 行号 指示 如果 不是 往下 找 直到 认出 文件
- 看 有 什么 obvious 跳出 修复 (找 error message 提到 的 东西)
- 如果 不 帮助 需要 更多 信息 如 受影响 变量 的 值
Debugging Ruby
基本 (不 代表 差) debugging technique 熟悉: dump 值 怀疑 variables 在 Ruby 用 puts 或 p p 等于 puts variable.inspect 有用 看 objects
例子:
Book = Struct.new(:title)
def find_book(title)
books = []
books << Book.new('Eloquent Ruby')
books.find { |b| b.title == title }
end
book = find_book('Eloquent Ruby')
p book # 印 我们 的 book object
book = find_book('POODR')
p book # 印 nil
book.name # 猜 会 发生 什么!
Digging Deeper with Pry
很多 variables 要 检查 加 puts 到处 不 实际 该 试 pry 用 pry 让 代码 停 在 特定 行 (breakpoint) 掉进 irb-like environment evaluate ruby code 在 project context 或 执行 pry commands
用 pry 很简单: 放 binding.pry 想装 breakpoint 要 require pry (require 'pry') 想 临时 用 叫 script 这样:
ruby -rpry app.rb
Rails app 不 太 有用 所以 加 pry 在 Gemfile 我 喜欢 有 macro/snippet editor 包含 require 同一 行 breakpoint 删 就 删 双方
掉进 pry session 长这样:

想 完全 退出 pry session 打 exit! 普通 exit 跑 程序 到 下一个 breakpoint pry 力量 不 止 这 用 ls command 看 object 有 methods 和 instance variables 什么

别忘 运行 help command 列出 所有 好东西!
另一个 Ruby Debugger: Byebug
Byebug 当 pry 替代 或 gdb-like debugger for Ruby 想 用 前者 放 byebug 代替 binding.pry 想 停 代码 缺点: 没 syntax highlighting
看 怎么 set breakpoints 和 debug 在 byebug!
通常 叫 help command 但 这案例 缺 信息:

所以 查 documentation 用 command break 和 line number set breakpoints info breakpoint 列表 breakpoints set 后 用 这些 commands 移动 程序 execution:
- step (进一 条 instruction, step into method calls)
- next (进一 条 instruction, 不 进 methods)
- continue (跑 到 结束 或 下一个 breakpoint)
按 enter 不打 command 重复 最后 一个 走 code 很 方便
When All Else Fails
休息 一下 花 很多 时间 还 看不出 解决 办法 回来 眼睛 亮 发现 解决 办法 在 面前 也 试 向 别人 解释 问题
有时 不 确定 问题 在 哪 还有 选项 比如 comment blocks of code 隔离 issue 如果 issue 消失 uncomment 部分 刚 comment 很 low-tech 但 可能 正是 需要 的
走 到 这 还 没 办法: 拿 出 大 炮 系统 tools 经常 有帮助
工具 之一 Wireshark inspect network traffic SSL-encrypted traffic mitm (Man in the middle) proxy 如 mitmproxy 可能 帮忙 curl 发起 HTTP connections 从 terminal debug invalid server responses
另一个 有用 tool: strace (linux only) 显示 所有 system calls app 做 用 -e option filter specific system calls 现代 替代 strace 是 sysdig
Warning! 避免 用 strace 在 production 因为 严重 降低 system under test performance
最后 如果 issue 看起来 来自 external gem 明显 步骤: inspect gem source code 用 gem open
结论
即使 debugging 不是 最 好玩 活动 有 充足 tools 和 techniques 让 它 容易 用 它们 帮助 你
分享 这 文章 如果 享受 这样 更多 人 学习! 🙂
谢谢