LoginSignup
2
1

More than 5 years have passed since last update.

Rubyでスレッドをデバッグする

Last updated at Posted at 2018-12-07

環境

  • Ruby 2.5.0
  • byebug 10.0.2

byebugにはスレッドをデバッグするために thread というコマンドがあり、以下の機能がある。

デバッガーでthread(短縮形はth) コマンドを使用すると、スレッド実行中にスレッドのリスト表示/停止/再開/切り替えを行えます。このコマンドには以下のささやかなオプションがあります。

threadは現在のスレッドを表示します。
thread listはすべてのスレッドのリストをステータス付きで表示します。現在実行中のスレッドは「+」記号と数字で示されます。
thread stop nはスレッド n を停止します。
thread resume nはスレッド n を再開します。
thread switch nは現在のスレッドコンテキストを n に切り替えます。
https://railsguides.jp/debugging_rails_applications.html#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89

使用例

hoge.rb
# デッドロックするスクリプト

require "byebug"

threads = []

m1 = Mutex.new
m2 = Mutex.new

threads << Thread.new do
  m1.synchronize do
    puts "thread1: enter m1.synchronize"
    sleep 1
    m2.synchronize do
      puts "thread1: enter m2.synchronize"
    end
  end
end

threads << Thread.new do
  m2.synchronize do
    puts "thread2: enter m2.synchronize"
    sleep 1
    m1.synchronize do
      puts "thread2: enter m1.synchronize"
    end
  end
end

# 普通、スレッドの終了待ちするときは threads.each {|t| t.join } のように書くと思うが、デバッグするためにこの待ち方をしている
while threads.any? {|t| t.alive? }
  puts "waiting..."
  sleep 1
  #byebug
end

puts "end"

上記のスクリプトは2個のスレッドを作成しており、それぞれm1, m2 m2, m1の順でmutexロックを取得しようとするので、デッドロックする。実際に実行すると次のような出力になる:

$ ruby hoge.rb
waiting...
thread1: enter m1.synchronize
thread2: enter m2.synchronize
waiting...
waiting...
waiting...
waiting...
waiting...
(永久にwaiting...が続く)

これをデバッグするため、スクリプトの# byebugの行のコメントアウトを外して実行する。

$ ruby hoge.rb
waiting...
thread1: enter m1.synchronize
thread2: enter m2.synchronize

[21, 30] in /Users/val00362/hoge.rb
   21:       puts "thread2: enter m1.synchronize"
   22:     end
   23:   end
   24: end
   25:
=> 26: while threads.any? {|t| t.alive? }
   27:   puts "waiting..."
   28:   sleep 1
   29:   byebug
   30: end
(byebug) th list
+ 1 #<Thread:0x00007fd8c107dd38 run> /Users/val00362/hoge.rb:26
  2 #<Thread:0x00007fd8c08a16f0@hoge.rb:8 sleep_forever> hoge.rb:11
  3 #<Thread:0x00007fd8c08a1538@hoge.rb:17 sleep_forever> hoge.rb:20
(byebug) th sw 2
  2 #<Thread:0x00007fd8c08a16f0@hoge.rb:8 sleep_forever> hoge.rb:11
waiting...

[21, 30] in /Users/val00362/hoge.rb
   21:       puts "thread2: enter m1.synchronize"
   22:     end
   23:   end
   24: end
   25:
=> 26: while threads.any? {|t| t.alive? }
   27:   puts "waiting..."
   28:   sleep 1
   29:   byebug
   30: end
(byebug) bt
--> #0  block in block in <main> at /Users/val00362/hoge.rb:26
    ͱ-- #1  Array.any?(*args) at /Users/val00362/hoge.rb:26
    #2  <main> at /Users/val00362/hoge.rb:26

メインスレッドのbyebugのところでデバッガが起動するので、th listでスレッド一覧表示、th sw 2でスレッド切り替え、btでバックトレース表示(今どこを実行中か確認)している。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1