SystemTapでrubyのUser-Space Evnetsをフックする

SystemTaprubyのUser-Space Evnetsをフックしてみる。

環境は以下の通り。

systemtapのインストール

sudo apt-get install systemtap systemtap-runtime systemtap-sdt-dev

ruby 2.0.0p195のビルド

既にビルドしている場合はmake cleanしてからビルドし直す。

./configure
make
sudo make install

利用可能なUser-Space Eventsの確認

stap -L 'process("rubyのパス").provider("ruby").mark("*")'

出力例:

% stap -L 'process("/usr/local/bin/ruby").provider("ruby").mark("*")'
process("/usr/local/bin/ruby").provider("ruby").mark("array__create") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("cmethod__entry") $arg1:long $arg2:long $arg3:long $arg4:long
process("/usr/local/bin/ruby").provider("ruby").mark("cmethod__return") $arg1:long $arg2:long $arg3:long $arg4:long
process("/usr/local/bin/ruby").provider("ruby").mark("find__require__entry") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("find__require__return") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("gc__mark__begin")
process("/usr/local/bin/ruby").provider("ruby").mark("gc__mark__end")
process("/usr/local/bin/ruby").provider("ruby").mark("gc__sweep__begin")
process("/usr/local/bin/ruby").provider("ruby").mark("gc__sweep__end")
process("/usr/local/bin/ruby").provider("ruby").mark("hash__create") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("load__entry") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("load__return") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("method__entry") $arg1:long $arg2:long $arg3:long $arg4:long
process("/usr/local/bin/ruby").provider("ruby").mark("method__return") $arg1:long $arg2:long $arg3:long $arg4:long
process("/usr/local/bin/ruby").provider("ruby").mark("object__create") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("parse__begin") $arg1:long $arg2:long
process("/usr/local/bin/ruby").provider("ruby").mark("parse__end") $arg1:long $arg2:long
process("/usr/local/bin/ruby").provider("ruby").mark("raise") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("require__entry") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("require__return") $arg1:long $arg2:long $arg3:long
process("/usr/local/bin/ruby").provider("ruby").mark("string__create") $arg1:long $arg2:long $arg3:long

SystemTapスクリプトの作成

今回は試しに例外の発生をフックしてみる。
まず以下のようなSystemTapスクリプトを書く。

raise.stp:

probe process("/usr/local/bin/ruby").provider("ruby").mark("raise") {
  printf("%s %s %d\n", user_string($arg1), user_string($arg2), $arg3)
}

以下のコマンドで実行:

sudo stap <SystemTapスクリプト> -c <実行するコマンド>

出力例:

% sudo stap raise.stp -c "ruby --disable-gems -e 'raise rescue nil'"
RuntimeError -e 1

RuntimeErrorの発生をフックする事ができた。