【问题标题】:Debugging threaded ruby applications调试线程化的 ruby​​ 应用程序
【发布时间】:2015-04-15 09:25:07
【问题描述】:

我正在开发一个使用线程的 ruby​​ 应用程序。我很难调试它。主要问题是我想单步执行一个特定的线程。我设置了断点并运行,但在我使用thr sw 2 之类的东西之前什么都没有发生。但是随后的输入是FUBAR'd。我尝试过 pry,但 pry 似乎并不擅长处理线程。

解决方案?变通方法?

编辑 1:

仅供参考:Pry version 0.10.1 on Ruby 2.0.0byebug

这就是我对 Pry 的体验。尝试1:我使用break设置线程特定断点

  1. 让程序运行。
  2. 断点被触发。
  3. Pry 正确地显示了断点和周围的代码。
  4. 现在什么都没有。 我没有提示。控制-C 不起作用。事实上,我必须从外部杀死 -9 进程。

尝试 2:使用上面的“binding.pry”方法。

  1. 一样
  2. 一样
  3. 一样!
  4. 我收到一个 Pry 提示!我可以产生表达式。
  5. 我尝试使用“step”或“next”,但突然间我进入了“Pry::history#load”。所以现在调试器已经跳转到处理输入本身的线程。这不起作用。

撬出输出:

[2] pry(#<QDS::Node::Primary>)> step

From: /usr/local/rvm/gems/ruby-2.0.0-p598/gems/pry-0.10.1/lib/pry/history.rb @ line 37 Pry::History#load:

    35: def load
    36:   @loader.call do |line|
 => 37:     @pusher.call(line.chomp)
    38:     @history << line.chomp
    39:     @original_lines += 1
    40:   end
    41: end

[2] pry(#<Pry::History>)>
  1. 我尝试“退出”,现在我在“pry(main)”,没有任何其他工作。

撬出输出:

[1] pry(main)> continue
Error: Cannot find local context. Did you use `binding.pry`?

【问题讨论】:

  • 我无法获取降价格式以正确地将 ruby​​ 输出显示为代码。我不知道为什么。

标签: ruby multithreading debugging


【解决方案1】:

pry 可以很好地处理线程。您的代码中可能存在故障。

让我们尝试检查以下线程切换器(以here 为例):

require 'pry'

module SeqExec
  class Seqs
    attr_reader :init
    def synch_prior mx, cv
      Thread.new {
        mx.synchronize {
          @init[:prior] = true
          loop do
            cv.wait mx
            # binding.pry
            yield if block_given?
            cv.broadcast
          end
        }
      }
    end

    def synch_posterior mx, cv
      Thread.new {
        mx.synchronize {
          @init[:posterior] = true
          loop do
            cv.wait mx
            yield if block_given?
            cv.broadcast
          end
        }
      }
    end

    def synch λ1, λ2
      @init = {}

      mx = Mutex.new
      cv = ConditionVariable.new

      synch_prior(mx, cv, &λ1)     # prior function
      Thread.pass until {:prior=>true} == @init

      synch_posterior(mx, cv, &λ2) # posterior function
      Thread.pass until {:prior=>true,:posterior=>true} == @init

      cv.signal                    # we are ready to start
    end
  end
end

module SeqExec
  Thread.abort_on_exception = true
  def pre &cb
    @prior = cb
  end
  def post &cb
    @posterior = cb
  end
  def run λ1 = nil, λ2 = nil
    pre &λ1 if λ1
    post &λ2 if λ2
    raise ArgumentError.new "Cannot run sequential execution, lambdas are not set" \
      unless (@prior && @posterior)
    Seqs.new.synch @prior, @posterior
  end
end

include SeqExec

@i=0
@stack = []
pre { sleep 0.3; print "-#{@i += 1}-"; @stack.push(@i) }
post { print "|#{@stack.pop}|" }
run

10.times { sleep 0.1 }
sleep 30000

注释掉binding.pry,它会打印:

#⇒ -1-|1|-2-|2|-3-|3|-4-|4|-5-|5|-6-|6|-7-........

binding.pry 未注释,我们屈服:

Frame number: 0/2
From: /tmp/a.rb @ line 12 SeqExec::Seqs#synch_prior:

     6: def synch_prior mx, cv
     7:   Thread.new {
     8:     mx.synchronize {
     9:       @init[:prior] = true
    10:       loop do
    11:         cv.wait mx
 => 12:         binding.pry
    13:         yield if block_given?
    14:         cv.broadcast
    15:       end
    16:     }
    17:   }
    18: end

▶ mx
=> #<Mutex:0xb21f204>
▶ exit
-1-|1|
Frame number: 0/2

From: /tmp/a.rb @ line 12 SeqExec::Seqs#synch_prior:

     6: def synch_prior mx, cv
     7:   Thread.new {
     8:     mx.synchronize {
     9:       @init[:prior] = true
    10:       loop do
    11:         cv.wait mx
 => 12:         binding.pry
    13:         yield if block_given?
    14:         cv.broadcast
    15:       end
    16:     }
    17:   }
    18: end

▶ exit
-2-|2|
Frame number: 0/2
...

不用说,上面的意思是线程停止直到pry被恢复。

【讨论】:

  • 对我来说并不是这样。首先,您建议“代码中的故障”会阻止调试器正常工作。你知道这听起来有多荒谬吗?我正在编辑我的原始问题以说明 Pry 和线程的问题。
  • 这听起来一点也不荒谬。例如。死锁会破坏任何调试器尝试(我知道这不是您的情况,只是一个示例。)我的猜测是尝试删除 byebug 依赖项并测试安装了 pry 的干 gemset 上会发生什么。我从来没有遇到过你描述的行为(窥探它自己的上下文),虽然我有一些线程调试的经验。这就是为什么我建议您的代码/环境中存在一些故障。
  • 当然会有一些小故障。这就是调试器的用途 :P 如果没有byebug,我如何通过代码step
  • 好吧,你的争论超过了我,调试器不应该屈服于故障,但没有人是完美的 :) 无论如何,你把我弄糊涂了。我很担心,step 是 pry 自己的功能,因为 ruby​​2。我已经尝试了我提供给byebug 的代码——它就像一个魅力一样工作(进入等等)。我认为,上面的工作示例和您的开发代码之间的主要区别在于,设备设计通常没有静默,其他线程仍在运行,可能会结束,这会将撬动的东西混在一起。我建议您在每个线程中设置断点,以防止调试环境中的竞争条件。
  • " 我建议您在每个线程中设置断点,以防止调试环境中的竞争条件" 哦,这是一个想法。令人沮丧的是,我必须从头开始测试线程。谢谢。我会跟进的。
猜你喜欢
  • 1970-01-01
  • 2010-09-21
  • 1970-01-01
  • 2013-04-17
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-01
相关资源
最近更新 更多