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スクリプトのファイル名と行番号は取れそうです。

CentOS 5にAspire Timeline (3810T)の有線LANドライバを入れる

by ginriki | 11 月 4th, 2009 

今年(2009年)の夏に、Aspire Timeline(3810T)を買いました。
元々Windows Vistaが入っていましたが、ついでにCentOS 5.31を入れてデュアルブートできるようにしました。

ただ、CentOS 5.3には (たぶん、CentOS 5.4にも) Aspire Timelineの有線LANドライバが入ってないので、ドライバを探してきて入れました。

ドライバは、http://partner.atheros.com/Drivers.aspxのAR81Family Linux Driverになります。私がダウンロードしたときは、AR81Family-linux-v1.0.0.10.tar.gzっていうバージョンでした。

いつの間にか、リンク切れになってました(partner.atheros.comのサイト自体なくなったようです)。12/2時点では、ドライバはここにあります。(katty9015さん、情報ありがとうございます)
ファイル名もAR81Family-linux-v1.0.0.10.tar.gzからAR81Family-linux-v1.0.1.0.tar.gzに変わったので、以下の記事を読むときは注意してください。

USBメモリとかでAspire TimelineのCentOS上にAR81Family-linux-v1.0.0.10.tar.gzを入れた後は、以下のようにコンパイル/インストールすれば、有線LANが使えるようになります。

$ tar xzvf AR81Family-linux-v1.0.0.10.tar
$ cd src/
$ make
$ make install       # 要root権限
$ reboot             # 再起動


おまけ (Intel VTの有効化)

Aspire Timeline(3810T)のCPUはIntel VTに対応してるのですが、BIOSで有効化できないため、そのままだとIntel VTが使えません。
とはいえ、CentOSのXenでWindowsが起動できないのも味気ないです。
以下の2つのリンク先を参考にいじれば、BIOSでIntel VTを有効にできます。Acerのサポート外になってしまうので、いじるときは自己責任でお願いします。

  1. 2009/10に、CentOS 5.4がリリースされましたね []

GPSログと撮影時刻を元に移動経路と写真をGoogle mapへ表示

by ginriki | 7 月 20th, 2009 

半年ほど前に、ハンディGPSのGarmin Oregon 300を買いました1
私の主な用途は旅行などの移動経路を記録することですが、地図情報をちゃんと入れておけば道に迷った時も重宝します。

今回は記録した移動経路と移動経路上で撮った写真をgoogle mapに表示する方法について説明します。
・・・といっても、フリーソフトの轍 Wadachiを使えば簡単に表示できます2

基本的な使い方は上のリンク先の「標準的な作業の流れ」を見てもらえればよいです。

ロンドン旅行したときの移動経路ログと写真を轍で編集してGoogle mapで表示したものを下記に貼りました3。移動経路は日付ごとに色分けしてあります。

轍以外の移動経路ログ編集ツールとして使えるソフトとして下記があります。

個人的には、轍が一番使いやすいです。たぶん、写真の縮小画像を自動生成してWaypointとしてgoogle mapで表示する機能があるからだと思います。

この記事の残りで、轍のサイトに載っていない下記作業手順について書きます。

  • 移動経路ログファイルから一部のログを消す。
  • HTMLファイルを使わず、kmlのみでgoogle mapを表示する。上のgoogle mapはkmlのみを使用してます。

Oregon 300は、移動経路のログをGPX形式で保存します4。下記説明もGPX形式のログデータが対象です。まあ、他の形式でも同じだと思います。


移動経路ログファイルから一部のログを消す

ログファイル内の、ある地点のログからGoogle mapで表示したい場合などにこの作業が必要です。
表示を開始したい地点の時刻を調べるためにGoogle Earthを使い、その後、開始地点前のログデータを削除するためにGPSbabelを使いますGPXファイルはXMLで書かれてるので、テキストエディタで手作業で消してもいいですが、GPSBabelの方が楽だと思います。GPX以外のファイル形式にも対応してるし。
Google Earthを使う利点は、下記2点です。

  • 地図を表示しながら開始地点を探せる。
  • GPXファイルのtrkpt表示数に制限がないっぽい。Google mapだと、trkptやwptなど合わせて1000件(100件だったかな?規約に書いてある?)しか表示できませんが、Google Earthには制限がないようです。

一部ログを消す手順は下記の通りです。

  1. Google Earthを起動
  2. Google EarthのウインドウにGPXファイルをドラッグ&ドロップ
  3. 「トラック ポイントとルート ポイントにクリック可能なイメージを作成する」にチェックを付けてインポート。チェックしないと、各地点の時刻を調べることができません。
  4. 起点となるトラックポイントを探してクリック。すると下記ポップアップが出てくると思います。ここのtimeの値を後で使います。もうGoogle Earthは閉じてよいです。

popup

  1. GPSBabelのgui版を起動。InputとしてGPXファイルを指定し、Outputのファイル名は好きなのを付ける。FormatはInput, Outputともに「GPX XML (.gpx)」です。設定例は下記画像の通り。

gpsbabel

  1. Filterボタンを押して、Google Earthで調べた時刻をTracksのStart atに設定する。設定例は下記画像。

  1. 「let's go」ボタンをクリックして変換すれば終了。後は、変換したGPXファイルを轍で読み込んで作業すればよいです。ちなみにコマンドラインでgpsbabelを実行する場合は下記のようにすればよい。
gpsbabel.exe -p "" -w -r -t -i gpx -f "MAR-17-09 062255 AM.gpx" -x track,start=20090316153053 -o gpx -F "MAR-17-09_in_london_test.gpx"


HTMLファイルを使わず、kmlのみでgoogle mapを表示する。

基本的には、轍で編集したデータをKMLでエクスポートして、KMLと轍が生成した縮小画像をWebサーバにアップロードすればよいです。
ただし、kml内の縮小画像への相対パスをフルパス(例: http://www.ginriki.net/~)にする必要があります。
適当に、テキストエディタなどで置換してください。私は下記のようにsedでやりました。

$ sed 's/tour_images\//http:\/\/www.ginriki.net\/wd\/wp-content\/uploads\/maps\/tour_images\//g' tour.kml  > tour2.kml

あとは、アップロードしたkmlのURLをgoogle mapの検索欄に入れれば、kmlがgoogle mapで表示されます。
google map右端の「リンク」というリンクをクリックすれば、ブログ埋め込み用htmlタグが下記のように表示されるので、それをブログに貼りつければよいです。


  1. 日本語版は高いので、英語版をYahooオークションで45,000円程度で買いました。Amazon.comが使えればもっと安く買えたのですが、日本から電気機器類を注文できないようです。 []
  2. はWindows専用ソフトのようです。MacやLinuxの人はごめんなさい。 []
  3. 表示時刻が日本時間になっているので注意。ロンドン時間では-9時間してください。 []
  4. Oregon 300の設定にもよりますが、通常、10秒ごとに現在位置をtrkタグとして書き込むようになっています。 []

ハンドルネームをginrikiに変えました

by ginriki | 7 月 20th, 2009 

そういえば、書くのを忘れてましたがハンドルネームをginriki(「じんりき」と読みます)に変更しました。
大学院を出て働き始めたので、それに合わせてハンドルネームも変えました。

ginrikiの由来は、書く気が起きたら書きます。大した話ではないです。

kprobes – カーネルへ動的にプローブ挿入

by ginriki | 7 月 13th, 2009 

Debug Hacksを読んでたら、kprobesっていうデバッグツールの話があった。
自分でモジュール作ってinsmodするだけで、カーネル再コンパイルなしで、ある実行地点でのカーネル内変数を調べるツールだそうで。

プロセスのユーザ空間(=デバッギのユーザ空間)に対してデバッガプロセスがプローブする場合は1

  1. デバッガプロセスがptrace辺りを使ってデバッギのメモリ空間上の指定アドレス上にあるアセンブリ命令をint 3に書き変えて2
  2. デバッギプロセス実行中にそのint 3命令実行して発生したSIGTRAPをデバッガプロセスがフック(=これも事前にptraceで設定する)して
  3. デバッガプロセスがptraceでデバッギプロセスのメモリ空間から値をread

っていう流れが普通だと思う。kprobes.cのコメントを見てみると、kprobesの場合もカーネルの指定命令アドレスにint 3を入れてるらしい。

/*
 * Called after single-stepping.  p->addr is the address of the
 * instruction whose first byte has been replaced by the "int 3"
 * instruction.  To avoid the SMP problems that can occur when we
 * temporarily put back the original opcode to single-step, we
 * single-stepped a copy of the instruction.  The address of this
 * copy is p->ainsn.insn. ...
 * ...
 */

ただ、SMPの関係で色々対策が必要なんだね3。int 3を実行した後、例外ベクタにジャンプしてからどういう手順で処理してるかについてイメージがわかないから、今度ソース読んでみようかな。

あと、kprobesだと見たい変数(とくにローカル変数などCPUアーキテクチャとデバッグ情報からアドレスを割り出す必要のあるもの)のアドレスを探すのが大変だけど、
kprobes+GDBでそこを解決しようとしてるのがtracepointsってやつらしい。ふむふむ。

  1. 以前、デバッガについて書いた記事もどうぞ。 []
  2. int 3はIA-32とかIntel architectureの場合の話です []
  3. デバッギプロセスにプローブする場合に必要なマルチスレッド対策とどの程度一緒なのかは、まだよくわからない。 []

crashコマンド使ってみた

by ginriki | 7 月 13th, 2009 

Kernelのコアダンプファイルを解析するのに、crashコマンドが便利だという話を聞いたので、練習として触ってみました。

手元にCentOS 5のマシンがあるので、それを使います。

解析にあたってkernelのデバッグ情報がいるので、debuginfoのRPMをインストールします1

 $ uname -a
Linux localhost.localdomain 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i386 GNU/Linux

 $ wget http://debuginfo.centos.org/5/i386/kernel-debuginfo-2.6.18-128.1.16.el5.i686.rpm
 $ wget http://debuginfo.centos.org/5/i386/kernel-debuginfo-common-2.6.18-128.1.16.el5.i686.rpm
 $ rpm -ivh kernel-debuginfo-common-2.6.18-128.1.16.el5.i686.rpm kernel-debuginfo-2.6.18-128.1.16.el5.i686.rpm

んで、crashコマンド実行。
SVR4 UNIXのcrashコマンドをベースにGDBを統合したやつだそうで2、GDBになじんでる私には結構使いやすいです。

CODE:
  1. $ crash
  2.       KERNEL: /usr/lib/debug/lib/modules/2.6.18-128.1.16.el5/vmlinux
  3.     DUMPFILE: /dev/crash
  4.         CPUS: 1
  5.         DATE: Mon Jul 13 02:17:05 2009
  6.       UPTIME: 06:52:42
  7. LOAD AVERAGE: 0.16, 0.03, 0.01
  8.        TASKS: 86
  9.     NODENAME: localhost.localdomain
  10.      RELEASE: 2.6.18-128.1.16.el5
  11.      VERSION: #1 SMP Tue Jun 30 06:10:28 EDT 2009
  12.      MACHINE: i686  (1197 Mhz)
  13.       MEMORY: 758.9 MB
  14.          PID: 3848
  15.      COMMAND: "crash"
  16.         TASK: ef67c000  [THREAD_INFO: d9120000]
  17.          CPU: 0
  18.        STATE: TASK_RUNNING (ACTIVE)
  19.  crash> set 1
  20.     PID: 1
  21. COMMAND: "init"
  22.    TASK: c16f5aa0  [THREAD_INFO: c16f6000]
  23.     CPU: 0
  24.   STATE: TASK_INTERRUPTIBLE
  25.  crash> p $tmp = jiffies
  26.   $1 = 24873000
  27.  crash> x modprobe_path
  28.   0xc0680be0 <modprobe_path>:     das
  29.  crash> show convenience
  30.    $tmp = 24873000
  31.    $__ = void
  32.    $_ = (examine_i_type *) 0xc0680be0 "/sbin/modprobe"
  33.  crash> exit

gdbスクリプトをsourceで読み込むこともできる。
まだ、まともな使い方は全然してないけど、Debug Hacksにいろいろ書いてあるので、それを読みながらやる予定。

  1. 最初、yumでinstallしたら、なぜか2.6.18-92.1.6.el5.centos.plusが入りました。kernelバージョンと合ってないので、rpmコマンドで入れなおし。 []
  2. Crash WhitepaperのAbstract参照 []

e-mobile接続時に割り当てられるIPアドレス範囲

by ginriki | 1 月 1st, 2009 

諸事情により、e-mobileのデータ通信カードが手元にあります。
データ通信カードを使ってインターネットに接続する際に割り当てられるIPは、接続毎に変化します。
この割り当てられるIPアドレスの範囲が気になったので調べてみることにしました。

これは、whoisサーバを使えば調べられます1

#とりあえず、接続した際に割り当てられたIPアドレス(114.48.45.211)で調べる
$ whois -h whois.apnic.net 114.48.45.211
% [whois.apnic.net node-2]
% Whois data copyright terms    http://www.apnic.net/db/dbcopyright.html

inetnum:      114.48.0.0 - 114.51.255.255
netname:      eMobile
...<略>

#netnameがわかったので、その名前で調べ直す
$ whois -h whois.apnic.net eMobile
% [whois.apnic.net node-2]
% Whois data copyright terms    http://www.apnic.net/db/dbcopyright.html

inetnum:      60.254.192.0 - 60.254.255.255
netname:      eMobile
...<略>

inetnum:      117.55.0.0 - 117.55.127.255
netname:      eMobile
...<略>

inetnum:      119.72.0.0 - 119.72.255.255
netname:      eMobile
...<略>

inetnum:      119.72.0.0 - 119.72.255.255
netname:      eMobile
...<略>

なお、inetnumやnetnameの仕様はこちらで規定されてます。

whoisプロトコルに関するメモ

whoisプロトコルの仕様は、RFC3912で規定されています。whoisサーバはTCP 43番ポートでListenするとか、データの区切りが<CR><LF>であることがRFC3912によって決められています。

RFC3912にはwhoisサーバとの通信例として下記が載っています。

clientがwhoisサーバ(whois.nic.mil)にSmithについて問い合わせた場合の通信例。

   client                           server at whois.nic.mil

   open TCP   ---- (SYN) ------------------------------>
              <---- (SYN+ACK) -------------------------
   send query ---- "Smith" -------------------->
   get answer <---- "Info about Smith" ---------
              <---- "More info about Smith" ----
   close      <---- (FIN) ------------------------------
              ----- (FIN) ----------------------------->

実際の所、RFC3912ではsend queryやget answerの部分でやりとりする情報について何も決められていません。

やり取りする情報はwhoisサーバによって変わります。
例えば、APNICのwhoisサーバの場合、各団体へ割り当てを行ったIPアドレス範囲の
問い合わせや応答に使われます。

APNICのwhoisサーバがどのような情報を返すかは下記リンク先を参照してください。
http://www.apnic.net/info/faq/apnic_faq/about_whois.html

なお、APNICの場合、IPアドレス範囲はinetnumという名称でしたが、
ARINのwhoisサーバの場合、NetRangeという名称になります。
http://www.felix-colibri.com/papers/web/whois/whois.html

このあたりは、各団体ごとに仕様がバラバラのようです。

  1. 判明したIPアドレスの範囲すべてがe-mobile接続時に割り当てられうるとは限りませんが []

同一SQLに対する、mysqlのバージョンによる動作の違い

in SQL, mysql
by ginriki | 12 月 5th, 2008 

最近のmysqlで、ASで作ったエイリアス名に1重引用符を付けてORDER BY指定したときの動作が変わりました。
DBサーバを移行してmysqlのバージョンを新しくしたら、PHPコード内のSQL動作結果が変わっていてびっくりしました。

具体例は以下、

SQL:
  1. $ mysql -u root mysql   # mysql-server: 5.0.27
  2. mysql> SELECT COUNT(*) AS cnt FROM user
  3. mysql> GROUP BY User ORDER BY cnt DESC;
  4. +-----+
  5. | cnt |
  6. +-----+
  7. |   4 |
  8. |   1 |
  9. +-----+
  10. 2 rows IN SET (0.00 sec)
  11.  
  12. mysql> SELECT COUNT(*) AS cnt FROM user
  13. mysql> GROUP BY User ORDER BY 'cnt' DESC;
  14. +-----+
  15. | cnt |
  16. +-----+
  17. |   4 |
  18. |   1 |
  19. +-----+
  20. 2 rows IN SET (0.00 sec)
  21. -- cntも'cnt'も同じ動作

SQL:
  1. $ mysql -u root mysql    # mysql-server: 5.0.45
  2. mysql> SELECT COUNT(*) AS cnt FROM user
  3. mysql> GROUP BY User ORDER BY cnt DESC;
  4. +-----+
  5. | cnt |
  6. +-----+
  7. |   4 |
  8. |   1 |
  9. +-----+
  10. 2 rows IN SET (0.00 sec)
  11.  
  12. mysql> SELECT COUNT(*) AS cnt FROM user
  13. mysql> GROUP BY User ORDER BY 'cnt' DESC;
  14. +-----+
  15. | cnt |
  16. +-----+
  17. |   1 |
  18. |   4 |
  19. +-----+
  20. 2 rows IN SET (0.00 sec)
  21. -- cntと'cnt'で違う動作

ORDER BY 'cnt'の 'cnt'部分を文字列リテラルとみなすか、識別子とみなすかの差ですね。

IA-32のjmp, call命令でTrapするGDBスクリプト

by ginriki | 12 月 3rd, 2008 

jmp/call命令の実行直前までプログラムを進めるGDBスクリプトです。
こういう用途に使えるGDBコマンドがないようなので、自分で作りました。
必要な方はご自由に使ってください。
- script.gdb

使い方は以下の通り、

$ gdb a.out
(gdb) source script.gdb
(gdb) break main
(gdb) run
# main関数でbreak

(gdb) run_until_call_jmp
# main関数内のcall or jmp命令直前で停止

jmp,call命令のop codeの調べ方

IA-32命令セットにおける、JMP, CALLのオペコードは下記の通りになりますIA-32 インテル アーキテクチャ・ソフトウェア・ディベロッパーズ・マニュアル 中巻より抜粋。http://www.intel.com/jp/download/index.htm

JMP命令のオペコード
callオペコード

cb、cw、cd、cpは、オペコードの後に続く1,2,4,6バイトの値です。
jmpやcall命令では、jmp/call先のアドレス値を表しています。

/2とか/3とかっていうのは、ModR/Mバイトのdigit(= op codeの一部) 上記マニュアルの2章(特に2.4~2.6)参照を表してます。
op codeの一部なので、当然、命令を判定する際にdigitのチェックが必要です。

digitは/0~/7まであり、ModR/Mバイトの下位3bitに対応します。

例えば32bitの絶対間接nearジャンプのオペコードは下記になります。

16進表記:  FF /4
2進表記 :  11111111 xxxxx100

xxxxxの部分の値によって、ジャンプ先のアドレス値として参照するレジスタ/メモリアドレスが変化します。
例えば、eaxレジスタに格納されたアドレス値へジャンプする場合は、xxxxx=11000 です。

こんな感じで命令のop codeを調べて実装したのが上記のGDBスクリプトです。
スクリプトを改造する時の参考にしてください。

rexml vs libxml-ruby

in Ruby, XML
by ginriki | 11 月 15th, 2008 

研究の関係で、37MBのXMLファイルに対して、XPathでデータを取り出したいという状況が起きました。

プロトタイプ実装として、Ruby標準ライブラリのrexmlでその処理プログラムを記述したのですが、
実行時にメモリを700MB以上消費しやがります1
768MBしか積んでない私のマシンでは、メモリスワップが発生しまくりで実行が極端に重いため、
別の手段を考えることにしました。

手段としては2通り思いつきます。

  1. メモリ使用効率の良いXMLライブラリを使う
  2. SAX2を使う

SAXで処理を実装するのはダルイので、今回は1番の手段にしました。

採用したXMLライブラリは、libxml-rubyっていうlibxmlのRuby Bindingです。
libxmlは、C言語で実装されたXMLライブラリで、libxml-rubyは、それをRubyで呼び出せるようにしたものです。

rexmlはRubyコードのみで実装されたXMLライブラリでして、libxml-rubyより実行速度が遅いです3
きっと、メモリ使用効率もlibxml-rubyの方がいいだろうと期待して、採用しました。

rexmlはRuby1.8では標準で入ってますが、libxml-rubyは自分でインストールする必要があります。
インストールは、RubyGemsを使うと楽です。

私のCentOS 5環境下では以下の手順でインストールできました。

$ yum install libxml2-devel
$ yum install ruby-devel
$ gem install libxml-ruby

libxml-rubyを使って実装しなおしてみたところ、実行時の消費メモリは最大でも360MB程度になりました。
これなら、私のマシンで普通に実行できます。

XMLファイルサイズ、ライブラリ、メモリ消費量

DOM操作にしろXPathにしろ、DOM Treeを作るのに一番メモリを消費すると
思うんですが、実際、どの程度メモリを消費するのか気になったので、適当にググって見つけた
リンクを貼っときます。

  1. 途中で実行を中断したため、最大でメモリをどのくらい消費するかは不明 []
  2. Simple API for XMLの略 []
  3. http://libxml.rubyforge.org/参照 []

ブログで紹介した商品

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