RubyConf 2021

debug.gem: Ruby's new debug functionality

debug.gem: Ruby's new debug functionality

by Koichi Sasada

Introduction

The talk presented by Koichi Sasada at RubyConf 2021 introduces the new debugging tool called debug.gem, which has been developed specifically for Ruby and is set to be included with Ruby 3.1, releasing in December. The debug.gem offers numerous enhancements over existing debuggers, addressing performance issues, integration with IDEs, remote debugging capabilities, and more.

Key Points Discussed

  • Purpose of the Debugger: The primary roles of a debugger are to help identify bugs within applications and to aid in understanding the program internals through interactive exploration.
  • Existing Debuggers: Sasada compares debug.gem to previous Ruby debuggers, highlighting that while tools like byebug and debug.rb exist, they often suffer from performance issues and lack modern features.
  • New Features in debug.gem: This debugger is designed with:
    • Improved Performance: Utilizing Ruby's TracePoint to minimize performance overhead, allowing developers to utilize breakpoints without significant slowdowns.
    • IDE Integration: Seamless integration with Visual Studio Code and other IDEs, making it easier for developers to set breakpoints and explore variables via a graphical interface.
    • Remote Debugging: The ability to run a debugging session and connect to it remotely, beneficial for processes that do not run in a normal terminal.
    • Support for Threads/Ractors: Enhancements allowing for debugging in a concurrent environment, a critical feature as Ruby continues to evolve.
    • Advanced Features: Including post-mortem debugging and a record-and-replay functionality that allows developers to go backwards in their debugging process.

Demonstrations

Sasada provides demonstrations on:
- Using the rdbg command to control the Ruby interpreter with debug functionality, showcasing local variables and step commands.
- Integrating debugging within Visual Studio Code, illustrating how to set breakpoints using keyboard shortcuts and the graphical interface.
- Remote debugging capabilities, showcasing the ability to connect to a running process and inspect its state.

Conclusion

The talk concludes with Sasada emphasizing that debug.gem represents a major milestone in the Ruby debugging landscape, providing developers with a more efficient and user-friendly debugging tool. He encourages the community to provide feedback as the gem progresses towards maturity, aiming to enhance its features and functionality. The overall message is that effective debugging is essential for improving Ruby application development, and this new tool is a significant step forward in achieving that goal.

00:00:11.120 Hello, and thank you for listening to this talk. I'm Koichi Sasada from Cookpad.
00:00:15.120 This year, I created a brand new debugger called 'ruby/debug.' I want to introduce this term in this talk.
00:00:30.080 The debugger has already been released and can be used with Ruby 3.6 or later. It will be shipped with Ruby 3.1, which will be released in December this year.
00:00:34.239 The Ruby 3.1 development branch has already matched the bulk of gameplay, so you can try Ruby 3.1 along with this new debugger. This debugger has many novel features that were not previously available in the Ruby world, and I hope you will enjoy these new features.
00:00:52.079 This talk will demonstrate the basic usage and advanced features of this new debugger. The presentation slides along with this talk's script are available at this URL. Please enjoy!
00:01:28.560 Let's introduce myself before the presentation. I'm Koichi Sasada, and I am an interpreter developer.
00:01:31.840 Usually, I develop Ruby internals such as the virtual machine and garbage collector. Last year, I worked on the language implementation for Ruby 3.0. This year, I've mainly spent my time creating this new debugger.
00:01:43.920 Before I introduce the new debugger, I want to summarize what a debugger is. The main purpose of a debugger is to help debug your applications. When we encounter a bug, we need to investigate the causal programs. First, we check the backtrace to determine where the issue originates.
00:02:03.920 Usually, we also use print statements to display the program state, using 'p' or 'pp' methods in Ruby. Sometimes we might use 'binding.pry' or 'binding.irb' methods to enter the interactive Ruby console on a specific binding.
00:02:59.519 Another purpose of using the debugger is to better understand the program. Learning about the program is one of the best ways to understand its internals, and in such cases, the debugger provides useful features.
00:03:05.200 It allows users to control the execution, for example, by specifying breakpoints at which they want the execution to stop and stepping forward line by line.
00:03:22.560 In the Ruby world, we already have several debuggers. 'byebug' is the most popular debugger for Ruby in recent days.
00:03:30.560 The previous debuggers, like 'debug.rb' from standard library available since Ruby 1.0, are not well-maintained, and maybe nobody uses them. However, I decided to create this new debugger for several reasons.
00:03:50.239 One reason is the performance of existing debuggers. When we use breakpoints, existing debuggers can slow down the program significantly. Recently introduced TracePoint features help to mitigate this slowdown, allowing us to improve the performance of debugging.
00:04:14.560 I want to ensure that developers do not hesitate to use the debugger during development. Additionally, I want to provide IDE integration by default, as the usual debugger console can sometimes be challenging to use.
00:04:51.680 For instance, we need to use commands in the console to set breakpoints, but I would prefer a more intuitive approach, such as clicking on the source code in the editor to specify breakpoints.
00:05:16.960 Another issue we face is that printing nested data structures can result in a huge output, which can be overwhelming. I would like to have a feature to expand the output by clicking, much like in JavaScript consoles.
00:05:40.480 Another reason for creating this debugger is that Ruby 3.0 introduces a new Ractor mechanism that isolates object spaces, while existing debuggers do not support it. Although the current debugger does not support Ractors yet, it is one of my motivations.
00:06:02.720 Lastly, my biggest motivation for creating this type of tool is that I enjoy building them. Although it's technically challenging, it’s highly rewarding.
00:06:23.760 Now, let's introduce debugging. This talk will not explain how to use the new debugger but will focus on the types of features it provides.
00:06:43.360 For detailed usage, please refer to the documentation on GitHub. The README contains everything you need to know, along with around a thousand lines of content.
00:07:02.319 Debugging has been created from scratch this year. The gem has already been released, so you can install it using the 'gem install debug' command. It supports Ruby 2.6 and later as it utilizes newly introduced APIs.
00:07:59.040 Ruby 3.1, which will be released soon, will ship with this debugger to replace the old 'debug.rb.'
Similar to other debuggers, such as byebug or gdb, it provides a library that accepts debug commands for real-time bug investigation.
00:08:43.200 To use the debugger, there are mainly three methods. The first is using the 'rdbg' command, similar to 'byebug' or 'gdb' commands, where you specify the Ruby script or external commands with the '-g' option.
00:09:06.080 The second method involves rewriting your application to require the debug library. This approach is popular with developers.
00:09:31.120 The third method is to use an IDE, such as Visual Studio Code, where you need to set up the launch.json file in your workspace. Thankfully, the Ruby 'rdbg' extension generates a default setting, so you don’t need to delve into the details.
00:09:54.399 After setup, you just need to click the 'Start Debugging' button to start using the debugger.
00:10:18.720 Let's take a look at a basic demonstration of the 'rdbg' command. By invoking the 'rdbg' command with the target script, it invokes the Ruby interpreter in debug mode.
00:10:54.080 The debugger pauses at the beginning of the script, allowing you to see where it stops. You can use the 'step' command to move to the next line and continue stepping through the code.
00:11:33.040 Using an empty command simply repeats the prior command, so we can step repeatedly. Additionally, you can inspect local variables at any halted point in the execution.
00:12:08.240 You can step into methods and observe the backtrace along with the parameters provided. When the method returns, you will see its return value.
00:12:38.080 You can also continue the program’s execution by using the 'continue' command.
00:12:57.200 This demonstration also shows debugging with VS Code integration. You can start debugging from the menu and specify what kind of program you want to run. If there are no breakpoints, the program terminates immediately.
00:13:45.600 To specify a breakpoint, you can press the F9 key on VS Code and retest the debugger. You should see the program halts at the breakpoint.
00:14:10.080 As you can observe, local variables are displayed, and you can expand nested data structures by clicking on them.
00:14:36.400 Execution control is also manageable through buttons on the card, providing an intuitive debugging experience.
00:15:01.760 The debugging provides basic features similar to other debuggers, including specifying breakpoints.
00:15:34.560 Three methods can be used to specify breakpoints: one is to use the 'break' command in the debugger console. You can specify files, lines, method names, and raise exceptions.
00:16:06.720 You can also specify conditions under which the execution should break.
00:16:30.400 Another way is to write 'binding.break' in your application, similar to 'binding.pry' or 'binding.irb'. This method is straightforward if you can modify the source code before executing the program.
00:17:02.240 The final way is using IDE breakpoint features. Currently, we only support VS Code, but we plan to extend support to more platforms.
00:17:15.520 Utilizing the break command or IDE support allows for breakpoint specification without modifying the source code. I believe this is the easiest way to use debugging.
00:17:45.680 The 'binding.break' method in your application is a well-known style, allowing you to control breakpoints from your application code.
00:18:10.080 Another interesting idea for using the 'binding.break' method is in conjunction with the 'do' keyword. If the 'do' keyword is present, it doesn’t stop execution but executes the debug command.
00:18:45.520 This provides trace information specifically for the method execution, helping you focus on the debugging process.
00:19:01.760 In general, a developer's target program cannot communicate with the underlying debugger, but this feature enables that interaction. It allows you to utilize the debugger's features within your application.
00:19:34.080 The debugger offers normal execution control features, including stepping, stepping over, and stepping out of code blocks.
00:19:54.080 For example, using the 'step' command allows you to enter a method and then stop at specific lines for detailed analysis.
00:20:32.080 The 'step over' command stops at the next line but does not interrupt method calls, allowing a smooth debugging experience.
00:20:51.680 It stops after returning from the current scope and allows further execution checking.
00:21:00.080 When the program stops at a breakpoint, you can view the backtrace along with the parameters, which helps in debugging.
00:21:23.760 With 'debug.rb', you can only access the specified binding, but with the new debugger, you can access any bindings in the backtrace using the 'with frame' command.
00:22:08.480 Therefore, you can check variables using commands such as 'info' or 'outline.' You can even evaluate Ruby expressions directly in the debug console.
00:22:30.960 I have introduced basic features available in many other debuggers, but now I want to show advanced features offered by the new debugger.
00:23:00.560 Today, I'll cover four advanced features.
00:23:06.160 First, the debugger supports remote debugging capabilities. You can open debug ports by using the 'rdbg' command with the 'open' option.
00:23:23.680 After opening the port, you can attach to it using the 'rdbg attach' command, allowing you to control the target program remotely.
00:23:43.760 This feature is crucial when debugging processes that do not have a tty, such as daemon processes or redirect processes with shared pipelines.
00:24:00.960 Should debug ports remain open, you can attach anytime to check the program state, which greatly aids development.
00:24:28.080 Next, I want to show you seamless integration with VS Code and the Chrome browser. If you are already debugging in the debug console but want to switch to VS Code, you can use the 'open vs code' command.
00:24:58.440 Once executed, it waits a few seconds while VS Code launches automatically, and debugging can continue there.
00:25:21.120 Likewise, you can utilize Chrome as a debugger frontend by using the 'open chrome' command. You will receive a URL.
00:25:36.640 Copy this URL into the Chrome browser, and you can continue debugging within Chrome.
00:25:54.560 You have options to select either VS Code or Chrome as a debugging frontend through the 'open' option.
00:26:11.040 Furthermore, if you're not using VS Code to modify source code but still want to utilize it for debugging, this feature will be advantageous to you.
00:26:32.160 Next, we'll explore post-mortem debugging.
00:26:38.080 This allows the debugger to activate when a program raises an exception, such as a 'zerodivisionerror.'
00:26:58.160 You can enter the debug console right when the process terminates due to the exception, allowing inspection of variable states at that moment.
00:27:10.720 This feature helps you investigate issues further.
00:27:25.680 Lastly, I want to demonstrate the recorder and replay debugging functionalities.
00:27:41.440 If you enable this feature, you can record execution information and then step back to previous execution points using the 'step back' command.
00:27:57.080 This is highly beneficial if you want to check the last status before a breakpoint.
00:28:28.080 This feature is currently not optimized, as it consumes time and memory; thus, it isn't feasible to use it for entire execution.
00:28:43.760 Nevertheless, it can be applied to a limited context.
00:29:06.240 You can use this recorder and replay debugging feature with VS Code. Start debugging and utilize the step back button to see previous states.
00:29:25.840 At the start of this presentation, I mentioned that one of the motivations was to enhance debugger performance.
00:29:53.360 The following figure illustrates how performance degrades using other debuggers with numerous breakpoints compared to the ruby/debug.
00:30:02.480 When employing 'byebug,' the slowdown can range significantly, resulting in a thirty times slower performance compared to regular execution.
00:30:29.280 By contrast, with debugging, we observe no performance penalties.
00:30:36.240 Finally, I would like to show acknowledgments. Onosang assisted me in making the test framework for the debugger and for integrating with the Chrome browser.
00:31:04.000 The test framework was achieved through Google Summer of Code this year. Stan Lawson made many contributions by submitting patches to improve debugging.
00:31:23.680 He suggested many valuable ideas from the Rails user perspective. Many others have also assisted me.
00:31:38.720 Thank you so much.
00:31:54.880 In conclusion, ruby/debug is a newly created Ruby debugger developed from scratch. It is fast, has a user-friendly interface, and provides many features.
00:32:14.560 You can use it now by installing this gem. Though it is not yet fully matured, your feedback will be vital for its improvement.
00:32:50.720 If you need any advice or have requests, please feel free to contact us.
00:33:00.000 Thank you for your attention. I hope this debug gem will help your development.