debug icon indicating copy to clipboard operation
debug copied to clipboard

debugger and binding.b are blocking in rspec, and not binding.pry for instance

Open dorianmariecom opened this issue 3 years ago • 17 comments

Your environment

  • ruby -v: ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-darwin20]
  • rdbg -v: rdbg 1.3.4

Describe the bug

When I do debugger or binding.b in my tests it blocks the background requests

To Reproduce

For instance:

require "rails_helper"

RSpec.describe "Approvals" do
  let!(:user) { create(:user) }
  let!(:approved_user) { create(:user) }

  before { create(:event, :approved, user: approved_user) }

  it "can approve events" do
    system_login_as(user)
    binding.b
  end
end

Expected behavior

Requests are not blocked, like what binding.pry does

Additional context

I'm using the preload, maybe that's related

dorianmariecom avatar Nov 21 '21 15:11 dorianmariecom

Also happens without the preload

dorianmariecom avatar Nov 21 '21 15:11 dorianmariecom

@dorianmariefr can you provide an example app? I also use debugger with rspec for my Rails project, and the request specs can run without any issue (whether the breakpoint is set in the spec file or the application).

st0012 avatar Nov 21 '21 15:11 st0012

You can try in https://github.com/dorianmariefr/parlement and put a debugger before the register here https://github.com/dorianmariefr/parlement/blob/master/spec/system/main_spec.rb#L11

and then click on "register" and notice nothing happens

dorianmariecom avatar Nov 21 '21 15:11 dorianmariecom

@dorianmariefr the app takes multiple env var to run, e.g. SECRET_KEY_BASE and BASE_URL (needed for capybara). it also requires postgresql to start. can you either make it easier to setup, or create a simpler application/script for reproducing the issue? thanks.

st0012 avatar Nov 30 '21 20:11 st0012

btw, I think the cause may be related to the forking mechanism as well, similar to https://github.com/ruby/debug/issues/336. can you try putting this in rails_helper and see if it fixes the issue? (as a temporary workaround)

ENV["RUBY_DEBUG_FORK_MODE"] ||= "parent"

st0012 avatar Nov 30 '21 21:11 st0012

You can put this in a .env.test:

HOST=localhost:4312
BASE_URL=http://localhost:4312

And I get the blocking behavior with and without the ENV

(rdbg) ENV["RUBY_DEBUG_FORK_MODE"]    # ruby
"parent"

dorianmariecom avatar Dec 01 '21 05:12 dorianmariecom

@dorianmariefr I'm now able to reproduce the behavior locally. But I think this may be an intentional behavior.

When breakpoint is activated, the debugger will enter a subsession:

https://github.com/ruby/debug/blob/bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/lib/debug/session.rb#L231-L232

And then it'll stop all threads under the process with thread stopper

https://github.com/ruby/debug/blob/bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/lib/debug/session.rb#L1500

https://github.com/ruby/debug/blob/bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/lib/debug/session.rb#L1468-L1478

This includes all the threads

--> #0 (sleep)@/Users/st0012/projects/parlement/spec/system/main_spec.rb:14:in `block (2 levels) in <top (required)>'
    #1 (sleep)@#<Thread:0x00007fbee3127448 /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/bundler/gems/rails-6f2c4027e820/activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb:40 sleep> (not under control)
    #2 (sleep)@#<Thread:0x00007fbf12abf210 /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/capybara-3.36.0/lib/capybara/server.rb:76 sleep_forever> (not under control)
    #3 (puma reactor)@/Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/reactor.rb:75:in `block in select_loop'
    #4 (puma server threadpool reaper)@/Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/thread_pool.rb:310:in `block in start!'
    #5 (puma server threadpool trimmer)@#<Thread:0x00007fbef5e0af68@puma server threadpool trimmer /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/thread_pool.rb:307 sleep> (not under control)
    #6 (puma server)@#<Thread:0x00007fbef5e0ad38@puma server /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/server.rb:259 sleep> (not under control)
    #7 (puma server threadpool 001)@#<Thread:0x00007fbee38d2a58@puma server threadpool 001 /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/thread_pool.rb:104 sleep_forever> (not under control)
    #8 (puma server threadpool 002)@/Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/map.rb:123:in `initialize'
    #9 (worker-1)@#<Thread:0x00007fbf02918390@worker-1 /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:332 sleep> (not under control)

(Thread 7 and 8, puma server threadpool 001 and 002, are the ones that serve requests).

So when you try to make another request from the browser, there will be no threads to serve them.

But I don't know why it's designed this way and need @ko1 to add a comment here.

st0012 avatar Dec 04 '21 20:12 st0012

thanks for looking into it, it prevents me from debugging my system specs with debug

On Sat, Dec 04, 2021 at 21:36:49, Stan Lo < @.*** > wrote:

@ dorianmariefr ( https://github.com/dorianmariefr ) I'm now able to reproduce the behavior locally. But I think this may be an intentional behavior.

When breakpoint is activated, the debugger will enter a subsession:

https:/ / github. com/ ruby/ debug/ blob/ bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/ lib/ debug/ session. rb#L231-L232 ( https://github.com/ruby/debug/blob/bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/lib/debug/session.rb#L231-L232 )

And then it'll stop all threads under the process with thread stopper

https:/ / github. com/ ruby/ debug/ blob/ bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/ lib/ debug/ session. rb#L1500 ( https://github.com/ruby/debug/blob/bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/lib/debug/session.rb#L1500 )

https:/ / github. com/ ruby/ debug/ blob/ bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/ lib/ debug/ session. rb#L1468-L1478 ( https://github.com/ruby/debug/blob/bd6947b7aa14c7c0d5ae97b62fe53af1b5738659/lib/debug/session.rb#L1468-L1478 )

This includes all the threads

--> #0 (sleep)@/Users/st0012/projects/parlement/spec/system/main_spec.rb:14:in block (2 levels) in <top (required)>' #1 (sleep)@#<Thread:0x00007fbee3127448 /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/bundler/gems/rails-6f2c4027e820/activerecord/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb:40 sleep> (not under control) #2 (sleep)@#<Thread:0x00007fbf12abf210 /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/capybara-3.36.0/lib/capybara/server.rb:76 sleep_forever> (not under control) #3 (puma reactor)@/Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/reactor.rb:75:in block in select_loop' #4 (puma server threadpool reaper)@/Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/thread_pool.rb:310:in block in start!' #5 (puma server threadpool trimmer)@***@***.*** server threadpool trimmer /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/thread_pool.rb:307 sleep> (not under control) #6 (puma server)@***@***.*** server /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/server.rb:259 sleep> (not under control) #7 (puma server threadpool 001)@***@***.*** server threadpool 001 /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/puma-5.5.2/lib/puma/thread_pool.rb:104 sleep_forever> (not under control) #8 (puma server threadpool 002)@/Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/map.rb:123:in initialize' #9 (worker-1)@@.*** /Users/st0012/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/concurrent-ruby-1.1.9/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb:332 sleep> (not under control)

(Thread 7 and 8, puma server threadpool 001 and 002 , are the ones that serve requests).

So when you try to make another request from the browser, there will be no threads to serve them.

But I don't know why it's designed this way and need @ ko1 ( https://github.com/ko1 ) to add a comment here.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub ( https://github.com/ruby/debug/issues/404#issuecomment-986089190 ) , or unsubscribe ( https://github.com/notifications/unsubscribe-auth/AOASD56VY6T64QAYXECHI6LUPJ3WDANCNFSM5IPICIWQ ).

dorianmariecom avatar Dec 05 '21 06:12 dorianmariecom

Without stopping all threads, we can not observe the status of the program. Do you want to start all threads except the current thread?

ko1 avatar Dec 17 '21 17:12 ko1

I guess it could stop only the current thread?

On Fri, Dec 17, 2021 at 18:13:06, Koichi Sasada < @.*** > wrote:

Without stopping all threads, we can not observe the status of the program. Do you want to start all threads except the current thread?

— Reply to this email directly, view it on GitHub ( https://github.com/ruby/debug/issues/404#issuecomment-996885190 ) , or unsubscribe ( https://github.com/notifications/unsubscribe-auth/AOASD55EAUJE2MTXBUZ6ZHDURNVSFANCNFSM5IPICIWQ ). Triage notifications on the go with GitHub Mobile for iOS ( https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 ) or Android ( https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub ). You are receiving this because you were mentioned. Message ID: <ruby/debug/issues/404/996885190 @ github. com>

dorianmariecom avatar Dec 18 '21 09:12 dorianmariecom

in theory, yes. we don't provide this feature yet.

ko1 avatar Dec 20 '21 21:12 ko1

How about we introduce a setting like: RUBY_DEBUG_CONCURRENCY_CONTROL_LEVEL with these levels:

  • :thread - only stops the current thread.
  • :ractor - stops all threads under the Ractor (introduced after the Ractor support).
  • :process (default) - stops all threads under the process.

st0012 avatar Apr 10 '22 10:04 st0012

Good idea, maybe Debug.concurrency = :thread in Ruby ?

dorianmariecom avatar Apr 10 '22 10:04 dorianmariecom

Consider the current interface we have, may be

DEBUGGER__::CONFIG[:concurrency_control_level] = :thread

st0012 avatar Apr 10 '22 11:04 st0012

Please survey other debuggers. It is not simple.

ko1 avatar Apr 17 '22 16:04 ko1

Hello! I'd like to reopen this conversation. We'd like to migrate our codebase from pry-byebug to debug, but this issue is the last thing holding us back.

Putting a breakpoint in a system test, in order to stop at a specific step, but still be able to play with our SPA (only the capybara thread being blocked), is an important part of our workflow. Unfortunately, it's currently impossible with debug.

Maybe also worth to mention others cases where it could be interesting, like https://github.com/Shopify/ruby-lsp-rails/issues/29.

@ko1 do you think that the proposal of @st0012 , could be concidered?

beauraF avatar Feb 06 '24 23:02 beauraF

@beauraF you can use binding.irb instead, and it's shipped by default, no need for an extra gem.

You can have an alias from binding.pry to binding.irb for those used to pry

dorianmariecom avatar Feb 07 '24 10:02 dorianmariecom