[![Ruby](https://github.com/ruby/debug/actions/workflows/ruby.yml/badge.svg?branch=master)](https://github.com/ruby/debug/actions/workflows/ruby.yml?query=branch%3Amaster) # debug.rb This library provides debugging functionality to Ruby. This debug.rb is replacement of traditional lib/debug.rb standard library which is implemented by `set_trace_func`. New debug.rb has several advantages: * Fast: No performance penalty on non-stepping mode and non-breakpoints. * Remote debugging: Support remote debugging natively. * UNIX domain socket * TCP/IP * VSCode/DAP integration ([VSCode rdbg Ruby Debugger - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg)) * Extensible: application can introduce debugging support with several methods * By `rdbg` command * By loading libraries with `-r` command line option * By calling Ruby's method explicitly * Misc * Support threads (almost done) and ractors (TODO). * Support suspending and entering to the console debugging with `Ctrl-C` at most of timing. * Show parameters on backtrace command. # Installation ``` $ gem install debug --pre ``` or specify `-Ipath/to/debug/lib` in `RUBYOPT` or each ruby command-line option, especially for debug this gem development. If you use Bundler, write the following line to your Gemfile. And use rdbg command with -c option. ``` gem "debug", ">= 1.0.0.beta" ``` ``` $ rdbg -c bundle exec ruby target.rb ``` # How to use ## Invoke with debugger You can run ruby program on debugger with the local debug console or the remote debug console. * (a) Run a ruby program with the local debug console * (b) Run a ruby program with the remote debug console by opening a network port * (b-1) Open with UNIX domain socket * (b-2) Open with TCP/IP port (b-1) is useful when you want to use debugging features after running the program. (b-2) is also useful when you don't have a ssh access for the Ruby process. To use debugging feature, you can have 3 ways. * (1) Use `rdbg` command * (2) Use `ruby -r debug...` command line option * (3) Write `require 'debug...'` in .rb files ### Local debug console #### (1) Use `rdbg` command ``` $ rdbg target.rb $ rdbg -- -r foo -e expr # -- is required to make clear rdbg options and ruby's options ``` #### (2) Use `-r debug/run` command line option ``` $ ruby -r debug/run target.rb ``` #### (3) Write `require 'debug...'` in .rb files ```ruby # target.rb require 'debug/run' # start the debug console # ... rest of program ... ``` ``` $ ruby target.rb ``` When you run the program with the debug console, you will see the debug console prompt `(rdbg)`. The debuggee program (`target.rb`) is suspended at the beginning of `target.rb`. Alternatively, start the debugger at a specific location in your program using `binding.break` (`binding.b` for short). ```ruby # target.rb require 'debug' # start the debugger # ... program ... binding.break # setup a breakpoint at this line # ... rest of program ... ``` ``` $ ruby target.rb ``` You can type any debugger's command described bellow. "c" or "continue" resume the debuggee program. You can suspend the debuggee program and show the debug console with `Ctrl-C`. The following example shows simple usage of the debug console. You can show the all variables ``` $ rdbg ~/src/rb/target.rb [1, 5] in /home/ko1/src/rb/target.rb => 1| a = 1 2| b = 2 3| c = 3 4| p [a + b + c] 5| --> #0 /home/ko1/src/rb/target.rb:1:in `
' (rdbg) info # Show all local variables %self => main a => nil b => nil c => nil (rdbg) p a # Same as p(a) => nil (rdbg) s # Step in ("s" is a short name of "step") [1, 5] in /home/ko1/src/rb/target.rb 1| a = 1 => 2| b = 2 3| c = 3 4| p [a + b + c] 5| --> #0 /home/ko1/src/rb/target.rb:2:in `
' (rdbg) # Repeat the last command ("step") [1, 5] in /home/ko1/src/rb/target.rb 1| a = 1 2| b = 2 => 3| c = 3 4| p [a + b + c] 5| --> #0 /home/ko1/src/rb/target.rb:3:in `
' (rdbg) # Repeat the last command ("step") [1, 5] in /home/ko1/src/rb/target.rb 1| a = 1 2| b = 2 3| c = 3 => 4| p [a + b + c] 5| --> #0 /home/ko1/src/rb/target.rb:4:in `
' (rdbg) info # Show all local variables %self => main a => 1 b => 2 c => 3 (rdbg) c # Continue the program ("c" is a short name of "continue") [6] ``` ### Remote debug (1) UNIX domain socket #### (1) Use `rdbg` command ``` $ rdbg --open target.rb # or rdbg -O target.rb for shorthand Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-5042) ``` #### (2) Use `-r debug/open` command line option ``` $ ruby -r debug/open target.rb Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-5042) ``` #### (3) Write `require 'debug/open'` in .rb files ```ruby # target.rb require 'debug/open' # open the debugger entry point by UNIX domain socket. # or require 'debug/server' # introduce remote debugging feature DEBUGGER__.open # open the debugger entry point by UNIX domain socket. # or DEBUGGER__.open_unix to specify UNIX domain socket. ``` ``` $ ruby target.rb Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-5042) ``` It runs target.rb and accept debugger connection within UNIX domain socket. The debuggee process waits for debugger connection at the beginning of `target.rb` like that: ``` $ rdbg -O ~/src/rb/target.rb DEBUGGER: Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-29828) DEBUGGER: wait for debugger connection... ``` You can attach the program with the following command: ``` $ rdbg --attach # or rdbg -A for shorthand [1, 4] in /home/ko1/src/rb/target.rb 1| (1..).each do |i| => 2| sleep 0.5 3| p i 4| end --> #0 [C] /home/ko1/src/rb/target.rb:2:in `sleep' #1 /home/ko1/src/rb/target.rb:2:in `block in
' {|i=17|} #2 [C] /home/ko1/src/rb/target.rb:1:in `each' # and 1 frames (use `bt' command for all frames) (rdb) ``` and you can input any debug commands. `c` (or `continue`) continues the debuggee process. You can detach the debugger from the debugger process with `quit` command. You can re-connect to the debuggee process by `rdbg -A` command again, and the debuggee process suspends the execution (and debugger can input any debug commands). If you don't want to stop the debuggee process at the beginning of debuggee process (`target.rb`), you can use the following to specify "non-stop" option. * Use `rdbg -n` option * Set the environment variable `RUBY_DEBUG_NONSTOP=1` If you are running multiple debuggee processes, the attach command (`rdbg -A`) shows the options like that: ``` $ rdbg --attach Please select a debug session: ruby-debug-ko1-19638 ruby-debug-ko1-19603 ``` and you need to specify one (copy and paste the name): ``` $ rdbg --attach ruby-debug-ko1-19638 ``` The socket file is located at * `RUBY_DEBUG_SOCK_DIR` environment variable if available. * `XDG_RUNTIME_DIR` environment variable if available. * `$HOME/.ruby-debug-sock` if `$HOME` is available. ### Remote debug (2) TCP/IP You can open the TCP/IP port instead of using UNIX domain socket. #### (1) Use `rdbg` command ``` $ rdbg -O --port=12345 target.rb # or $ rdbg --open --port=12345 target.rb Debugger can attach via TCP/IP (localhost:12345) ``` #### (2) Use `-r debug/open` command line option ``` $ RUBY_DEBUG_PORT=12345 ruby -r debug/open target.rb Debugger can attach via TCP/IP (localhost:12345) ``` #### (3) Write `require 'debug/open'` in .rb files ```ruby # target.rb require 'debug/open' # open the debugger entry point. ``` and run with environment variable RUBY_DEBUG_PORT ``` $ RUBY_DEBUG_PORT=12345 ruby target.rb Debugger can attach via TCP/IP (localhost:12345) ``` or ```ruby # target.rb require 'debug/server' # introduce remote debugging feature DEBUGGER__.open(port: 12345) # or DEBUGGER__.open_tcp(port: 12345) ``` ``` $ ruby target.rb Debugger can attach via TCP/IP (localhost:12345) ``` You can also specify the host with the `RUBY_DEBUG_HOST` environment variable. And also `DEBUGGER__.open` method accepts a `host:` keyword parameter. If the host is not given, `localhost` will be used. To attach the debuggee process, specify the port number (and hostname if needed) for the `rdbg --attach` (or `rdbg -A`) command. ``` $ rdbg --attach 12345 $ rdbg --attach hostname 12345 ``` ### Initial scripts If there are `~/.rdbgrc`, the file is loaded as initial scripts which contains debugger commands at the beginning of debug session. `RUBY_DEBUG_INIT_SCRIPT` environment variable can specify the initial script file. You can write configurations in a file. For example, you can set break points with `break file:123` in `~/.rdbgrc`. If there are `~/.rdbgrc.rb` is available, it is loaded as a ruby script at same timing. ### Configurations You can configure debugger's setting with environment variables and `config` command. You can write any configuration into `~/.rdbgrc` like: ``` config set log_level INFO config set no_color true ``` <% cat = nil; DEBUGGER__::CONFIG_SET.each do |key, (env, desc)| %> <% /\A(\w+): (.+)/ =~ desc; if cat != $1; cat = 1 %> * <%= $1 %> <% cat = $1; end %> * `<%= env %>` (`<%= key %>`): <%= $2 %><% end %> ## Debug command on the debug console * `Enter` repeats the last command (useful when repeating `step`s). * `Ctrl-D` is equal to `quit` command. * [debug command compare sheet - Google Sheets](https://docs.google.com/spreadsheets/d/1TlmmUDsvwK4sSIyoMv-io52BUUz__R5wpu-ComXlsw0/edit?usp=sharing) You can use the following debug commands. Each command should be written in 1 line. The `[...]` notation means this part can be eliminate. For example, `s[tep]` means `s` or `step` are valid command. `ste` is not valid. The `<...>` notation means the argument. <%= DEBUGGER__.help %> ## rdbg command help ``` <%= `exe/rdbg --help` %> ``` # Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/debug. Please also check the [contributing guideline](/CONTRIBUTING.md). # Acknowledgement * Some tests are based on [deivid-rodriguez/byebug: Debugging in Ruby 2](https://github.com/deivid-rodriguez/byebug)