The Art of Execution Control
for Ruby's Debugger
Koichi Sasada
Cookpad Inc.
RubyKaigi Takeout 2021

1About this talk• Introduce “debug.gem” https://github.com/ruby/debug• Newly created debugger for Ruby 2.6 and later• Will be bundled with Ruby 3.1 (Dec/2021)• Demonstrate “debug.gem”• Basic usage instructions• Advanced features• Show “How to make a debugger” on Ruby
• Introduction of recent features of TracePoint• Some techniques for a debugger2

About Koichi Sasada• Ruby interpreter developer employed by
Cookpad Inc. (2017-) with @mame• YARV (Ruby 1.9-)• Generational/Incremental GC (Ruby 2.1-)• Ractor (Ruby 3.0-)•…

• Ruby Association Director (2012-)• 2021 Call for Grant Proposals

3What is a debugger?
• A tool to help debugging• To investigate the cause of problems• To know the program live state• To understand the program• Basic features• CONTROL execution• STOP at breakpoints• STEP forward to the next line
• …

• QUERY program status
4

Ruby’s existing debuggers• lib/debug.rb• ruby -r debug script.rb• Standard library, but maybe nobody uses it• byebug• byebug script.rb• debase / ruby-debug-ide• Used by IDE (rubymine, vscode, …)

5

Why create yet another debugger?
• Performance• Existing debuggers slow with breakpoints• Recent TracePoint API support line-specific• Native support for remote execution and IDE• Native support for Ractors• (and I like to make this kind of tools)

6How to use a debugger?
• Set breakpoints.
• Run a program with a debugger.
• At the breakpoint, enter the debugger console.
• Use debug commands.
• Control execution• Move to next line.
• Continue the execution and stop at the next breakpoint.
• Set another breakpoint (e.g. stop when Exception is raised).

• Query the program status (e.g. see the local variables of another frame).
• Change the configuration (e.g. change coloring option).
7

Introduction of“debug.gem”

8Demo: Basic usage9

Demo: VSCode integration• Implement DAP (Debug Adapter Protocol) to communicate
IDEs (VSCode, …)

10

11

Start debugging with debug.gem• Use “rdbg” command• like traditional debuggers such as GDB/LLDB• rdbg script.rb• rdbg –c rake test
• rdbg --open … # open remote debugging port

• Require “debug*” files• like old lib/debug.rb• require “debug” == “rdbg --nonstop --no-sigint-hook”• require “debug/start” == “rdbg”• require “debug/open” == “rdbg --open”• Push IDE’s button
12Set a breakpoint• Use “break” command at the beginning• break 10 # break at 10 line on current file• break foo.rb:10 # break at the location• break MyClass#my_method # break at the method• break … if foo == bar # break if foo == bar• catch FooException # break at FooException is raised• Use “binding.break” method in your program• You can insert it like “binding.irb”• “binding.b” for short• You can write debug command in your program withbinding.b do: “debug_command”
13Set a breakpoint (cont.)• Use “break” command at the beginning• Do not need to modify the source code
• Cooperation with IDE/Editor (e.g. set BP with F9 on VSCode)• Use “binding.break” method in your program• Straight forward for some Ruby users• Control debugger from debuggee

14

Demo: Combination with program and debugger
binding.break is a breakpoint like binding.irb# enable “trace line” feature while bar()def foobinding.break do: ‘trace line’bar()biding.break do: ‘trace off line’end

15

Advanced features• Remote debugging• Postmortem debugging• Event tracing• Record and replay debugging

16

Demo: Remote debugging
Connect over network• Easy to open remote debug port and attach• rdbg --open script.rb• rdbg -O script.rb• require ‘debug/open’ # in script• rdbg --attach• rdbg -A• Debug deamon like process• Query the process status like sigdump but more details

17Demo: Postmortem debugging
Debug dead Ruby process

19

Demo: Event tracing
Show interesting events20

Demo: Record and replay debugging
Backward stepping execution

21

22

Performancedef fib n
if n < 0raise # breakpoint here!!
elsif n<2nelse
fib(n-1)+fib(n-2)
end
endrequire 'benchmark'Benchmark.bm{|x|x.report{ fib(35) }}Without
breakpointWith
breakpoint

ruby0.93

N/A

rdbg (debug.gem)

0.92

0.92

byebug

1.23

75.15

RubyMine

0.97

22.66

221.88285.99old lib/debug.rb

Execution time (sec)ruby 3.0.1p64
rdbg 1.0.0.rc2
byebug 11.1.3
RubyMine 2021.2.1 w/ debase 0.2.5.beta2

Intel(R) Core(TM) i7-10810U CPU, Windows 10, WSL223

How to make a debugger
on Ruby?

24

Key feature: TracePoint• “TracePoint” hooks many events in Ruby
• Replacement of “set_trace_func” from Ruby 1.9• “line”, “call”, “return”, …

• Set TracePoint at the specific location (from Ruby 2.6)
• “enable(target: …, target_line: …)”• “target” is specified by a Method/Proc/ISeq (bytecode)def foo ab = a + 1 # line 2b# line 3
end
TracePoint.new(:line){|tp| p tp}.enable(target: method(:foo), target_line: 3){foo 10} #=> #<TracePoint:line t.rb:3 in `foo'>25

You can implement a line breakpoint …?
• Debug command: break file:line• Set a breakpoint at file:line• Question: How to collect the code for “target:”?
• Answer: Use ObjectSpace to collect all ISeqs (bytecode)26How to implement step execution?
• Use “TracePoint” without “#enable(target:…)”• “#enable” method without “target:” keyword will hook at any hookpoints• Set “:line” event and trap the execution

27

How to implement capturing frames
information?
• Use internal API “rb_debug_inspector_*”• You can use it from Ruby with “debug_inspector.gem”.
• With C-extension we can control the capturing target.

28

How to support Threads/Ractors?
• We can’t share the objects between Ractors→ Existing debugger can’t support Ractors.
• Ractor programming is hard
→ Debugger can be important for ractor debugging

29

Session management thread andcommunication
Debuggee process

Suspended on a bp

Remoteconsole

Remote UI
thread(if needed)

SessionmanagerthreadRunning thread

Suspend
Running thread

Running thread30

Session management thread andcommunication
Debuggee process

Suspended on a bp

Running thread

Ractor #2

Suspend

SessionmanagerthreadSuspend
Running thread

Running thread

Running thread

Ractor #3

Running thread

main Ractor
But not available now…

31

Acknowledgements• Naoto Ono san (@ono-max) implements test-frameworks forthe debugger. It is part of his GSoC project.
• Stan Lo san (@st0012) submits tremendous patches toimprove the debugger usability such as coloring and so on
based on his debugger trials. Also, he makes many tests forthe debugger.
• Ruby committers helps me to design and implement thedebugger

32

Conclusion• “debug.gem” is newly created Ruby debugger from scratch• Useful features on modern UI.
• No performance penalties.

• “gem install debug” now!
• And give us your feedback.
• I love to introduce the debugger on your meetup, please contact me.

• Ruby already has useful features like “TracePoint” for thedebugger.
• Ractor supports is not available, now working on.
33