Python's archives

rubyプロセスのコアファイルからバックトレースする。(おまけ: python)

by ginriki | 11 月 9th, 2009 

某所で、「rubyプロセスがSEGVとかした時のコアファイルから、rubyスクリプトのバックトレースは取れるの?」って聞かれたので、ちょっと調べてみました。

結論としては、「rubyメソッドの呼び出し位置(ファイル名, 行番号)は取れるけど、呼び出し時の実引数を見るのは難しい」っていう感じです。
rubyのスタックフレームは、当然、rubyプロセスのメモリ上に構築されるので、スタックフレームのデータ構造さえわかれば、ある程度は表示できます。

ただ、コアファイルを出力したrubyプロセスはすでに存在しないので、ruby実装に使われているC関数をデバッガで(正確にはrubyプロセス上で)実行することができません。
そのため、オブジェクトのinspectなどを実行することが難しく、実引数のオブジェクトが何か調べることが困難です。ということで、今回はスタックフレームを表示させる方法を以下で説明します。

rubyスタックフレームを表示するGDBスクリプトは以下になります(rb_dump.gdb)。
(moriyoshiさんのブログ「 GDBで実行中のスクリプト言語のスタックフレームをダンプしてみる試み」のコードをほとんどそのまま使わせていただきました。ありがとうございます。)

CODE:
  1. define dump_rb_bt_from_core
  2.   set $t = ruby_frame
  3.   while $t
  4.     printf "[0x%08x] ", $t
  5.     if $t->node.nd_file
  6.       printf "(%s:%d)\n", $t->node.nd_file, ($t->node.flags>> 19) & ((1 <<(sizeof(NODE*) * 8 - 19)) - 1)
  7.     else
  8.       printf "(UNKNOWN)\n"
  9.     end
  10.     set $t = $t->prev
  11.   end
  12. end
  13.  
  14. document dump_rb_bt_from_core
  15.   dumps the current frame stack from core file. usage: dump_rb_bt_from_core
  16. end

今回はサンプルプログラムとして、0アドレスにアクセスするC拡張ライブラリを実行するrubyスクリプトを用意します。
まず、C拡張ライブラリ(segv.c)、

C:
  1. #include "ruby.h"
  2.  
  3. VALUE do_segv(VALUE self){
  4.         *(char*)(0x0) = 0;
  5.         return Qnil;
  6. }
  7.  
  8. void Init_segv(){
  9.         VALUE module;
  10.         module = rb_define_module("Segv");
  11.         rb_define_module_function(module, "do_segv", &do_segv, 0);
  12. }

で、後は適当にextconf.rbを作って、segv.soを作ります。

次にsegv.soを呼び出すRubyスクリプト(test.rb)。

RUBY:
  1. require 'segv'
  2.  
  3. def test1(a)
  4.   test2(a)
  5. end
  6.  
  7. def test2(b)
  8.   Segv::do_segv
  9. end
  10.  
  11. test1(1)

さて、サンプルプログラムの用意ができたので、SEGVさせてみます。

$ ulimit -c unlimited
$ ruby test.rb
test.rb:8: [BUG] Segmentation fault
ruby 1.8.5 (2006-08-25) [i386-linux]

アボートしました (core dumped)

これでコアファイルが出力されたので、それをGDBで解析します。

今回はCentOS 5.3のyumでインストールしたrubyから出力されたコアファイルなので、
GDBで解析するには、ここからrubyのdebuginfoをインストールしておく必要があります。

$ wget http://debuginfo.centos.org/5/i386/ruby-debuginfo-1.8.5-5.el5_3.7.i386.rpm
# rpm -i ruby-debuginfo-1.8.5-5.el5_3.7.i386.rpm

それでは、GDBでコアファイルからrubyのバックトレースをさせてみます。

$ gdb ruby core.29443
GNU gdb Fedora (6.8-37.el5)
...
(gdb) backtrace   #通常のC関数レベルのバックトレース。
#0  0x00b3a402 in __kernel_vsyscall ()
#1  0x0056fdf0 in raise () from /lib/libc.so.6
#2  0x00571701 in abort () from /lib/libc.so.6
#3  0x00c514c2 in rb_bug (fmt=) at error.c:214
#4  0x00cbd80b in sigsegv (sig=) at signal.c:537
#5  [signal handler called]
#6  do_segv (self=3086662140) at segv.c:4
#7  0x00c54dd5 in call_cfunc (...) at eval.c:5657
#8  0x00c5c4ab in rb_call0 (...) at eval.c:5810

(gdb) source rb_dump.gdb      #この記事の最初に作ったGDBスクリプトをロード
(gdb) dump_rb_bt_from_core  # rubyバックトレース
[0xbfc1da20] (test.rb:8)
[0xbfc1e0e0] (test.rb:4)
[0xbfc1e7c0] (test.rb:11)
[0x00d0f960] Cannot access memory at address 0x4

以上になります。

pythonもコアファイルからスタックフレームが取れるか調べてみましたが、
DebuggingWithGDBのgdbinitスクリプトを読む限り、
rubyと同様に実行していたpythonスクリプトのファイル名と行番号は取れそうです。

PythonのインタラクティブモードでTab補完を使う

by ginriki | 9 月 25th, 2008 

昨日の記事の続き

このまま、各言語のインタラクティブモードの補完機能について書いてけば、
しばらくネタに困らないので続けよう。

今回はPythonです。

pythonの場合、引数なしで起動すればインタラクティブモードになりますが、
下記のようにrlcompleterをimportすることで、補完機能が使えます。
ただし、readlineライブラリが必要です。Linuxとかなら、yum install pythonで普通に入るようです。

PYTHON:
  1. $ python
  2. >>> import rlcompleter
  3. >>> rlcompleter.readline.parse_and_bind('tab: complete')
  4. >>> di [TAB]
  5. dict    dir     divmod
  6. >>> di

Windowsの人は、Python Alternative Readlineをインストールするのが楽だと思います。

若干、通常のreadlineと挙動が違うのがアレですが・・・。

参考サイト

tracプラグインとか

in Python, trac
by ginriki | 11 月 21st, 2007 

なぜか大学院の授業でtracのプラグインを作ることになった。

とりあえずプラグインのtutorialを見てたら、プラグインが表示するHTML用テンプレートの記述に "include header.cs"みたいなのを発見。

csファイル*1ってなんだよと思って調べてみたらClearSilverの略だと判明。

http://discypus.jp/wiki/?%A5%BD%A5%D5%A5%C8/Bug%20Tracking/tracを見たところ、

  • trac0.10 まではテンプレートエンジンとしてClearSilverを使ってる。ClearSilverはC言語で書かれている
  • trac0.11からはPythonで書かれているGenshiをテンプレートエンジンとして使う
    • ClearSilverがコンパイルできないような環境でもtracが使えるようにするため
    • ただ、これまでのプラグインも動作させるため、trac0.11でもClearSilverは使えるらしい

ということらしい。

ClearSilverとGenshiの記述上の違いは以下のサイトが参考になりそう。

http://trac.edgewall.org/wiki/TracDev/PortingFromClearSilverToGenshi

*1:C#のソースファイルではありません

ブログで紹介した商品

  • Image of デバッガの理論と実装 (ASCII SOFTWARE SCIENCE Language)