Ruby自身のデバッガだと、任意のタイミングでアタッチしてbacktrace見るといったことができない(はず)なので、もっと低レベルのデバッガを利用して、それを実現する方法についてメモ。今回はJRubyに対してjdbでアタッチ&backtraceする方法についてのメモです。
なお、CRubyの場合は、以前の記事とか、さらにその先のリンク先を見れば良いです。
ちなみに、今回試したJRubyのversionは以下の通り。OSはCentOS 5.3です。
$ jruby --version jruby 1.3.1 (ruby 1.8.6p287) (2009-06-15 2fd6c3d) (Java HotSpot(TM) Client VM 1.6.0_16) [i386-java]
jdbによるbacktrace
まず、アタッチ対象例のRubyコードとして以下を用意。
sample.rb
[RUBY]
def foo
p “abc”
bar
end
def bar
p “foge”
sleep(1000)
end
abc
[/RUBY]
そして、デバッガattach用のポートを開けて起動します
$ jruby -J-Xdebug -J-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n sample.rb
barメソッド内のsleepの実行をしているうちに、別のターミナルを開いてjdbでアタッチし、スレッドを停止させます。
$ jdb -attach 8000 > suspend すべてのスレッドが中断されました
そして、backtraceを表示させます。以下の方法で大体表示できます(いい加減なやり方ですが)。
> threads グループ system: (java.lang.ref.Reference$ReferenceHandler)0x9db Reference Handler 状況待機中 (java.lang.ref.Finalizer$FinalizerThread)0x9da Finalizer 状況待機中 (java.lang.Thread)0x9d9 Signal Dispatcher 実行中 グループ main: (java.lang.Thread)0x1 main 状況待機中 > thread 0x1 main[1] where [1] java.lang.Object.wait (ネイティブ メソッド) [2] org.jruby.RubyThread.sleep (RubyThread.java:718) [3] org.jruby.RubyKernel.sleep (RubyKernel.java:725) [4] org.jruby.RubyKernel$s_method_0_1$RUBYINVOKER$sleep.call (null) [5] org.jruby.internal.runtime.methods.JavaMethod$JavaMethodN.call (JavaMethod.java:620) ...(以下略) main[1] up 4 main[5] locals メソッド引数: context = instance of org.jruby.runtime.ThreadContext(id=2527) self = instance of org.jruby.RubyObject(id=2528) clazz = instance of org.jruby.MetaClass(id=2529) name = "sleep" arg0 = instance of org.jruby.RubyFixnum(id=2531) block = instance of org.jruby.runtime.Block(id=2532) ローカル変数: main[5] dump context.frameIndex context.frameIndex = 2 #frameStackの上限(たぶん) main[5] dump context.file + ":" + context.line context.file + ":" + context.line = "sample.rb:7" #現在位置 main[5] dump context.frameStack[2].fileName + ": " + context.frameStack[2].name + ":" + context.frameStack[2].line ...(省略)... = "sample.rb: bar:2" #1つ前のスタックフレーム main[5] dump context.frameStack[1].fileName + ": " + context.frameStack[1].name + ":" + context.frameStack[1].line ...(省略)... = "sample.rb: foo:10" #2つ前のスタックフレーム main[5] dump context.frameStack[0].fileName + ": " + context.frameStack[0].name + ":" + context.frameStack[0].line ...(省略)... = ": null:0" #トップレベル
以上になります。