From a6e6551ec39eb0d63abe1d82a491a50d5afbe675 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Thu, 14 Jul 2022 10:54:10 +0100 Subject: [PATCH] Refresh README --- README.md | 786 ++++++++++---------------------------- Rakefile | 1 + docs/configuration.md | 78 ++++ docs/remote_debugging.md | 258 +++++++++++++ misc/README.md.erb | 658 ++++++++----------------------- misc/configuration.md.erb | 40 ++ 6 files changed, 724 insertions(+), 1097 deletions(-) create mode 100644 docs/configuration.md create mode 100644 docs/remote_debugging.md create mode 100644 misc/configuration.md.erb diff --git a/README.md b/README.md index 8ca1f7a1a..a83d08223 100644 --- a/README.md +++ b/README.md @@ -2,549 +2,288 @@ # debug.rb -This library provides debugging functionality to Ruby (MRI) 2.6 and later. - -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](#remote-debugging): Support remote debugging natively. - * UNIX domain socket (UDS) - * TCP/IP - * Integration with rich debugger frontends - - Frontend | [Console](https://github.com/ruby/debug#invoke-as-a-remote-debuggee) | [VSCode](https://github.com/ruby/debug#vscode-integration) | [Chrome DevTool](#chrome-devtool-integration) | +This library provides debugging functionality to Ruby (MRI) 2.6 and later. It has several advantages: + +- Fast: No performance penalty on non-stepping mode and non-breakpoints. +- Native remote debugging support: + - [UNIX domain socket](/docs/remote_debugging.md#unix-domain-socket-uds) (UDS) + - [TCP/IP](/docs/remote_debugging.md#tcpip) + - Integration with rich debugger frontends + Frontend | [Console](/docs/remote_debugging.md#debugger-console) | [VSCode](/docs/remote_debugging.md#vscode) | [Chrome DevTools](/docs/remote_debugging.md#chrome-devtool-integration) | ---|---|---|---| Connection | UDS, TCP/IP | UDS, TCP/IP | TCP/IP | Requirement | No | [vscode-rdbg](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) | Chrome | - -* Extensible: application can introduce debugging support with several ways: - * 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. - * Support recording & reply debugging. + - See the [remote debugging guide](/docs/remote_debugging.md) for details +- Flexible: Users can use the debugger in multiple ways + - Through requiring files - like `require "debug"` + - Through the [`rdbg` executable](#the-rdbg-executable) + - Through Ruby APIs + - See [activate the debugger in your program](#activate-the-debugger-in-your-program) for details +- Configurable: + - It provides many configurations and is configurable through the `~/.rdbgrc` file + - See the [configuration guide](/docs/configuration.md) for more information # Installation ``` $ gem install debug ``` - -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. -``` -gem "debug", ">= 1.0.0" +```rb +gem "debug", ">= 1.6.0" ``` -(The version constraint is important; `debug < 1.0.0` is an older, -abandoned gem that is completely different from this product.) +# Usage -# HOW TO USE +The debugger is designed to support a wide range of use cases, so you have many ways to use it. -To use a debugger, roughly you will do the following steps: +But a debugging session usually consists of 4 steps: -1. Set breakpoints. -2. Run a program with the debugger. -3. At the breakpoint, enter the debugger console. -4. Use debug commands. - * [Evaluate Ruby expressions](#evaluate) (e.g. `p lvar` to see the local variable `lvar`). - * [Query the program status](#information) (e.g. `info` to see information about the current frame). - * [Control program flow](#control-flow) (e.g. move to the another line with `step`, to the next line with `next`). - * [Set another breakpoint](#breakpoint) (e.g. `catch Exception` to set a breakpoint that'll be triggered when `Exception` is raised). - * [Activate tracing in your program](#trace) (e.g. `trace call` to trace method calls). - * [Change the configuration](#configuration-1) (e.g. `config set no_color true` to disable coloring). - * Continue the program (`c` or `continue`) and goto 3. +1. [Activate the debugger in your program](#activate-the-debugger-in-your-program) +1. [Set breakpoints](#set-breakpoints) + - Through [`binding.break`](#the-bindingbreak-method) + - Or [breakpoint commands](#breakpoint) +1. Execute/continue your program and wait for it to hit the breakpoints +1. [Start debugging](#start-debugging) + - Here's the [full command list](#console-commands) + - You can also type `help` or `help ` in the console to see commands -## Invoke with the debugger -There are several options for (1) and (2). Please choose your favorite way. +### Remote debugging -### Modify source code with [`binding.break`](#bindingbreak-method) (similar to `binding.pry` or `binding.irb`) +You can connect the debugger to your program remotely through UNIX socket or TCP/IP. +To learn more, please check the [remote debugging guide](/docs/remote_debugging.md). -If you can modify the source code, you can use the debugger by adding `require 'debug'` at the top of your program and putting [`binding.break`](#bindingbreak-method) method into lines where you want to stop as breakpoints like `binding.pry` and `binding.irb`. +### VSCode and Chrome Devtools integration -You can also use its 2 aliases in the same way: +If you want to use VSCode/Chrome integration, the steps will be slightly different. Please also check their dedicated sections: -- `binding.b` -- `debugger` +- [VSCode](/docs/remote_debugging.md#vscode) +- [Chrome DevTools](/docs/remote_debugging.md#chrome-devtool-integration) -After that, run the program as usual and you will enter the debug console at breakpoints you inserted. +## Activate the debugger in your program -The following example shows the demonstration of [`binding.break`](#bindingbreak-method). +As mentioned earlier, you can use various ways to integrate the debugger with your program. Here's a simple breakdown: -```shell -$ cat target.rb # Sample program -require 'debug' - -a = 1 -b = 2 -binding.break # Program will stop here -c = 3 -d = 4 -binding.break # Program will stop here -p [a, b, c, d] - -$ ruby target.rb # Run the program normally. -DEBUGGER: Session start (pid: 7604) -[1, 10] in target.rb - 1| require 'debug' - 2| - 3| a = 1 - 4| b = 2 -=> 5| binding.break # Now you can see it stops at this line - 6| c = 3 - 7| d = 4 - 8| binding.break - 9| p [a, b, c, d] - 10| -=>#0
at target.rb:5 - -(rdbg) info locals # You can show local variables -=>#0
at target.rb:5 -%self => main -a => 1 -b => 2 -c => nil -d => nil - -(rdbg) continue # Continue the execution -[3, 11] in target.rb - 3| a = 1 - 4| b = 2 - 5| binding.break - 6| c = 3 - 7| d = 4 -=> 8| binding.break # Again the program stops here - 9| p [a, b, c, d] - 10| - 11| __END__ -=>#0
at target.rb:8 - -(rdbg) info locals # And you can see the updated local variables -=>#0
at target.rb:8 -%self => main -a => 1 -b => 2 -c => 3 -d => 4 - -(rdbg) continue -[1, 2, 3, 4] -``` - -### Invoke the program from the debugger as a traditional debuggers - -If you don't want to modify the source code, you can set breakpoints with a debug command `break` (`b` for short). -Using `rdbg` command (or `bundle exec rdbg`) to launch the program without any modifications, you can run the program with the debugger. - -```shell -$ cat target.rb # Sample program -a = 1 -b = 2 -c = 3 -d = 4 -p [a, b, c, d] - -$ rdbg target.rb # run like `ruby target.rb` -DEBUGGER: Session start (pid: 7656) -[1, 7] in target.rb -=> 1| a = 1 - 2| b = 2 - 3| c = 3 - 4| d = 4 - 5| p [a, b, c, d] - 6| - 7| __END__ -=>#0
at target.rb:1 +Start at program start | `rdbg` | require | debugger API (after `require "debug/session"`) +---|---|---|---| +Yes | `rdbg` | `require "debug/start"` | `DEBUGGER__.start` +No | `rdbg --nonstop` | `require "debug"` | `DEBUGGER__.start(nonstop: true)` -(rdbg) -``` +But here are the 2 most common use cases: -`rdbg` command suspends the program at the beginning of the given script (`target.rb` in this case) and you can use debug commands. `(rdbg)` is prompt. Let's set breakpoints on line 3 and line 5 with `break` command (`b` for short). +### `require "debug"` -```shell -(rdbg) break 3 # set breakpoint at line 3 -#0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line) +Similar to `byebug` or `pry`, once you've required `debug`, you can start setting breakpoints with the [`binding.break`](#the-bindingbreak-method) method. -(rdbg) b 5 # set breakpoint at line 5 -#1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line) +### The `rdbg` executable -(rdbg) break # show all registered breakpoints -#0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line) -#1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line) -``` - -You can see that two breakpoints are registered. Let's continue the program by `continue` command. +You can also start your program with the `rdbg` executable, which will enter a debugging session at the beginning of your program by default. ```shell -(rdbg) continue +❯ rdbg target.rb [1, 7] in target.rb - 1| a = 1 - 2| b = 2 -=> 3| c = 3 - 4| d = 4 - 5| p [a, b, c, d] - 6| - 7| __END__ -=>#0
at target.rb:3 - -Stop by #0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line) - +=> 1| def foo # stops at the beginning of the program + 2| 10 + 3| end + 4| + 5| foo + 6| + 7| binding.break +=>#0
at target.rb:1 (rdbg) ``` -You can see that we can stop at line 3. -Let's see the local variables with `info` command, and continue. -You can also confirm that the program will suspend at line 5 and you can use `info` command again. +If you don't want to stop your program until it hits a breakpoint, you can use `rdbg --nonstop` instead (or `-n` for short). ```shell -(rdbg) info -=>#0
at target.rb:3 -%self => main -a => 1 -b => 2 -c => nil -d => nil - -(rdbg) continue -[1, 7] in target.rb - 1| a = 1 - 2| b = 2 - 3| c = 3 - 4| d = 4 -=> 5| p [a, b, c, d] - 6| - 7| __END__ -=>#0
at target.rb:5 - -Stop by #1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line) - -(rdbg) info -=>#0
at target.rb:5 -%self => main -a => 1 -b => 2 -c => 3 -d => 4 - -(rdbg) continue -[1, 2, 3, 4] +❯ rdbg --nonstop target.rb +[2, 7] in target.rb + 2| 10 + 3| end + 4| + 5| foo + 6| +=> 7| binding.break # stops at the first breakpoint +=>#0
at target.rb:7 +(rdbg) ``` -By the way, using `rdbg` command you can suspend your application with `C-c` (SIGINT) and enter the debug console. -It will help that if you want to know what the program is doing. - -### Use `rdbg` with commands written in Ruby - If you want to run a command written in Ruby like like `rake`, `rails`, `bundle`, `rspec` and so on, you can use `rdbg -c` option. -* Without `-c` option, `rdbg ` means that `` is Ruby script and invoke it like `ruby ` with the debugger. -* With `-c` option, `rdbg -c ` means that `` is command in `PATH` and simply invoke it with the debugger. +- Without `-c` option, `rdbg ` expects `` to be a Ruby script and invokes it like `ruby ` with the debugger. +- With `-c` option, `rdbg -c ` expects `` to be a command in `PATH` and simply invoke it with the debugger. Examples: -* `rdbg -c -- rails server` -* `rdbg -c -- bundle exec ruby foo.rb` -* `rdbg -c -- bundle exec rake test` -* `rdbg -c -- ruby target.rb` is same as `rdbg target.rb` - -NOTE: `--` is needed to separate the command line options for `rdbg` and invoking command. For example, `rdbg -c rake -T` is recognized like `rdbg -c -T -- rake`. It should be `rdbg -c -- rake -T`. +- `rdbg target.rb` +- `rdbg -c -- rails server` +- `rdbg -c -- bundle exec ruby foo.rb` +- `rdbg -c -- bundle exec rake test` +- `rdbg -c -- ruby target.rb` is same as `rdbg target.rb` -NOTE: If you want to use bundler (`bundle` command), you need to write `gem debug` line in your `Gemfile`. +> **Note** +> `--` is needed to separate the command line options for `rdbg` and invoking command. For example, `rdbg -c rake -T` is recognized like `rdbg -c -T -- rake`. It should be `rdbg -c -- rake -T`. -### Using VSCode +> **Note** +> If you want to use bundler (`bundle` command), you need to write `gem debug` line in your `Gemfile`. -Like other languages, you can use this debugger on the VSCode. +
Expand to see all options 👇 -1. Install [VSCode rdbg Ruby Debugger - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) -2. Open `.rb` file (e.g. `target.rb`) -3. Register breakpoints with "Toggle breakpoint" in Run menu (or type F9 key) -4. Choose "Start debugging" in "Run" menu (or type F5 key) -5. You will see a dialog "Debug command line" and you can choose your favorite command line your want to run. -6. Chosen command line is invoked with `rdbg -c` and VSCode shows the details at breakpoints. - -Please refer [Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging) for operations on VSCode. - -You can configure the extension in `.vscode/launch.json`. -Please see the extension page for more details. - -## Remote debugging - -You can use this debugger as a remote debugger. For example, it will help the following situations: - -* Your application does not run on TTY and it is hard to use `binding.pry` or `binding.irb`. - * Your application is running on Docker container and there is no TTY. - * Your application is running as a daemon. - * Your application uses pipe for STDIN or STDOUT. -* Your application is running as a daemon and you want to query the running status (checking a backtrace and so on). - -You can run your application as a remote debuggee and the remote debugger console can attach to the debuggee anytime. - -### Invoke as a remote debuggee - -There are multiple ways to run your program as a debuggee: - -Stop at program start | [`rdbg` option](https://github.com/ruby/debug#rdbg---open-or-rdbg--o-for-short) | [require](https://github.com/ruby/debug#require-debugopen-in-a-program) | [debugger API](https://github.com/ruby/debug#start-by-method) ----|---|---|---| -Yes | `rdbg --open` | `require "debug/open"` | `DEBUGGER__.open` -No | `rdbg --open --nonstop` | `require "debug/open_nonstop"` | `DEBUGGER__.open(nonstop: true)` - -#### `rdbg --open` (or `rdbg -O` for short) - -You can run a script with `rdbg --open target.rb` command and run a `target.rb` as a debuggee program. It also opens the network port and suspends at the beginning of `target.rb`. - -```shell -$ exe/rdbg --open target.rb -DEBUGGER: Session start (pid: 7773) -DEBUGGER: Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773) -DEBUGGER: wait for debugger connection... ``` +exe/rdbg [options] -- [debuggee options] -By default, `rdbg --open` uses UNIX domain socket and generates path name automatically (`/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773` in this case). - -You can connect to the debuggee with `rdbg --attach` command (`rdbg -A` for short). - -```shell -$ rdbg -A -[1, 7] in target.rb -=> 1| a = 1 - 2| b = 2 - 3| c = 3 - 4| d = 4 - 5| p [a, b, c, d] - 6| - 7| __END__ -=>#0
at target.rb:1 - -(rdbg:remote) -``` - -If there is no other opening ports on the default directory, `rdbg --attach` command chooses the only one opening UNIX domain socket and connect to it. If there are more files, you need to specify the file. - -When `rdbg --attach` connects to the debuggee, you can use any debug commands (set breakpoints, continue the program and so on) like local debug console. When an debuggee program exits, the remote console will also terminate. - -NOTE: If you use `quit` command, only remote console exits and the debuggee program continues to run (and you can connect it again). If you want to exit the debuggee program, use `kill` command. - -If you want to use TCP/IP for the remote debugging, you need to specify the port and host with `--port` like `rdbg --open --port 12345` and it binds to `localhost:12345`. - -To connect to the debuggee, you need to specify the port. - -```shell -$ rdbg --attach 12345 -``` - -If you want to choose the host to bind, you can use `--host` option. -Note that all messages communicated between the debugger and the debuggee are *NOT* encrypted so please use remote debugging carefully. - -#### `require 'debug/open'` in a program - -If you can modify the program, you can open debugging port by adding `require 'debug/open'` line in the program. - -If you don't want to stop the program at the beginning, you can also use `require 'debug/open_nonstop'`. -Using `debug/open_nonstop` is useful if you want to open a backdoor to the application. -However, it is also danger because it can become another vulnerability. -Please use it carefully. - -By default, UNIX domain socket is used for the debugging port. To use TCP/IP, you can set the `RUBY_DEBUG_PORT` environment variable. - -```shell -$ RUBY_DEBUG_PORT=12345 ruby target.rb -``` - -### Integration with external debugger frontend - -You can attach with external debugger frontend with VSCode and Chrome. - -``` -$ rdbg --open=[frontend] target.rb -``` - -will open a debug port and `[frontend]` can attach to the port. - -Also `open` command allows opening the debug port. - -#### VSCode integration - -([vscode-rdbg v0.0.9](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) or later is required) - -If you don't run a debuggee Ruby process on VSCode, you can attach with VSCode later with the following steps. - -`rdbg --open=vscode` opens the debug port and tries to invoke the VSCode (`code` command). - -``` -$ rdbg --open=vscode target.rb -DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/ruby-debug-sock-1000/ruby-debug-ko1-27706) -DEBUGGER: wait for debugger connection... -Launching: code /tmp/ruby-debug-vscode-20211014-27706-gd7e85/ /tmp/ruby-debug-vscode-20211014-27706-gd7e85/README.rb -DEBUGGER: Connected. -``` +Debug console mode: + -n, --nonstop Do not stop at the beginning of the script. + -e DEBUG_COMMAND Execute debug command at the beginning of the script. + -x, --init-script=FILE Execute debug command in the FILE. + --no-rc Ignore ~/.rdbgrc + --no-color Disable colorize + --no-sigint-hook Disable to trap SIGINT + -c, --command Enable command mode. + The first argument should be a command name in $PATH. + Example: 'rdbg -c bundle exec rake test' -And it tries to invoke the new VSCode window and VSCode starts attaching to the debuggee Ruby program automatically. + -O, --open=[FRONTEND] Start remote debugging with opening the network port. + If TCP/IP options are not given, a UNIX domain socket will be used. + If FRONTEND is given, prepare for the FRONTEND. + Now rdbg, vscode and chrome is supported. + --sock-path=SOCK_PATH UNIX Domain socket path + --port=PORT Listening TCP/IP port + --host=HOST Listening TCP/IP host + --cookie=COOKIE Set a cookie for connection -You can also use `open vscode` command in REPL. + Debug console mode runs Ruby program with the debug console. -``` -$ rdbg target.rb -[1, 8] in target.rb - 1| -=> 2| p a = 1 - 3| p b = 2 - 4| p c = 3 - 5| p d = 4 - 6| p e = 5 - 7| - 8| __END__ -=>#0
at target.rb:2 -(rdbg) open vscode # command -DEBUGGER: wait for debugger connection... -DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/ruby-debug-sock-1000/ruby-debug-ko1-28337) -Launching: code /tmp/ruby-debug-vscode-20211014-28337-kg9dm/ /tmp/ruby-debug-vscode-20211014-28337-kg9dm/README.rb -DEBUGGER: Connected. -``` + 'rdbg target.rb foo bar' starts like 'ruby target.rb foo bar'. + 'rdbg -- -r foo -e bar' starts like 'ruby -r foo -e bar'. + 'rdbg -c rake test' starts like 'rake test'. + 'rdbg -c -- rake test -t' starts like 'rake test -t'. + 'rdbg -c bundle exec rake test' starts like 'bundle exec rake test'. + 'rdbg -O target.rb foo bar' starts and accepts attaching with UNIX domain socket. + 'rdbg -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234. + 'rdbg -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234. + 'rdbg target.rb -O chrome --port 1234' starts and accepts connecting from Chrome Devtools with localhost:1234. -If the machine which runs the Ruby process doesn't have a `code` command, the following message will be shown: +Attach mode: + -A, --attach Attach to debuggee process. -``` -(rdbg) open vscode -DEBUGGER: wait for debugger connection... -DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/ruby-debug-sock-1000/ruby-debug-ko1-455) -Launching: code /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb -DEBUGGER: Can not invoke the command. -Use the command-line on your terminal (with modification if you need). + Attach mode attaches the remote debug console to the debuggee process. - code /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb + 'rdbg -A' tries to connect via UNIX domain socket. + If there are multiple processes are waiting for the + debugger connection, list possible debuggee names. + 'rdbg -A path' tries to connect via UNIX domain socket with given path name. + 'rdbg -A port' tries to connect to localhost:port via TCP/IP. + 'rdbg -A host port' tries to connect to host:port via TCP/IP. -If your application is running on a SSH remote host, please try: +Other options: + -h, --help Print help + --util=NAME Utility mode (used by tools) + --stop-at-load Stop immediately when the debugging feature is loaded. - code --remote ssh-remote+[SSH hostname] /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb +NOTE + All messages communicated between a debugger and a debuggee are *NOT* encrypted. + Please use the remote debugging feature carefully. ``` -and try to use proposed commands. - -Note that you can attach with `rdbg --attach` and continue REPL debugging. +
-#### Chrome DevTool integration +## Set breakpoints -With `rdbg --open=chrome` command will show the following message. +### The `binding.break` method -``` -$ rdbg target.rb --open=chrome -DEBUGGER: Debugger can attach via TCP/IP (127.0.0.1:43633) -DEBUGGER: With Chrome browser, type the following URL in the address-bar: +`binding.break` (and its aliases `binding.b` and `debugger`) set breakpoints at the written line. - devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=127.0.0.1:57231/b32a55cd-2eb5-4c5c-87d8-b3dfc59d80ef - -DEBUGGER: wait for debugger connection... +```rb +❯ ruby -rdebug target.rb +[2, 7] in target.rb + 2| 10 + 3| end + 4| + 5| foo + 6| +=> 7| binding.break +=>#0
at target.rb:7 +(rdbg) ``` -Type `devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=127.0.0.1:57231/b32a55cd-2eb5-4c5c-87d8-b3dfc59d80ef` in the address-bar on Chrome browser, and you can continue the debugging with chrome browser. - -Also `open chrome` command works like `open vscode`. - -For more information about how to use Chrome debugging, you might want to read [here](https://developer.chrome.com/docs/devtools/). - -## Configuration - -You can configure the debugger's behavior with debug commands and environment variables. -When the debug session is started, initial scripts are loaded so you can put your favorite configurations in the initial scripts. - -### Configuration list +#### Advanced usages -You can configure debugger's behavior with environment variables and `config` command. Each configuration has environment variable and the name which can be specified by `config` command. +- If `do: 'command'` is specified, the debugger will -``` -# configuration example -config set log_level INFO -config set no_color true -``` + 1. Stop the program + 1. Run the `command` as a debug command + 1. Continue the program. + It is useful if you only want to call a debug command and don't want to stop there. + ```rb + def initialize + @a = 1 + binding.b do: 'watch @a' + end + ``` -* UI - * `RUBY_DEBUG_LOG_LEVEL` (`log_level`): Log level same as Logger (default: WARN) - * `RUBY_DEBUG_SHOW_SRC_LINES` (`show_src_lines`): Show n lines source code on breakpoint (default: 10) - * `RUBY_DEBUG_SHOW_FRAMES` (`show_frames`): Show n frames on breakpoint (default: 2) - * `RUBY_DEBUG_USE_SHORT_PATH` (`use_short_path`): Show shorten PATH (like $(Gem)/foo.rb) (default: false) - * `RUBY_DEBUG_NO_COLOR` (`no_color`): Do not use colorize (default: false) - * `RUBY_DEBUG_NO_SIGINT_HOOK` (`no_sigint_hook`): Do not suspend on SIGINT (default: false) - * `RUBY_DEBUG_NO_RELINE` (`no_reline`): Do not use Reline library (default: false) - * `RUBY_DEBUG_NO_HINT` (`no_hint`): Do not show the hint on the REPL (default: false) + In this case, the debugger will register a watch breakpoint for `@a` and continue to run. -* CONTROL - * `RUBY_DEBUG_SKIP_PATH` (`skip_path`): Skip showing/entering frames for given paths - * `RUBY_DEBUG_SKIP_NOSRC` (`skip_nosrc`): Skip on no source code lines (default: false) - * `RUBY_DEBUG_KEEP_ALLOC_SITE` (`keep_alloc_site`): Keep allocation site and p, pp shows it (default: false) - * `RUBY_DEBUG_POSTMORTEM` (`postmortem`): Enable postmortem debug (default: false) - * `RUBY_DEBUG_FORK_MODE` (`fork_mode`): Control which process activates a debugger after fork (both/parent/child) (default: both) - * `RUBY_DEBUG_SIGDUMP_SIG` (`sigdump_sig`): Sigdump signal (default: false) +- If `pre: 'command'` is specified, the debugger will + 1. Stop the program + 1. Run the `command` as a debug command + 1. Keep the console open -* BOOT - * `RUBY_DEBUG_NONSTOP` (`nonstop`): Nonstop mode (default: false) - * `RUBY_DEBUG_STOP_AT_LOAD` (`stop_at_load`): Stop at just loading location (default: false) - * `RUBY_DEBUG_INIT_SCRIPT` (`init_script`): debug command script path loaded at first stop - * `RUBY_DEBUG_COMMANDS` (`commands`): debug commands invoked at first stop. commands should be separated by ';;' - * `RUBY_DEBUG_NO_RC` (`no_rc`): ignore loading ~/.rdbgrc(.rb) (default: false) - * `RUBY_DEBUG_HISTORY_FILE` (`history_file`): history file (default: ~/.rdbg_history) - * `RUBY_DEBUG_SAVE_HISTORY` (`save_history`): maximum save history lines (default: 10000) + It is useful if you have repeated operations to perform before the debugging at the breakpoint -* REMOTE - * `RUBY_DEBUG_OPEN` (`open`): Open remote port (same as `rdbg --open` option) - * `RUBY_DEBUG_PORT` (`port`): TCP/IP remote debugging: port - * `RUBY_DEBUG_HOST` (`host`): TCP/IP remote debugging: host (default: 127.0.0.1) - * `RUBY_DEBUG_SOCK_PATH` (`sock_path`): UNIX Domain Socket remote debugging: socket path - * `RUBY_DEBUG_SOCK_DIR` (`sock_dir`): UNIX Domain Socket remote debugging: socket directory - * `RUBY_DEBUG_LOCAL_FS_MAP` (`local_fs_map`): Specify local fs map - * `RUBY_DEBUG_SKIP_BP` (`skip_bp`): Skip breakpoints if no clients are attached (default: false) - * `RUBY_DEBUG_COOKIE` (`cookie`): Cookie for negotiation - * `RUBY_DEBUG_CHROME_PATH` (`chrome_path`): Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64)) + ```rb + def foo + binding.b pre: 'info locals' + ... + end + ``` -* OBSOLETE - * `RUBY_DEBUG_PARENT_ON_FORK` (`parent_on_fork`): Keep debugging parent process on fork (default: false) + In this case, the debugger will display local variable information automatically so you don't need to type it repeatedly. -There are other environment variables: +## Start debugging -* `NO_COLOR`: If the value is set, set `RUBY_DEBUG_NO_COLOR` ([NO_COLOR: disabling ANSI color output in various Unix commands](https://no-color.org/)). -* `RUBY_DEBUG_ENABLE`: If the value is `0`, do not enable debug.gem feature. -* `RUBY_DEBUG_ADDED_RUBYOPT`: Remove this value from `RUBYOPT` at first. This feature helps loading debug.gem with `RUBYOPT='-r debug/...'` and you don't want to derive it to child processes. In this case you can set `RUBY_DEBUG_ADDED_RUBYOPT='-r debug/...'` (same value) and this string will be deleted from `RUBYOPT` at first. -* `RUBY_DEBUG_EDITOR` or `EDITOR`: An editor used by `edit` debug command. -* `RUBY_DEBUG_BB`: Define `Kernel#bb` method which is alias of `Kernel#debugger`. +Once you're in the debugger console, you can start debugging with it. But here are some useful tips: -### Initial scripts +- `` without debug command is almost same as `pp `. + - If the input line `` does *NOT* start with any debug command, the line `` will be evaluated as a Ruby expression and the result will be printed with `pp` method. So that the input `foo.bar` is same as `pp foo.bar`. + - If `` is recognized as a debug command, of course it is not evaluated as a Ruby expression, but is executed as debug command. + - For example, you can not evaluate such single letter local variables `i`, `b`, `n`, `c` because they are single letter debug commands. Use `p i` instead. + - So the author (Koichi Sasada) recommends to use `p`, `pp` or `eval` command to evaluate the Ruby expression everytime. +- `Enter` without any input repeats the last command for some commands, such as `step` or `next`. This is useful when step-debugging. +- `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) -If there is `~/.rdbgrc`, the file is loaded as an initial script (which contains debug commands) when the debug session is started. +### Command & expression parsing -* `RUBY_DEBUG_INIT_SCRIPT` environment variable can specify the initial script file. -* You can specify the initial script with `rdbg -x initial_script` (like gdb's `-x` option). +Because the debugger supports both Ruby expression evaluation and dozens of commands, name collision happens. -Initial scripts are useful to write your favorite configurations. -For example, you can set break points with `break file:123` in `~/.rdbgrc`. +So here're a few rules explaining how the debugger interprets your input: -If there are `~/.rdbgrc.rb` is available, it is also loaded as a ruby script at same timing. +- If `.split(" ")` does **NOT** start with any debugger command (e.g. `my_var` or `foo bar`), it will be evaluated as `pp ` +- If `.split(" ")` starts with a command or a command shortcut (like `info` and `i`), it will be treated as a command instead. -## Debug command on the debug console +Some examples: -On the debug console, you can use the following debug commands. + - `foo.bar` is same as `pp foo.bar`. + - `info arg` are `i arg` are considered as ` arg` + - `info(arg)` is considered as `pp self.info(arg)` + - `i` is considered as `` + - `pp i` prints `i` -There are additional features: -* `` without debug command is almost same as `pp `. - * If the input line `` does *NOT* start with any debug command, the line `` will be evaluated as a Ruby expression and the result will be printed with `pp` method. So that the input `foo.bar` is same as `pp foo.bar`. - * If `` is recognized as a debug command, of course it is not evaluated as a Ruby expression, but is executed as debug command. For example, you can not evaluate such single letter local variables `i`, `b`, `n`, `c` because they are single letter debug commands. Use `p i` instead. - * So the author (Koichi Sasada) recommends to use `p`, `pp` or `eval` command to evaluate the Ruby expression everytime. -* `Enter` without any input repeats the last command (useful when repeating `step`s) for some commands. -* `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) +### Console commands 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 this part can be eliminated. For example, `s[tep]` means `s` or `step` are both valid commands. `ste` is not valid. The `<...>` notation means the argument. +Here's a [Google sheet](https://docs.google.com/spreadsheets/d/1TlmmUDsvwK4sSIyoMv-io52BUUz__R5wpu-ComXlsw0/edit?usp=sharing) for comparing this and other Ruby debuggers' commands. + ### Control flow * `s[tep]` @@ -566,7 +305,7 @@ The `<...>` notation means the argument. * Run til the program reaches given location or the end of the current frame. * `u[ntil] * Run til the program invokes a method ``. `` can be a regexp with `/name/`. -* `c[ontinue]` +* `c` or `cont` or `continue` * Resume the program. * `q[uit]` or `Ctrl-D` * Finish debugger (with the debuggee process on non-remote debugging). @@ -773,142 +512,9 @@ The `<...>` notation means the argument. * Show help for the given command. -## Debugger API - -### Start debugging - -#### Start by requiring a library - -You can start debugging without `rdbg` command by requiring the following libraries: - -* `require 'debug'`: Same as `rdbg --nonstop --no-sigint-hook`. -* `require 'debug/start'`: Same as `rdbg`. -* `require 'debug/open'`: Same as `rdbg --open`. -* `require 'debug/open_nonstop'`: Same as `rdbg --open --nonstop`. - -You need to require one of them at the very beginning of the application. -Using `ruby -r` (for example `ruby -r debug/start target.rb`) is another way to invoke with debugger. - -NOTE: Until Ruby 3.0, there is old `lib/debug.rb` standard library. So that if this gem is not installed, or if `Gemfile` missed to list this gem and `bundle exec` is used, you will see the following output: - -```shell -$ ruby -r debug -e0 -.../2.7.3/lib/ruby/2.7.0/x86_64-linux/continuation.so: warning: callcc is obsolete; use Fiber instead -Debug.rb -Emacs support available. - -.../2.7.3/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:162: if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?) -(rdb:1) -``` - -`lib/debug.rb` was not maintained well in recent years, and the purpose of this library is to rewrite old `lib/debug.rb` with recent techniques. - -#### Start by method - -After loading `debug/session`, you can start debug session with the following methods. They are convenient if you want to specify debug configurations in your program. - -* `DEBUGGER__.start(**kw)`: start debug session with local console. -* `DEBUGGER__.open(**kw)`: open debug port with configuration (without configurations open with UNIX domain socket) -* `DEBUGGER__.open_unix(**kw)`: open debug port with UNIX domain socket -* `DEBUGGER__.open_tcp(**kw)`: open debug port with TCP/IP - -For example: - -```ruby -require 'debug/session' -DEBUGGER__.start(no_color: true, # disable colorize - log_level: 'INFO') # Change log_level to INFO - -... # your application code -``` - -### `binding.break` method - -`binding.break` (or `binding.b`) set breakpoints at written line. It also has several keywords. - -If `do: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command and continue the program. -It is useful if you only want to call a debug command and don't want to stop there. - -``` -def initialize - @a = 1 - binding.b do: 'watch @a' -end -``` - -On this case, register a watch breakpoint for `@a` and continue to run. - -If `pre: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command, and keep suspend. -It is useful if you have operations before suspend. - -``` -def foo - binding.b pre: 'p bar()' - ... -end -``` - -On this case, you can see the result of `bar()` every time you stop there. - -## rdbg command help +# Configuration -``` -exe/rdbg [options] -- [debuggee options] - -Debug console mode: - -n, --nonstop Do not stop at the beginning of the script. - -e DEBUG_COMMAND Execute debug command at the beginning of the script. - -x, --init-script=FILE Execute debug command in the FILE. - --no-rc Ignore ~/.rdbgrc - --no-color Disable colorize - --no-sigint-hook Disable to trap SIGINT - -c, --command Enable command mode. - The first argument should be a command name in $PATH. - Example: 'rdbg -c bundle exec rake test' - - -O, --open=[FRONTEND] Start remote debugging with opening the network port. - If TCP/IP options are not given, a UNIX domain socket will be used. - If FRONTEND is given, prepare for the FRONTEND. - Now rdbg, vscode and chrome is supported. - --sock-path=SOCK_PATH UNIX Domain socket path - --port=PORT Listening TCP/IP port - --host=HOST Listening TCP/IP host - --cookie=COOKIE Set a cookie for connection - - Debug console mode runs Ruby program with the debug console. - - 'rdbg target.rb foo bar' starts like 'ruby target.rb foo bar'. - 'rdbg -- -r foo -e bar' starts like 'ruby -r foo -e bar'. - 'rdbg -c rake test' starts like 'rake test'. - 'rdbg -c -- rake test -t' starts like 'rake test -t'. - 'rdbg -c bundle exec rake test' starts like 'bundle exec rake test'. - 'rdbg -O target.rb foo bar' starts and accepts attaching with UNIX domain socket. - 'rdbg -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234. - 'rdbg -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234. - 'rdbg target.rb -O chrome --port 1234' starts and accepts connecting from Chrome Devtools with localhost:1234. - -Attach mode: - -A, --attach Attach to debuggee process. - - Attach mode attaches the remote debug console to the debuggee process. - - 'rdbg -A' tries to connect via UNIX domain socket. - If there are multiple processes are waiting for the - debugger connection, list possible debuggee names. - 'rdbg -A path' tries to connect via UNIX domain socket with given path name. - 'rdbg -A port' tries to connect to localhost:port via TCP/IP. - 'rdbg -A host port' tries to connect to host:port via TCP/IP. - -Other options: - -h, --help Print help - --util=NAME Utility mode (used by tools) - --stop-at-load Stop immediately when the debugging feature is loaded. - -NOTE - All messages communicated between a debugger and a debuggee are *NOT* encrypted. - Please use the remote debugging feature carefully. - -``` +See the [configuration guide](/docs/configuration.md) for more information. # Additional Resources @@ -924,5 +530,5 @@ 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) -* Several codes in `server_cdp.rb` are based on [geoffreylitt/ladybug: Visual Debugger](https://github.com/geoffreylitt/ladybug) +- Some tests are based on [deivid-rodriguez/byebug: Debugging in Ruby 2](https://github.com/deivid-rodriguez/byebug) +- Several codes in `server_cdp.rb` are based on [geoffreylitt/ladybug: Visual Debugger](https://github.com/geoffreylitt/ladybug) diff --git a/Rakefile b/Rakefile index 99a5a2a10..2972684a7 100644 --- a/Rakefile +++ b/Rakefile @@ -18,6 +18,7 @@ file 'README.md' => ['lib/debug/session.rb', 'lib/debug/config.rb', require_relative 'lib/debug/session' require 'erb' File.write 'README.md', ERB.new(File.read('misc/README.md.erb')).result + File.write 'docs/configuration.md', ERB.new(File.read('misc/configuration.md.erb')).result puts 'README.md is updated.' end diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 000000000..874c2c918 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,78 @@ +# Configuration + +You can configure the debugger's behavior with the `config` command and environment variables. + +Every configuration has a corresponding environment variable, for example: + +``` +config set log_level INFO # RUBY_DEBUG_LOG_LEVEL=INFO +config set no_color true # RUBY_DEBUG_NO_COLOR=true +``` + + + +- UI + - `RUBY_DEBUG_LOG_LEVEL` (`log_level`): Log level same as Logger (default: WARN) + - `RUBY_DEBUG_SHOW_SRC_LINES` (`show_src_lines`): Show n lines source code on breakpoint (default: 10) + - `RUBY_DEBUG_SHOW_FRAMES` (`show_frames`): Show n frames on breakpoint (default: 2) + - `RUBY_DEBUG_USE_SHORT_PATH` (`use_short_path`): Show shorten PATH (like $(Gem)/foo.rb) (default: false) + - `RUBY_DEBUG_NO_COLOR` (`no_color`): Do not use colorize (default: false) + - `RUBY_DEBUG_NO_SIGINT_HOOK` (`no_sigint_hook`): Do not suspend on SIGINT (default: false) + - `RUBY_DEBUG_NO_RELINE` (`no_reline`): Do not use Reline library (default: false) + - `RUBY_DEBUG_NO_HINT` (`no_hint`): Do not show the hint on the REPL (default: false) + +- CONTROL + - `RUBY_DEBUG_SKIP_PATH` (`skip_path`): Skip showing/entering frames for given paths + - `RUBY_DEBUG_SKIP_NOSRC` (`skip_nosrc`): Skip on no source code lines (default: false) + - `RUBY_DEBUG_KEEP_ALLOC_SITE` (`keep_alloc_site`): Keep allocation site and p, pp shows it (default: false) + - `RUBY_DEBUG_POSTMORTEM` (`postmortem`): Enable postmortem debug (default: false) + - `RUBY_DEBUG_FORK_MODE` (`fork_mode`): Control which process activates a debugger after fork (both/parent/child) (default: both) + - `RUBY_DEBUG_SIGDUMP_SIG` (`sigdump_sig`): Sigdump signal (default: false) + +- BOOT + - `RUBY_DEBUG_NONSTOP` (`nonstop`): Nonstop mode (default: false) + - `RUBY_DEBUG_STOP_AT_LOAD` (`stop_at_load`): Stop at just loading location (default: false) + - `RUBY_DEBUG_INIT_SCRIPT` (`init_script`): debug command script path loaded at first stop + - `RUBY_DEBUG_COMMANDS` (`commands`): debug commands invoked at first stop. commands should be separated by ';;' + - `RUBY_DEBUG_NO_RC` (`no_rc`): ignore loading ~/.rdbgrc(.rb) (default: false) + - `RUBY_DEBUG_HISTORY_FILE` (`history_file`): history file (default: ~/.rdbg_history) + - `RUBY_DEBUG_SAVE_HISTORY` (`save_history`): maximum save history lines (default: 10000) + +- REMOTE + - `RUBY_DEBUG_OPEN` (`open`): Open remote port (same as `rdbg --open` option) + - `RUBY_DEBUG_PORT` (`port`): TCP/IP remote debugging: port + - `RUBY_DEBUG_HOST` (`host`): TCP/IP remote debugging: host (default: 127.0.0.1) + - `RUBY_DEBUG_SOCK_PATH` (`sock_path`): UNIX Domain Socket remote debugging: socket path + - `RUBY_DEBUG_SOCK_DIR` (`sock_dir`): UNIX Domain Socket remote debugging: socket directory + - `RUBY_DEBUG_LOCAL_FS_MAP` (`local_fs_map`): Specify local fs map + - `RUBY_DEBUG_SKIP_BP` (`skip_bp`): Skip breakpoints if no clients are attached (default: false) + - `RUBY_DEBUG_COOKIE` (`cookie`): Cookie for negotiation + - `RUBY_DEBUG_CHROME_PATH` (`chrome_path`): Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64)) + +- OBSOLETE + - `RUBY_DEBUG_PARENT_ON_FORK` (`parent_on_fork`): Keep debugging parent process on fork (default: false) + +There are other environment variables: + +* `NO_COLOR`: If the value is set, set `RUBY_DEBUG_NO_COLOR` ([NO_COLOR: disabling ANSI color output in various Unix commands](https://no-color.org/)). +* `RUBY_DEBUG_ENABLE`: If the value is `0`, do not enable debug.gem feature. +* `RUBY_DEBUG_ADDED_RUBYOPT`: Remove this value from `RUBYOPT` at first. This feature helps loading debug.gem with `RUBYOPT='-r debug/...'` and you don't want to derive it to child processes. In this case you can set `RUBY_DEBUG_ADDED_RUBYOPT='-r debug/...'` (same value) and this string will be deleted from `RUBYOPT` at first. +* `RUBY_DEBUG_EDITOR` or `EDITOR`: An editor used by `edit` debug command. +* `RUBY_DEBUG_BB`: Define `Kernel#bb` method which is alias of `Kernel#debugger`. + +## Initialization scripts + +If you want to run certain commands or set configurations for every debugging session automatically, you can put them into the `~/.rdbgrc` file. + +If you want to run additional initial scripts, you can also, + +- Use `RUBY_DEBUG_INIT_SCRIPT` environment variable can specify the initial script file. +- Specify the initial script with `rdbg -x initial_script`. + +Initial scripts are useful to write your favorite configurations. For example, + +``` +config set use_short_path true # Use $(Gem)/gem_content to replace the absolute path of gem files +``` + +Finally, you can also write the initial script in Ruby with the file name `~/.rdbgrc.rb`. diff --git a/docs/remote_debugging.md b/docs/remote_debugging.md new file mode 100644 index 000000000..afbf202a6 --- /dev/null +++ b/docs/remote_debugging.md @@ -0,0 +1,258 @@ +# Introduction + +This debugger's remote debugging functionality can help the following situations: + +- Your application is running on Docker container and there is no TTY. +- Your application is running as a daemon. +- Your application uses pipe for STDIN or STDOUT. + +Remote debugging with the this debugger means: + +1. [Invoke your program as a remote debuggee](#invoke-program-as-a-remote-debuggee) + - Through the [rdbg](#rdbg---open-or-rdbg--o-for-short) executable + - Through [`require "debug/open"`](#require-debugopen) + - Through [`DEBUGGER__.open`](#debugger__open) +2. [Connect the debuggee to supported platforms](#connect-to-a-remote-debuggee): + - [Debugger console](#debugger-console) + - [VSCode](#vscode) (or other DAP supporting clients) + - [Chrome DevTools](#chrome-devtool-integration) (or other CDP supporting clients) + +## Quick start with VSCode or Chrome DevTools + +If you use VSCode or Chrome DevTools **and** are running the program and debugger in the same environment (e.g. on the same machine without using container), `--open` flag can do both steps 1 and 2 for you. + +For example: + +```shell +$ rdbg --open=vscode target.rb +# chrome users can specify --open=chrome instead +``` + +It will open a debug port, launch VSCode, and attach to it automatically. + +# Invoke program as a remote debuggee + +There are multiple ways to run your program as a debuggee: + +Stop at program start | [`rdbg` option](#rdbg---open-or-rdbg--o-for-short) | [require](#require-debugopen) | [debugger API](#debuggeropen) +---|---|---|---| +Yes | `rdbg --open` | `require "debug/open"` | `DEBUGGER__.open` +No | `rdbg --open --nonstop` | `require "debug/open_nonstop"` | `DEBUGGER__.open(nonstop: true)` + +## `rdbg --open` (or `rdbg -O` for short) + +When `--open` flag is used without specifying a client, it'll start the program as remote debuggee and wait for the connection: + +```shell +$ rdbg --open target.rb +DEBUGGER: Session start (pid: 7773) +DEBUGGER: Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773) +DEBUGGER: wait for debugger connection... +``` + +By default, `rdbg --open` uses UNIX domain socket and generates path name automatically (`/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773` in this case). + +## `require 'debug/open'` + +If you can modify the program, you can open a debugging port by adding `require 'debug/open'` to the program. + +If you don't want to stop the program when `require 'debug/open'` is executed, you can instead use `require 'debug/open_nonstop'`. + +> **Note** +> Please do not leave these requires in your program as they will allow other people to connect to it. + +## `DEBUGGER__.open` + +After requiring `debug/session`, you can start a debug session using the following methods. +They are convenient if you want to specify debug configurations with Ruby code. + +- `DEBUGGER__.open(**kw)`: opens a debug port using the specified configuration (by default opens a UNIX domain socket). +- `DEBUGGER__.open_unix(**kw)`: opens a debug port through UNIX domain socket. +- `DEBUGGER__.open_tcp(**kw)`: opens a debug port through TCP/IP. + +For example: + +```ruby +require 'debug/session' +DEBUGGER__.open + +# your application code +``` + +## UNIX Domain Socket (UDS) + +By default, the debugger uses UNIX domain socket for remote connection. You can customize the socket directory and socket path with environment variables: + +- `RUBY_DEBUG_SOCK_DIR` - This is where the debugger will create and look for socket files. +- `RUBY_DEBUG_SOCK_PATH` - This is the socket file's absolute path that will be used to connect to the debugger. + - Currently, the debugger expects socket files to have a `ruby-debug-#{USER}` prefix during socket lookup. But this may be changed in the future. + +> **Note** +> Make sure these variables are visible and synced between both the debuggee and the client. Otherwise, the client may not find the correct socket file. + +In addition to the environment variables, you can also configure those options depend on how the debuggee is invoked: + +- For `rdbg` executable, you can use the `--sock-path` flag +- For `DEBUGGER__.open`, you can pass `sock_dir:` and `sock_path:` keyword arguments + +## TCP/IP + +If you want to use TCP/IP for remote debugging, you need to specify the port and host through the `--port` and `--host` flags: + +```shell +rdbg --open --port 12345 # binds to 127.0.0.1:12345 +rdbg --open --port 12345 --host myhost.dev # binds to myhost.dev:12345 +``` + +Alternatively, you can also specify port and host with `RUBY_DEBUG_PORT` and `RUBY_DEBUG_HOST` environment variables: + +```shell +$ RUBY_DEBUG_PORT=12345 RUBY_DEBUG_HOST=localhost ruby target.rb +``` + +# Connect to a remote debuggee + +## Debugger console + +You can connect to the debuggee with `rdbg --attach` command (`rdbg -A` for short). + +```shell +$ rdbg --attach +[1, 7] in target.rb +=> 1| a = 1 + 2| b = 2 + 3| c = 3 + 4| d = 4 + 5| p [a, b, c, d] + 6| + 7| __END__ +=>#0
at target.rb:1 + +(rdbg:remote) +``` + +If there is only one opened UNIX domain socket in the socket directory, `rdbg --attach` will connect to it automatically. + +If there are more than one socket file, you need to specify the socket name after the `--attach` flag: + +```shell +❯ rdbg --attach +Please select a debug session: + ruby-debug-st0012-64507 + ruby-debug-st0012-68793 + +❯ rdbg --attach ruby-debug-st0012-64507 +[1, 1] in target.rb +=> 1| a = 1 +=>#0
at target.rb:1 +(rdbg:remote) +``` + +When `rdbg --attach` connects to the debuggee, you can use debugger commands like in a local debugger console. When a debuggee program exits, the remote console will also terminate. + +> **Note** +> If you use the `quit` command, only the remote console exits and the debuggee program continues to run (and you can connect to it again). If you want to exit the debuggee program as well, use the `kill` command instead. + +### Through TCP/IP + +To connect to the debuggee via TCP/IP, you need to specify the port. + +```shell +$ rdbg --attach 12345 +``` + +If you want to choose the host to bind, you can use the `--host` option. + +> **Note** +> The connection between the debugger and the debuggee is **NOT** encrypted. So please use remote debugging carefully. + +## VSCode + +You can use this debugger with VSCode. First, install the [VSCode rdbg Ruby Debugger](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) extension (`v0.0.9` or later is required). + +You can configure the extension in `.vscode/launch.json`. Please see the extension page for more details. + +You can also check [Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging) for more information about VSCode's debugger features. + +### Debug a Ruby script + +1. Open a `.rb` file (e.g. `target.rb`) +1. Register breakpoints using "Toggle breakpoint" in the `Run` menu (or press F9) +1. Choose "Start debugging" in the `Run` menu (or press F5) +1. You will see a dialog "Debug command line" and you can choose your favorite command line your want to run. +1. Chosen command line is invoked with `rdbg -c` and VSCode shows the details at breakpoints. + +### Debug a Rails/web application + +1. Open the application in VSCode and add some breakpoints. +1. [Start your program as a remote debuggee](#invoke-program-as-a-remote-debuggee) + - For example: `bundle exec rdbg --open -n -c -- bundle exec rails s` + - `--open` or `-O` means starting the debugger in server mode + - `-n` means don't stop at the beginning of the program, which is usually somewhere at rubygems, not helpful + - `-c` means you'll be running a Ruby-based command +1. Go back to VSCode's `Run and Debug` panel, you should see a grean play button +1. Click the dropdown besides the button and select `Attach with rdbg` +1. Click the play button (or press F5) +1. It should now connect the VSCode to the debugger +1. Send some requests and it should stop at your breakpoints + +### The `open` command + +You can use `open vscode` command in REPL, which is the same as `--open=vscode`. + +```shell +$ rdbg target.rb +[1, 8] in target.rb + 1| +=> 2| p a = 1 + 3| p b = 2 + 4| p c = 3 + 5| p d = 4 + 6| p e = 5 + 7| + 8| __END__ +=>#0
at target.rb:2 +(rdbg) open vscode # command +DEBUGGER: wait for debugger connection... +DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/ruby-debug-sock-1000/ruby-debug-ko1-28337) +Launching: code /tmp/ruby-debug-vscode-20211014-28337-kg9dm/ /tmp/ruby-debug-vscode-20211014-28337-kg9dm/README.rb +DEBUGGER: Connected. +``` + +If the environment doesn't have a `code` command, the following message will be shown: + +```shell +(rdbg) open vscode +DEBUGGER: wait for debugger connection... +DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/ruby-debug-sock-1000/ruby-debug-ko1-455) +Launching: code /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb +DEBUGGER: Can not invoke the command. +Use the command-line on your terminal (with modification if you need). + + code /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb + +If your application is running on a SSH remote host, please try: + + code --remote ssh-remote+[SSH hostname] /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb + +``` + +## Chrome DevTool integration + +With `rdbg --open=chrome` command will show the following message. + +```shell +$ rdbg --open=chrome target.rb +DEBUGGER: Debugger can attach via TCP/IP (127.0.0.1:43633) +DEBUGGER: With Chrome browser, type the following URL in the address-bar: + + devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=127.0.0.1:57231/b32a55cd-2eb5-4c5c-87d8-b3dfc59d80ef + +DEBUGGER: wait for debugger connection... +``` + +Type `devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=127.0.0.1:57231/b32a55cd-2eb5-4c5c-87d8-b3dfc59d80ef` in the address-bar on Chrome browser, and you can continue the debugging with chrome browser. + +You can use the `open chrome` command in a `rdbg` console to open the debug session in Chrome. + +For more information about how to use Chrome debugging, refer to [the documentation for Chrome DevTools](https://developer.chrome.com/docs/devtools/). diff --git a/misc/README.md.erb b/misc/README.md.erb index 99195e138..f0808269f 100644 --- a/misc/README.md.erb +++ b/misc/README.md.erb @@ -2,595 +2,239 @@ # debug.rb -This library provides debugging functionality to Ruby (MRI) 2.6 and later. - -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](#remote-debugging): Support remote debugging natively. - * UNIX domain socket (UDS) - * TCP/IP - * Integration with rich debugger frontends - - Frontend | [Console](https://github.com/ruby/debug#invoke-as-a-remote-debuggee) | [VSCode](https://github.com/ruby/debug#vscode-integration) | [Chrome DevTool](#chrome-devtool-integration) | +This library provides debugging functionality to Ruby (MRI) 2.6 and later. It has several advantages: + +- Fast: No performance penalty on non-stepping mode and non-breakpoints. +- Native remote debugging support: + - [UNIX domain socket](/docs/remote_debugging.md#unix-domain-socket-uds) (UDS) + - [TCP/IP](/docs/remote_debugging.md#tcpip) + - Integration with rich debugger frontends + Frontend | [Console](/docs/remote_debugging.md#debugger-console) | [VSCode](/docs/remote_debugging.md#vscode) | [Chrome DevTools](/docs/remote_debugging.md#chrome-devtool-integration) | ---|---|---|---| Connection | UDS, TCP/IP | UDS, TCP/IP | TCP/IP | Requirement | No | [vscode-rdbg](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) | Chrome | - -* Extensible: application can introduce debugging support with several ways: - * 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. - * Support recording & reply debugging. + - See the [remote debugging guide](/docs/remote_debugging.md) for details +- Flexible: Users can use the debugger in multiple ways + - Through requiring files - like `require "debug"` + - Through the [`rdbg` executable](#the-rdbg-executable) + - Through Ruby APIs + - See [activate the debugger in your program](#activate-the-debugger-in-your-program) for details +- Configurable: + - It provides many configurations and is configurable through the `~/.rdbgrc` file + - See the [configuration guide](/docs/configuration.md) for more information # Installation ``` $ gem install debug ``` - -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. -``` -gem "debug", ">= 1.0.0" +```rb +gem "debug", ">= 1.6.0" ``` -(The version constraint is important; `debug < 1.0.0` is an older, -abandoned gem that is completely different from this product.) +# Usage -# HOW TO USE +The debugger is designed to support a wide range of use cases, so you have many ways to use it. -To use a debugger, roughly you will do the following steps: +But a debugging session usually consists of 4 steps: -1. Set breakpoints. -2. Run a program with the debugger. -3. At the breakpoint, enter the debugger console. -4. Use debug commands. - * [Evaluate Ruby expressions](#evaluate) (e.g. `p lvar` to see the local variable `lvar`). - * [Query the program status](#information) (e.g. `info` to see information about the current frame). - * [Control program flow](#control-flow) (e.g. move to the another line with `step`, to the next line with `next`). - * [Set another breakpoint](#breakpoint) (e.g. `catch Exception` to set a breakpoint that'll be triggered when `Exception` is raised). - * [Activate tracing in your program](#trace) (e.g. `trace call` to trace method calls). - * [Change the configuration](#configuration-1) (e.g. `config set no_color true` to disable coloring). - * Continue the program (`c` or `continue`) and goto 3. +1. [Activate the debugger in your program](#activate-the-debugger-in-your-program) +1. [Set breakpoints](#set-breakpoints) + - Through [`binding.break`](#the-bindingbreak-method) + - Or [breakpoint commands](#breakpoint) +1. Execute/continue your program and wait for it to hit the breakpoints +1. [Start debugging](#start-debugging) + - Here's the [full command list](#console-commands) + - You can also type `help` or `help ` in the console to see commands -## Invoke with the debugger -There are several options for (1) and (2). Please choose your favorite way. +### Remote debugging -### Modify source code with [`binding.break`](#bindingbreak-method) (similar to `binding.pry` or `binding.irb`) +You can connect the debugger to your program remotely through UNIX socket or TCP/IP. +To learn more, please check the [remote debugging guide](/docs/remote_debugging.md). -If you can modify the source code, you can use the debugger by adding `require 'debug'` at the top of your program and putting [`binding.break`](#bindingbreak-method) method into lines where you want to stop as breakpoints like `binding.pry` and `binding.irb`. +### VSCode and Chrome Devtools integration -You can also use its 2 aliases in the same way: +If you want to use VSCode/Chrome integration, the steps will be slightly different. Please also check their dedicated sections: -- `binding.b` -- `debugger` +- [VSCode](/docs/remote_debugging.md#vscode) +- [Chrome DevTools](/docs/remote_debugging.md#chrome-devtool-integration) -After that, run the program as usual and you will enter the debug console at breakpoints you inserted. +## Activate the debugger in your program -The following example shows the demonstration of [`binding.break`](#bindingbreak-method). +As mentioned earlier, you can use various ways to integrate the debugger with your program. Here's a simple breakdown: -```shell -$ cat target.rb # Sample program -require 'debug' - -a = 1 -b = 2 -binding.break # Program will stop here -c = 3 -d = 4 -binding.break # Program will stop here -p [a, b, c, d] - -$ ruby target.rb # Run the program normally. -DEBUGGER: Session start (pid: 7604) -[1, 10] in target.rb - 1| require 'debug' - 2| - 3| a = 1 - 4| b = 2 -=> 5| binding.break # Now you can see it stops at this line - 6| c = 3 - 7| d = 4 - 8| binding.break - 9| p [a, b, c, d] - 10| -=>#0
at target.rb:5 - -(rdbg) info locals # You can show local variables -=>#0
at target.rb:5 -%self => main -a => 1 -b => 2 -c => nil -d => nil - -(rdbg) continue # Continue the execution -[3, 11] in target.rb - 3| a = 1 - 4| b = 2 - 5| binding.break - 6| c = 3 - 7| d = 4 -=> 8| binding.break # Again the program stops here - 9| p [a, b, c, d] - 10| - 11| __END__ -=>#0
at target.rb:8 - -(rdbg) info locals # And you can see the updated local variables -=>#0
at target.rb:8 -%self => main -a => 1 -b => 2 -c => 3 -d => 4 - -(rdbg) continue -[1, 2, 3, 4] -``` - -### Invoke the program from the debugger as a traditional debuggers - -If you don't want to modify the source code, you can set breakpoints with a debug command `break` (`b` for short). -Using `rdbg` command (or `bundle exec rdbg`) to launch the program without any modifications, you can run the program with the debugger. - -```shell -$ cat target.rb # Sample program -a = 1 -b = 2 -c = 3 -d = 4 -p [a, b, c, d] - -$ rdbg target.rb # run like `ruby target.rb` -DEBUGGER: Session start (pid: 7656) -[1, 7] in target.rb -=> 1| a = 1 - 2| b = 2 - 3| c = 3 - 4| d = 4 - 5| p [a, b, c, d] - 6| - 7| __END__ -=>#0
at target.rb:1 - -(rdbg) -``` +Start at program start | `rdbg` | require | debugger API (after `require "debug/session"`) +---|---|---|---| +Yes | `rdbg` | `require "debug/start"` | `DEBUGGER__.start` +No | `rdbg --nonstop` | `require "debug"` | `DEBUGGER__.start(nonstop: true)` -`rdbg` command suspends the program at the beginning of the given script (`target.rb` in this case) and you can use debug commands. `(rdbg)` is prompt. Let's set breakpoints on line 3 and line 5 with `break` command (`b` for short). +But here are the 2 most common use cases: -```shell -(rdbg) break 3 # set breakpoint at line 3 -#0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line) +### `require "debug"` -(rdbg) b 5 # set breakpoint at line 5 -#1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line) +Similar to `byebug` or `pry`, once you've required `debug`, you can start setting breakpoints with the [`binding.break`](#the-bindingbreak-method) method. -(rdbg) break # show all registered breakpoints -#0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line) -#1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line) -``` +### The `rdbg` executable -You can see that two breakpoints are registered. Let's continue the program by `continue` command. +You can also start your program with the `rdbg` executable, which will enter a debugging session at the beginning of your program by default. ```shell -(rdbg) continue +❯ rdbg target.rb [1, 7] in target.rb - 1| a = 1 - 2| b = 2 -=> 3| c = 3 - 4| d = 4 - 5| p [a, b, c, d] - 6| - 7| __END__ -=>#0
at target.rb:3 - -Stop by #0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line) - +=> 1| def foo # stops at the beginning of the program + 2| 10 + 3| end + 4| + 5| foo + 6| + 7| binding.break +=>#0
at target.rb:1 (rdbg) ``` -You can see that we can stop at line 3. -Let's see the local variables with `info` command, and continue. -You can also confirm that the program will suspend at line 5 and you can use `info` command again. +If you don't want to stop your program until it hits a breakpoint, you can use `rdbg --nonstop` instead (or `-n` for short). ```shell -(rdbg) info -=>#0
at target.rb:3 -%self => main -a => 1 -b => 2 -c => nil -d => nil - -(rdbg) continue -[1, 7] in target.rb - 1| a = 1 - 2| b = 2 - 3| c = 3 - 4| d = 4 -=> 5| p [a, b, c, d] - 6| - 7| __END__ -=>#0
at target.rb:5 - -Stop by #1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line) - -(rdbg) info -=>#0
at target.rb:5 -%self => main -a => 1 -b => 2 -c => 3 -d => 4 - -(rdbg) continue -[1, 2, 3, 4] +❯ rdbg --nonstop target.rb +[2, 7] in target.rb + 2| 10 + 3| end + 4| + 5| foo + 6| +=> 7| binding.break # stops at the first breakpoint +=>#0
at target.rb:7 +(rdbg) ``` -By the way, using `rdbg` command you can suspend your application with `C-c` (SIGINT) and enter the debug console. -It will help that if you want to know what the program is doing. - -### Use `rdbg` with commands written in Ruby - If you want to run a command written in Ruby like like `rake`, `rails`, `bundle`, `rspec` and so on, you can use `rdbg -c` option. -* Without `-c` option, `rdbg ` means that `` is Ruby script and invoke it like `ruby ` with the debugger. -* With `-c` option, `rdbg -c ` means that `` is command in `PATH` and simply invoke it with the debugger. +- Without `-c` option, `rdbg ` expects `` to be a Ruby script and invokes it like `ruby ` with the debugger. +- With `-c` option, `rdbg -c ` expects `` to be a command in `PATH` and simply invoke it with the debugger. Examples: -* `rdbg -c -- rails server` -* `rdbg -c -- bundle exec ruby foo.rb` -* `rdbg -c -- bundle exec rake test` -* `rdbg -c -- ruby target.rb` is same as `rdbg target.rb` - -NOTE: `--` is needed to separate the command line options for `rdbg` and invoking command. For example, `rdbg -c rake -T` is recognized like `rdbg -c -T -- rake`. It should be `rdbg -c -- rake -T`. - -NOTE: If you want to use bundler (`bundle` command), you need to write `gem debug` line in your `Gemfile`. - -### Using VSCode - -Like other languages, you can use this debugger on the VSCode. - -1. Install [VSCode rdbg Ruby Debugger - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) -2. Open `.rb` file (e.g. `target.rb`) -3. Register breakpoints with "Toggle breakpoint" in Run menu (or type F9 key) -4. Choose "Start debugging" in "Run" menu (or type F5 key) -5. You will see a dialog "Debug command line" and you can choose your favorite command line your want to run. -6. Chosen command line is invoked with `rdbg -c` and VSCode shows the details at breakpoints. - -Please refer [Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging) for operations on VSCode. +- `rdbg target.rb` +- `rdbg -c -- rails server` +- `rdbg -c -- bundle exec ruby foo.rb` +- `rdbg -c -- bundle exec rake test` +- `rdbg -c -- ruby target.rb` is same as `rdbg target.rb` -You can configure the extension in `.vscode/launch.json`. -Please see the extension page for more details. +> **Note** +> `--` is needed to separate the command line options for `rdbg` and invoking command. For example, `rdbg -c rake -T` is recognized like `rdbg -c -T -- rake`. It should be `rdbg -c -- rake -T`. -## Remote debugging +> **Note** +> If you want to use bundler (`bundle` command), you need to write `gem debug` line in your `Gemfile`. -You can use this debugger as a remote debugger. For example, it will help the following situations: +
Expand to see all options 👇 -* Your application does not run on TTY and it is hard to use `binding.pry` or `binding.irb`. - * Your application is running on Docker container and there is no TTY. - * Your application is running as a daemon. - * Your application uses pipe for STDIN or STDOUT. -* Your application is running as a daemon and you want to query the running status (checking a backtrace and so on). - -You can run your application as a remote debuggee and the remote debugger console can attach to the debuggee anytime. - -### Invoke as a remote debuggee - -There are multiple ways to run your program as a debuggee: - -Stop at program start | [`rdbg` option](https://github.com/ruby/debug#rdbg---open-or-rdbg--o-for-short) | [require](https://github.com/ruby/debug#require-debugopen-in-a-program) | [debugger API](https://github.com/ruby/debug#start-by-method) ----|---|---|---| -Yes | `rdbg --open` | `require "debug/open"` | `DEBUGGER__.open` -No | `rdbg --open --nonstop` | `require "debug/open_nonstop"` | `DEBUGGER__.open(nonstop: true)` - -#### `rdbg --open` (or `rdbg -O` for short) - -You can run a script with `rdbg --open target.rb` command and run a `target.rb` as a debuggee program. It also opens the network port and suspends at the beginning of `target.rb`. - -```shell -$ exe/rdbg --open target.rb -DEBUGGER: Session start (pid: 7773) -DEBUGGER: Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773) -DEBUGGER: wait for debugger connection... -``` - -By default, `rdbg --open` uses UNIX domain socket and generates path name automatically (`/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773` in this case). - -You can connect to the debuggee with `rdbg --attach` command (`rdbg -A` for short). - -```shell -$ rdbg -A -[1, 7] in target.rb -=> 1| a = 1 - 2| b = 2 - 3| c = 3 - 4| d = 4 - 5| p [a, b, c, d] - 6| - 7| __END__ -=>#0
at target.rb:1 - -(rdbg:remote) ``` - -If there is no other opening ports on the default directory, `rdbg --attach` command chooses the only one opening UNIX domain socket and connect to it. If there are more files, you need to specify the file. - -When `rdbg --attach` connects to the debuggee, you can use any debug commands (set breakpoints, continue the program and so on) like local debug console. When an debuggee program exits, the remote console will also terminate. - -NOTE: If you use `quit` command, only remote console exits and the debuggee program continues to run (and you can connect it again). If you want to exit the debuggee program, use `kill` command. - -If you want to use TCP/IP for the remote debugging, you need to specify the port and host with `--port` like `rdbg --open --port 12345` and it binds to `localhost:12345`. - -To connect to the debuggee, you need to specify the port. - -```shell -$ rdbg --attach 12345 -``` - -If you want to choose the host to bind, you can use `--host` option. -Note that all messages communicated between the debugger and the debuggee are *NOT* encrypted so please use remote debugging carefully. - -#### `require 'debug/open'` in a program - -If you can modify the program, you can open debugging port by adding `require 'debug/open'` line in the program. - -If you don't want to stop the program at the beginning, you can also use `require 'debug/open_nonstop'`. -Using `debug/open_nonstop` is useful if you want to open a backdoor to the application. -However, it is also danger because it can become another vulnerability. -Please use it carefully. - -By default, UNIX domain socket is used for the debugging port. To use TCP/IP, you can set the `RUBY_DEBUG_PORT` environment variable. - -```shell -$ RUBY_DEBUG_PORT=12345 ruby target.rb -``` - -### Integration with external debugger frontend - -You can attach with external debugger frontend with VSCode and Chrome. - -``` -$ rdbg --open=[frontend] target.rb -``` - -will open a debug port and `[frontend]` can attach to the port. - -Also `open` command allows opening the debug port. - -#### VSCode integration - -([vscode-rdbg v0.0.9](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg) or later is required) - -If you don't run a debuggee Ruby process on VSCode, you can attach with VSCode later with the following steps. - -`rdbg --open=vscode` opens the debug port and tries to invoke the VSCode (`code` command). - -``` -$ rdbg --open=vscode target.rb -DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/ruby-debug-sock-1000/ruby-debug-ko1-27706) -DEBUGGER: wait for debugger connection... -Launching: code /tmp/ruby-debug-vscode-20211014-27706-gd7e85/ /tmp/ruby-debug-vscode-20211014-27706-gd7e85/README.rb -DEBUGGER: Connected. -``` - -And it tries to invoke the new VSCode window and VSCode starts attaching to the debuggee Ruby program automatically. - -You can also use `open vscode` command in REPL. - -``` -$ rdbg target.rb -[1, 8] in target.rb - 1| -=> 2| p a = 1 - 3| p b = 2 - 4| p c = 3 - 5| p d = 4 - 6| p e = 5 - 7| - 8| __END__ -=>#0
at target.rb:2 -(rdbg) open vscode # command -DEBUGGER: wait for debugger connection... -DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/ruby-debug-sock-1000/ruby-debug-ko1-28337) -Launching: code /tmp/ruby-debug-vscode-20211014-28337-kg9dm/ /tmp/ruby-debug-vscode-20211014-28337-kg9dm/README.rb -DEBUGGER: Connected. -``` - -If the machine which runs the Ruby process doesn't have a `code` command, the following message will be shown: - -``` -(rdbg) open vscode -DEBUGGER: wait for debugger connection... -DEBUGGER: Debugger can attach via UNIX domain socket (/tmp/ruby-debug-sock-1000/ruby-debug-ko1-455) -Launching: code /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb -DEBUGGER: Can not invoke the command. -Use the command-line on your terminal (with modification if you need). - - code /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb - -If your application is running on a SSH remote host, please try: - - code --remote ssh-remote+[SSH hostname] /tmp/ruby-debug-vscode-20211014-455-gtjpwi/ /tmp/ruby-debug-vscode-20211014-455-gtjpwi/README.rb - +<%= `exe/rdbg --help` %> ``` -and try to use proposed commands. +
-Note that you can attach with `rdbg --attach` and continue REPL debugging. +## Set breakpoints -#### Chrome DevTool integration +### The `binding.break` method -With `rdbg --open=chrome` command will show the following message. +`binding.break` (and its aliases `binding.b` and `debugger`) set breakpoints at the written line. +```rb +❯ ruby -rdebug target.rb +[2, 7] in target.rb + 2| 10 + 3| end + 4| + 5| foo + 6| +=> 7| binding.break +=>#0
at target.rb:7 +(rdbg) ``` -$ rdbg target.rb --open=chrome -DEBUGGER: Debugger can attach via TCP/IP (127.0.0.1:43633) -DEBUGGER: With Chrome browser, type the following URL in the address-bar: - - devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=127.0.0.1:57231/b32a55cd-2eb5-4c5c-87d8-b3dfc59d80ef -DEBUGGER: wait for debugger connection... -``` +#### Advanced usages -Type `devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=127.0.0.1:57231/b32a55cd-2eb5-4c5c-87d8-b3dfc59d80ef` in the address-bar on Chrome browser, and you can continue the debugging with chrome browser. +- If `do: 'command'` is specified, the debugger will -Also `open chrome` command works like `open vscode`. + 1. Stop the program + 1. Run the `command` as a debug command + 1. Continue the program. -For more information about how to use Chrome debugging, you might want to read [here](https://developer.chrome.com/docs/devtools/). + It is useful if you only want to call a debug command and don't want to stop there. -## Configuration + ```rb + def initialize + @a = 1 + binding.b do: 'watch @a' + end + ``` -You can configure the debugger's behavior with debug commands and environment variables. -When the debug session is started, initial scripts are loaded so you can put your favorite configurations in the initial scripts. + In this case, the debugger will register a watch breakpoint for `@a` and continue to run. -### Configuration list +- If `pre: 'command'` is specified, the debugger will + 1. Stop the program + 1. Run the `command` as a debug command + 1. Keep the console open -You can configure debugger's behavior with environment variables and `config` command. Each configuration has environment variable and the name which can be specified by `config` command. + It is useful if you have repeated operations to perform before the debugging at the breakpoint -``` -# configuration example -config set log_level INFO -config set no_color true -``` + ```rb + def foo + binding.b pre: 'info locals' + ... + end + ``` -<% cat = nil; DEBUGGER__::CONFIG_SET.each do |key, (env, desc, _, default)| %> -<% /\A(\w+): (.+)/ =~ desc; if cat != $1; cat = 1 %> -* <%= $1 %> -<% cat = $1; end %> * `<%= env %>` (`<%= key %>`): <%= default ? "#{$2} (default: #{default})" : $2 %><% end %> + In this case, the debugger will display local variable information automatically so you don't need to type it repeatedly. -There are other environment variables: +## Start debugging -* `NO_COLOR`: If the value is set, set `RUBY_DEBUG_NO_COLOR` ([NO_COLOR: disabling ANSI color output in various Unix commands](https://no-color.org/)). -* `RUBY_DEBUG_ENABLE`: If the value is `0`, do not enable debug.gem feature. -* `RUBY_DEBUG_ADDED_RUBYOPT`: Remove this value from `RUBYOPT` at first. This feature helps loading debug.gem with `RUBYOPT='-r debug/...'` and you don't want to derive it to child processes. In this case you can set `RUBY_DEBUG_ADDED_RUBYOPT='-r debug/...'` (same value) and this string will be deleted from `RUBYOPT` at first. -* `RUBY_DEBUG_EDITOR` or `EDITOR`: An editor used by `edit` debug command. -* `RUBY_DEBUG_BB`: Define `Kernel#bb` method which is alias of `Kernel#debugger`. +Once you're in the debugger console, you can start debugging with it. But here are some useful tips: -### Initial scripts +- `` without debug command is almost same as `pp `. + - If the input line `` does *NOT* start with any debug command, the line `` will be evaluated as a Ruby expression and the result will be printed with `pp` method. So that the input `foo.bar` is same as `pp foo.bar`. + - If `` is recognized as a debug command, of course it is not evaluated as a Ruby expression, but is executed as debug command. + - For example, you can not evaluate such single letter local variables `i`, `b`, `n`, `c` because they are single letter debug commands. Use `p i` instead. + - So the author (Koichi Sasada) recommends to use `p`, `pp` or `eval` command to evaluate the Ruby expression everytime. +- `Enter` without any input repeats the last command for some commands, such as `step` or `next`. This is useful when step-debugging. +- `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) -If there is `~/.rdbgrc`, the file is loaded as an initial script (which contains debug commands) when the debug session is started. +### Command & expression parsing -* `RUBY_DEBUG_INIT_SCRIPT` environment variable can specify the initial script file. -* You can specify the initial script with `rdbg -x initial_script` (like gdb's `-x` option). +Because the debugger supports both Ruby expression evaluation and dozens of commands, name collision happens. -Initial scripts are useful to write your favorite configurations. -For example, you can set break points with `break file:123` in `~/.rdbgrc`. +So here're a few rules explaining how the debugger interprets your input: -If there are `~/.rdbgrc.rb` is available, it is also loaded as a ruby script at same timing. +- If `.split(" ")` does **NOT** start with any debugger command (e.g. `my_var` or `foo bar`), it will be evaluated as `pp ` +- If `.split(" ")` starts with a command or a command shortcut (like `info` and `i`), it will be treated as a command instead. -## Debug command on the debug console +Some examples: -On the debug console, you can use the following debug commands. + - `foo.bar` is same as `pp foo.bar`. + - `info arg` are `i arg` are considered as ` arg` + - `info(arg)` is considered as `pp self.info(arg)` + - `i` is considered as `` + - `pp i` prints `i` -There are additional features: -* `` without debug command is almost same as `pp `. - * If the input line `` does *NOT* start with any debug command, the line `` will be evaluated as a Ruby expression and the result will be printed with `pp` method. So that the input `foo.bar` is same as `pp foo.bar`. - * If `` is recognized as a debug command, of course it is not evaluated as a Ruby expression, but is executed as debug command. For example, you can not evaluate such single letter local variables `i`, `b`, `n`, `c` because they are single letter debug commands. Use `p i` instead. - * So the author (Koichi Sasada) recommends to use `p`, `pp` or `eval` command to evaluate the Ruby expression everytime. -* `Enter` without any input repeats the last command (useful when repeating `step`s) for some commands. -* `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) +### Console commands 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 %> -## Debugger API - -### Start debugging - -#### Start by requiring a library - -You can start debugging without `rdbg` command by requiring the following libraries: - -* `require 'debug'`: Same as `rdbg --nonstop --no-sigint-hook`. -* `require 'debug/start'`: Same as `rdbg`. -* `require 'debug/open'`: Same as `rdbg --open`. -* `require 'debug/open_nonstop'`: Same as `rdbg --open --nonstop`. - -You need to require one of them at the very beginning of the application. -Using `ruby -r` (for example `ruby -r debug/start target.rb`) is another way to invoke with debugger. - -NOTE: Until Ruby 3.0, there is old `lib/debug.rb` standard library. So that if this gem is not installed, or if `Gemfile` missed to list this gem and `bundle exec` is used, you will see the following output: - -```shell -$ ruby -r debug -e0 -.../2.7.3/lib/ruby/2.7.0/x86_64-linux/continuation.so: warning: callcc is obsolete; use Fiber instead -Debug.rb -Emacs support available. - -.../2.7.3/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:162: if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?) -(rdb:1) -``` - -`lib/debug.rb` was not maintained well in recent years, and the purpose of this library is to rewrite old `lib/debug.rb` with recent techniques. - -#### Start by method - -After loading `debug/session`, you can start debug session with the following methods. They are convenient if you want to specify debug configurations in your program. - -* `DEBUGGER__.start(**kw)`: start debug session with local console. -* `DEBUGGER__.open(**kw)`: open debug port with configuration (without configurations open with UNIX domain socket) -* `DEBUGGER__.open_unix(**kw)`: open debug port with UNIX domain socket -* `DEBUGGER__.open_tcp(**kw)`: open debug port with TCP/IP - -For example: - -```ruby -require 'debug/session' -DEBUGGER__.start(no_color: true, # disable colorize - log_level: 'INFO') # Change log_level to INFO - -... # your application code -``` - -### `binding.break` method - -`binding.break` (or `binding.b`) set breakpoints at written line. It also has several keywords. - -If `do: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command and continue the program. -It is useful if you only want to call a debug command and don't want to stop there. - -``` -def initialize - @a = 1 - binding.b do: 'watch @a' -end -``` - -On this case, register a watch breakpoint for `@a` and continue to run. - -If `pre: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command, and keep suspend. -It is useful if you have operations before suspend. +The `[...]` notation means this part can be eliminated. For example, `s[tep]` means `s` or `step` are both valid commands. `ste` is not valid. +The `<...>` notation means the argument. -``` -def foo - binding.b pre: 'p bar()' - ... -end -``` +Here's a [Google sheet](https://docs.google.com/spreadsheets/d/1TlmmUDsvwK4sSIyoMv-io52BUUz__R5wpu-ComXlsw0/edit?usp=sharing) for comparing this and other Ruby debuggers' commands. -On this case, you can see the result of `bar()` every time you stop there. +<%= DEBUGGER__.help %> -## rdbg command help +# Configuration -``` -<%= `exe/rdbg --help` %> -``` +See the [configuration guide](/docs/configuration.md) for more information. # Additional Resources @@ -606,5 +250,5 @@ 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) -* Several codes in `server_cdp.rb` are based on [geoffreylitt/ladybug: Visual Debugger](https://github.com/geoffreylitt/ladybug) +- Some tests are based on [deivid-rodriguez/byebug: Debugging in Ruby 2](https://github.com/deivid-rodriguez/byebug) +- Several codes in `server_cdp.rb` are based on [geoffreylitt/ladybug: Visual Debugger](https://github.com/geoffreylitt/ladybug) diff --git a/misc/configuration.md.erb b/misc/configuration.md.erb new file mode 100644 index 000000000..5587e3a6b --- /dev/null +++ b/misc/configuration.md.erb @@ -0,0 +1,40 @@ +# Configuration + +You can configure the debugger's behavior with the `config` command and environment variables. + +Every configuration has a corresponding environment variable, for example: + +``` +config set log_level INFO # RUBY_DEBUG_LOG_LEVEL=INFO +config set no_color true # RUBY_DEBUG_NO_COLOR=true +``` + +<% cat = nil; DEBUGGER__::CONFIG_SET.each do |key, (env, desc, _, default)| %> +<% /\A(\w+): (.+)/ =~ desc; if cat != $1; cat = 1 %> +- <%= $1 %> +<% cat = $1; end %> - `<%= env %>` (`<%= key %>`): <%= default ? "#{$2} (default: #{default})" : $2 %><% end %> + +There are other environment variables: + +* `NO_COLOR`: If the value is set, set `RUBY_DEBUG_NO_COLOR` ([NO_COLOR: disabling ANSI color output in various Unix commands](https://no-color.org/)). +* `RUBY_DEBUG_ENABLE`: If the value is `0`, do not enable debug.gem feature. +* `RUBY_DEBUG_ADDED_RUBYOPT`: Remove this value from `RUBYOPT` at first. This feature helps loading debug.gem with `RUBYOPT='-r debug/...'` and you don't want to derive it to child processes. In this case you can set `RUBY_DEBUG_ADDED_RUBYOPT='-r debug/...'` (same value) and this string will be deleted from `RUBYOPT` at first. +* `RUBY_DEBUG_EDITOR` or `EDITOR`: An editor used by `edit` debug command. +* `RUBY_DEBUG_BB`: Define `Kernel#bb` method which is alias of `Kernel#debugger`. + +## Initialization scripts + +If you want to run certain commands or set configurations for every debugging session automatically, you can put them into the `~/.rdbgrc` file. + +If you want to run additional initial scripts, you can also, + +- Use `RUBY_DEBUG_INIT_SCRIPT` environment variable can specify the initial script file. +- Specify the initial script with `rdbg -x initial_script`. + +Initial scripts are useful to write your favorite configurations. For example, + +``` +config set use_short_path true # Use $(Gem)/gem_content to replace the absolute path of gem files +``` + +Finally, you can also write the initial script in Ruby with the file name `~/.rdbgrc.rb`.