Erlang/OTP には systemtap と連携したトレース機能が備わっています。Erlang/OTP で systemtap 連携を有効にすると、systemtap から Erlang のトレースポイントを扱うことができます。トレースポイントを使うことで、BEAM 仮想マシンの挙動をより正確に把握したり、性能解析しやすくなります。
使い方
Erlang/OTP の ビルド
systemtap 連携を使うには、Erlang/OTP を configure --with-dynamic-trace=systemtap でビルドする必要があります。
# git clone git://github.com/erlang/otp.git # cd otp # ./otp_build autoconf # ./configure --with-dynamic-trace=systemtap # make
Erlang Shell を起動して [systemtap] と表示されていれば、systemtap が有効になっていることが確認できます。
# ./bin/erl Erlang R16B03 (erts-5.10.4) [source-fb0006c] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [systemtap] Eshell V5.10.4 (abort with ^G)
stap スクリプト起動方法
まず、stap -L コマンドで トレースポイントを確認しましょう。予め、beam バイナリファイルへの PATH を通しておいてください。
# PATH=/home/eiichi/git/otp/bin/x86_64-unknown-linux-gnu/:$PATH stap -L 'process("beam.smp").mark("*")' process("beam.smp").mark("aio_pool__add") $arg1:long $arg2:long process("beam.smp").mark("aio_pool__get") $arg1:long $arg2:long process("beam.smp").mark("bif__entry") $arg1:long $arg2:long process("beam.smp").mark("bif__return") $arg1:long $arg2:long process("beam.smp").mark("copy__object") $arg1:long $arg2:long process("beam.smp").mark("copy__struct") $arg1:long process("beam.smp").mark("dist__monitor") $arg1:long $arg2:long $arg3:long $arg4:long $arg5:long process("beam.smp").mark("dist__output") $arg1:long $arg2:long $arg3:long $arg4:long process("beam.smp").mark("dist__outputv") $arg1:long $arg2:long $arg3:long $arg4:long process("beam.smp").mark("dist__port_busy") $arg1:long $arg2:long $arg3:long $arg4:long ...
約60個のトレースポイント(user-probe 系を除く)が確認できます。Linux Kernel のトレースポイントは 約 1200個、Qemu のトレースポイントが 約 900個なのと比較すると、若干少ないですね。
これらトレースポイントのうち、gc_major_start を例にトレースポイントの使い方を説明します。
まず、下記のような stap スクリプトを用意しましょう。(otp/lib/runtime_tools/examples より抜粋)
# cat garbage-collection.systemtap probe process("beam.smp").mark("gc_major-start") { printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2); }
systemtap を有効にした Erlang Shell を起動し、下記のコマンドでsystemtap スクリプトを起動します。
# PATH=/home/eiichi/git/otp/bin/x86_64-unknown-linux-gnu/:$PATH stap garbage-collection.systemtapErlang Shell で適当に > "aaaaaaaaaa". などとコマンドを入力すると、stap スクリプトで下記のような出力が得られます。
GC major start pid <0 .33.0=""> need 9 words 0>
GCの他にも、Message の送受信、プロセスの spawn、スケジュールなど、興味深く有用なトレースポイントがありあますので、お試しください。
systemtap スクリプトの例は lib/runtime_tools/example にあります。
dyntrace(user-probe)
Erlang/OTP で systemtap 連携を有効にすると、dyntrace モジュールを使って、 Erlang コードから動的にsystemtap スクリプトに情報を出力することができます。
Erlang/OTP に同伴されている、lib/runtime_tools/example/user-probe.systemtap を例にとって dyntrace モジュールの使いかたを説明します。
まず、user-probe.systemtap は beam 向けになっていますので、beam.smp 向けに直しましょう。user-probe.systemtap を開き、process('beam') となっているところを process('beam.smp') に書き換えます。
# vim user-probe.systemtap
書き換えたら、Erlang Shell を起動し、user-probe.systemtap を起動します。(beam に PATHを通しておいてください。)
# stap user-proeb.systemtap
この状態で、Erlang Shell にて、以下のように入力します。
2> dyntrace:p(1, 2, 3, 4, "a", "b", "c"). true 3> dyntrace:put_tag("test"). undefined 4> dyntrace:p(1, 2, 3, 4, "a", "b", "c"). true
すると、stap スクリプトでは、以下のような出力が得られます。
<0 .33.0=""> 1 2 3 4 'a' 'b' 'c' 'c' <0 .33.0=""> test 1 2 3 4 'a' 'b' 'c' 'c' 0>0>
dyntrace:p/nで各種情報を出力します。dyntrace:put_tag/1 でトレース出力のプレフィックスを設定することができます。
詳しくはErlang User's Guide: dyntrace をご覧ください。
参考文献
Dtrace and Erlang: a new beginning
Erlang User's Guide: Systemtap and Erlang/OTP
Erlang User's Guide: dyntrace
Systemtap and Erlang: a tutorial
runtime_tools/src/dyntrace.erl