From byebug to ruby/debug

st0012

Stan Lo

Posted on August 10, 2022

From byebug to ruby/debug

Switching to a new debugger and potentially changing your debugging process could be scary. So I hope this post can help you get familiar with ruby/debug and determine whether to switch to it.

(In the rest of the article, I'll use debug to refer to ruby/debug)

Disclaimers:

  • I'm not as experienced with byebug as with debug. So please let me know if I listed incorrect/outdated information.
  • Its purpose is to give a higher-level comparison. To learn more about debug's specific usages, please check its official documentation.
  • It doesn't contain all the features but should already cover most of them.

Advantages of debug

Before we get into individual features, I want to quickly mention some advantages of debug:

Disadvantages of debug

  • Less flexible on thread control
  • Doesn't work well with Fiber yet
  • Doesn't have pry integration like pry-byebug
  • No per-project configuration

byebug vs debug

Installation

(Although Ruby 3.1 comes with debug v1.4, I recommend always using its latest release)

byebug debug
Supported Ruby version 2.5+ 2.6+
Gem install gem install byebug gem install debug
Bundler gem "byebug" gem "debug"
Dependencies No irb, reline
Has C extension Yes Yes

Start Debugging

byebug debug
Via executable byebug foo.rb rdbg foo.rb
Via debugger statement byebug binding.break, binding.b, debugger (the same)
Via requiring No require "debug/start"

User Experience Features

byebug debug
Colorizing No Yes
Command history Yes Yes
Help command h[elp] h[elp]
Edit source file edit edit

Evaluation

Code evaluation in REPL is more or less the same between byebug and debug, that is:

  • If the expression doesn't match a console command, like my_method(arg), it'll be evaluated as Ruby code
  • If the expression matches a console command, like n, you can use console commands to evaluate it
byebug debug
Execute debugger commands <cmd> <cmd>
Avoid expression/command conflicts eval <expr> pp <expr>, p <expr>, or eval <expr>

Flow Control & Frame Navigation

debug provides the same stepping commands as byebug does. So byebug users can switch to debug without learning new behaviors.

byebug debug
Step in s[tep] s[tep]
Step over n[ext] n[ext]
Finish fin[ish] fin[ish]
Move to <id> frame f[rame] <id> f[rame] <id>
Move up a frame up up
Move down a frame down down
Move up n frames up <n> No
Move down n frames down <n> No
Continue the program c[ontinue] c[ontinue]
Quit the debugger q[uit] q[uit]
Kill the program kill kill

Thread Control

As previously mentioned, debug stops all threads when suspended and doesn't allow per-thread management at the moment. So it has simpler thread-related commands.

byebug debug
Thread suspension Only the current thread All threads
List all threads th[read] l th[read]
Switch to thread th[read] switch <id> th[read] <id>
Stop a thread th[read] stop <id> No
Resume a thread th[read] resume <id> No

Breakpoint

Although byebug already has decent breakpoints support, debug takes it to another level by:

  • Allowing breakpoints to execute commands with pre: and do: options
  • Supporting call-location-based triggering condition with the path: option
  • Supporting specialized catch and watch breakpoints

Setting Breakpoints

byebug debug
On line b[reak] <line> b[reak] <line>
On file:line b[reak] <file>:<line> b[reak] <file>:<line>
On a method b[reak] <class>#<method> b[reak] <class>#<method>
With a condition b[reak] ... if <expr> b[reak] ... if: <expr>
With a path condition No b[reak] ... path: /path/
To run a command and continue No b[reak] ... do: <cmd>
To run a command before stopping No b[reak] ... pre: <cmd>
Exception breakpoint No catch <ExceptionClass>
Instance variable watch breakpoint No watch <@ivar>

Managing Breakpoints

byebug debug
List all breakpoints info breakpoints b[reak]
Set a breakpoint b[reak] ... b[reak] ...
Delete a breakpoint del[ete] <id> del[ete] <id>
Delete all breakpoints del[ete] del[ete]

Information

Backtrace

Backtrace in debug contains values of method call or block arguments. This small improvement can save you from inspecting them manually between frames.

The filtering support can help you focus on relevant frames and ignore the ones from frameworks or libraries.

byebug debug
Show backtrace where, backtrace, bt backtrace, bt
Displaying method/block arguments in backtrace No Yes
Filter backtrace No bt /regexp/
Limit the number of backtraces No bt <n>

Varibles/Constants

byebug debug
Show local varibales var local info l
Show instance variables var instance info i
Show global varibales var global info g
Show constants var const info c
Show only arguments var args No (included in info l)
Filter variables/constants by names No info ... /regexp/

Methods

While debug doesn't have a dedicated command to list an object's methods, it has a ls command that's similar to
irb or pry's.

byebug debug
obj.methods method instance obj No
obj.methods(false) method obj.class ls obj

Tracing

I will write another dedicated article to introduce debug's tracing functionalities, but I encourage you to already give them a try from time to time, especially:

  • trace exception - to monitor exceptions raised in your code. You may be surprised by the exceptions raised and rescued in your application under the surface.
  • trace object <expr> - to observe an object's activities: receiving a method call or being passed to a method call.

    trace object

byebug debug
Line tracer set linetrace trace line
Global variable tracer tarcevar No
Allow multiple tracers No Yes
Method call tracer No trace call
Exceptions tracer No trace exception
Ruby object tracer No trace object <expr>
Filter tracing output No trace ... /regexp/
Disable tracer set linetrace false trace off [tracer type]
Disable the specific tracer No trace off <id>

Configuration

debug supports a wide range of configurations to make it fit your needs. But it doesn't support per-project RC files due to security concerns.

byebug debug
List all configs No config
Show a config show <name> config show <name>
Set a config set <name> <value> config set <name> <value>
RC file name .byebugrc .rdbgrc (or .rdbgrc.rb for Ruby script)
RC file locations $HOME and project root $HOME

Remote Debugging

Remote debugging functionality is becoming crucial as we start containerizing our development environment and don't always have direct standard IO access.
It's also crucial for connecting to different debugger clients like VSCode or Chrome.

So if you have needs in this area, migrating to debug is your best option.

byebug debug
Connect via TCP/IP Yes Yes
Connect via Unix Domain Socket No Yes
VSCode integration through Debug Adapter Protocol (DAP) No Yes
Chrome integration through Chrome DevTools Protocol (CDP) No Yes

Wrapping Up

To most users, switching from byebug to debug should take little effort. But the potential productivity gain from debug's features could be significant.

byebug is a great debugger and has served the community well for many years.
But as its development slows down (last release was 2 years ago) and debug receives constant updates from the Ruby core, the gap between them will only widen.

So adding debug to your toolbelt and exploring its powerful features will be a great productivity investment for the long term.

References

πŸ’– πŸ’ͺ πŸ™… 🚩
st0012
Stan Lo

Posted on August 10, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related