Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a command to activate and switch to ruby/debug console #425

Closed
st0012 opened this issue Oct 31, 2022 · 6 comments · Fixed by #446
Closed

Add a command to activate and switch to ruby/debug console #425

st0012 opened this issue Oct 31, 2022 · 6 comments · Fixed by #446

Comments

@st0012
Copy link
Member

st0012 commented Oct 31, 2022

Background

When debugging programs with Ruby 2.7+ , it's common to start with adding binding.irb breakpoints in the code. This is because irb is the only console-based tool (including REPLs and debuggers) that doesn't require installation nor require.

However, because irb is a REPL and lacks debugger features like step-debugging, frame navigation...etc., users may need to switch to debuggers like ruby/debug for advanced debugging.

And currently, that switch looks like this:

  1. Exit the current irb session.
  2. Add require "debug" if it's not yet required.
  3. Replace binding.irb with ruby/debug's breakpoints like binding.b or debugger (they're the same).
  4. Rerun the program and start debugging.

It's a 4-step process and requires switching between terminal and editor.

During a discussion with @k0kubun, he mentioned that maybe irb can help reduce the pain.

Proposal

Add a new debug command to irb, which when being executed will

  1. Run require "debug/session" and start the debugger.
    • If it causes LoadError, it means the new debug gem is not installed. So it will print a message to remind the user instead.
  2. Replace the current irb session with a new debug console.
  3. The user can start debugging at the same location as binding.irb was placed.

With this command, a user's irb -> debug switch would be:

  1. Type debug in the irb session.
  2. Start debugging.

Demo

irb debug command

Discussion Points

  • Should the command be debug or debugger?
    • debug:
      • As a verb it seems to convey the intention well as "Debug in the current context"
      • On the other hand, it's another debugging related name to remember.
    • debugger:
      • It's the same as the debugger statement ruby/debug already has.
      • It may give a false impression that we're simply calling the same statement in irb.
  • When users exit the debugger, should they exit completely or return to irb?
    • IMO, it should exit completely so users don't need to exit twice.
  • Because irb registers autocompletion logic to reline, debug's console (also powered by reline) will inherit that too.
    • Perhaps it should be suspended to avoid unexpected side-effects?
@k0kubun
Copy link
Member

k0kubun commented Oct 31, 2022

Did you conclude it's impossible to make IRB work like pry-byebug? On pry-byebug, you don't need to explicitly switch to byebug to use some of the byebug features. Requiring users to run debug command to enable those features feels like an extra step compared to pry-byebug's transparent experience.

@st0012
Copy link
Member Author

st0012 commented Oct 31, 2022

I didn't conclude it's "impossible". But it is more difficult because pry-byebug is implemented via pry's command extension APIs (example: backtrace command), which irb doesn't have (the current command APIs are aimed for internal use AFAICT).

But more importantly, what you proposed is not mutually exclusive with this proposal: pry-byebug only supports some byebug commands, so it's a partial enhancement of pry, not a true combination of both worlds. For example, to use commands like th, switching to byebug is still needed.

In irb's case, we can have something like irb-debug to make irb more powerful. But unless we want to have a more aggressive irb + debug integration (like having all debug commands available in irb), users could still use a way to quickly switch between them. And this proposal aims for the latter (fyi, debug has an irb command too).

@k0kubun
Copy link
Member

k0kubun commented Oct 31, 2022

But unless we want to have a more aggressive irb + debug integration (like having all debug commands available in irb), users could still use a way to quickly switch between them.

When you want to use all the debug.gem commands, why don't you start binding.b in the first place? For users who need all the debug.gem features like thread debugging, directly starting binding.b seems more convenient than the two steps of binding.irb -> debug.

You said binding.irb "doesn't require installation nor require", but generally, you have to write gem "debug" in Gemfile to use a bundled gem under bundle exec (forced to install it), and then Bundler.require will take care of require (automatically required). If such users (e.g. Rails app developers) want to use all the debug.gem commands, I guess they should be able to start binding.b without the hustle explained in the issue description.

Is this proposal primarily targeting other users, e.g. gem/library developers? I feel like we have more users who are application developers, and I think they should not use this feature (binding.irb -> debug) but simply start debug.gem directly (binding.b) given that they currently need to write gem "debug" anyway and Bundler.require will require it.

@k0kubun
Copy link
Member

k0kubun commented Oct 31, 2022

In summary, my proposal is:

  • Users who need all debug.gem features like thread/frame switching:
    • They should directly start binding.b because doing binding.irb -> debug every time is a lot more work than binding.b.
    • Also, this shouldn't be too difficult because you already need to write gem "debug" to use debug.gem and Bundler.require will automatically require it on a Rails app.
  • Users who need only some debug.gem features like step/continue/next:
    • We could have pry-byebug -like integration for some commands that don't require mode switching.

@st0012
Copy link
Member Author

st0012 commented Oct 31, 2022

you have to write gem "debug" in Gemfile to use a bundled gem under bundle exec (forced to install it), and then Bundler.require will take care of require (automatically required).

This only applies to some Rails apps though. In my previous and current company's apps, we don't want debug to automatically be required during test run but just installed, so we add require: false. So to use it, we always need require "debug"; debugger, for example.

This is because debug is activated through require, which then creates a new thread and also activates 2 tracepoints. So when running medium-to-large apps' tests with it required, it could cause obvious slow down or even side effects.

I don't think this experience applies to the majority of Rails devs. But if we just look at Shopify, all major Rails apps have require: false for the debug gem. So I think there's still a considerable number of devs don't have debug required by default.

And as you said, library developers would need to manually require it too.

They should directly start binding.b because doing binding.irb -> debug every time is a lot more work than binding.b.

I don't mean people would use it every time they want to debug something. Personally, I always throw binding.b when I know debug is required. But if it's not, like in a gem, I use binding.irb by default instead. And I can say I do the irb -> debug transition at least once/issue.

I think it's a feature like debug's edit command (which opens the current context's file in the editor): it's not an everyday feature like ls or show-source, but it makes transition between tools easier when needed.

We could have pry-byebug -like integration for some commands that don't require mode switching.

I think it's a good idea. But I'm also curious about more details you have in mind. Like, should it be another gem? Or it should be baked in debug gem or irb gem?

Maybe we can have another issue for this?

@k0kubun
Copy link
Member

k0kubun commented Nov 17, 2022

I changed my mind. I support adding this to IRB in Ruby 3.2.

Since you already have a working demo, can you make https://github.com/st0012/irb/commits/irb-debug a pull request so that we can merge it first and improve it from there as needed?

We could have pry-byebug -like integration for some commands that don't require mode switching.

I spent a couple of nights experimenting with this idea. I have a working-ish seamless implementation of next/break/delete, which is cool, but I found it's not so easy to support corner cases and step/finish with a reasonable amount of IRB code and the current set of debug.gem APIs. At least, I don't have enough bandwidth to realize this idea before Ruby 3.2.

Also, while working on it, I noticed that all of these commands need to share an implementation to start the DEBUGGER__, which I would put at lib/irb/cmd/debug.rb. If it's needed for what I proposed anyway, adding the command to switch to debug.gem would bring no extra complexity.

Because of the complexity and the portion that will be shared anyway, I think it's more reasonable/realistic to introduce what you're proposing first.

This only applies to some Rails apps though. In my previous and current company's apps, we don't want debug to automatically be required during test run but just installed, so we add require: false. So to use it, we always need require "debug"; debugger, for example.

I don't mean people would use it every time they want to debug something. Personally, I always throw binding.b when I know debug is required. But if it's not, like in a gem, I use binding.irb by default instead. And I can say I do the irb -> debug transition at least once/issue.

While I still think pry-byebug -like integration is more useful, I do agree that your proposal is a good enough solution to those problems too. Now that I understand my idea is a long shot for Ruby 3.2, I'd rather like to see Ruby 3.2 with the feature you proposed as an alternative. Let's do this!


Finally, as to the discussion points you raised:

Should the command be debug or debugger?

+1 on debug. debug.gem seems to have irb command (instead of binding.irb), so it seems more consistent to have debug as the command name. debugger command would need to shadow Kernel#debugger, which would be extra complexity.

When users exit the debugger, should they exit completely or return to irb?
IMO, it should exit completely so users don't need to exit twice.

Agreed.

Because irb registers autocompletion logic to reline, debug's console (also powered by reline) will inherit that too.
Perhaps it should be suspended to avoid unexpected side-effects?

I'm still disabling autocompletion (which would remain as is at least until https://bugs.ruby-lang.org/issues/18996 is added), so I don't have any preference yet. Please implement what feels the most convenient for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

2 participants