2013/11/27

Erlang/OTP の systemtap トレース機能の使い方

はじめに

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.systemtap                    
Erlang Shell で適当に > "aaaaaaaaaa". などとコマンドを入力すると、stap スクリプトで下記のような出力が得られます。
GC major start pid <0 .33.0=""> need 9 words

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'

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

2013/11/22

OSS プロジェクト間の関連性を可視化してみました

はじめに


以前、OSS の開発活動を可視化する WEB サービス を作っていた時、 OSS プロジェクト相互の開発者の乗り入れ状況が気になりました。例えば 仮想マシンを管理するライブラリ libvirt と仮想マシンエミュレータ qemu では相当の共通開発者がいることが予想されます。また qemu と Linux Kernel も関係が深いはずです。
これらの関係を可視化することで、OSS プロジェクト同士の関連性を読み取ってみましょう。関連性を探ることで、OSS 開発者のプロジェクト間の移動や、潮流まで理解できるかもしれません。

デモ

と、いうわけで、Chord Diagram を用いて OSS プロジェクト間の関連性を可視化してみました。対象としたOSSプロジェクトは、自分の趣味で選んだ以下のものです。
"couchdb  libvirt  mongo    node  ocaml  perl     postgresql  redis  swift      virt-manager
cpython  linux    neutron  nova  otp    php-src  qemu        riak   systemtap"

下がその画像になります。

画像だけではいまいちわかりにくいので、インタラクティブなデモを用意しました。
こちらです : OSS Relationship Visualization
コードは GitHub で公開しています : Etsukata/ossrel
カーソルを各プロジェクトの弧に乗せると、そのプロジェクトと他プロジェクトの関連性のみが表示されます。
観察してみると、以下のことが読み取れました。

・仮想化(qemu, libvirt, virt-manager, OpenStack) は互いに関連性が強い
・Linux Kernel と qemu は関連性が強い
・OpenStack(nova, swift, neutron) 同士は関連性が強い
・Erlang/OTP と riak, couchdb は関連性が強い
・データベース(mongo, redis, riak, ...) と 仮想化(qemu, libvirt, ...) は関連性が薄い
・Ocaml とその他のOSS は関連性が薄い

などなど。
あらかた予想通りであることがわかりましたが、予想していなかったことがありました。

・ほとんどのプロジェクト同士は、関連性がある

複数のプロジェクトに貢献するのは、幅広い興味と知識が要求されるため、難しいことです。しかし、世の中にはそれが出来る人が多数いるんだなぁ、と感心させられました。

実装


実装について、簡単に記載します。
処理の流れは以下のようになっています。
1. 対象となるOSSの git commit logから author name を抜き出す
2. OSS プロジェクト同士(A, B)の関連性を以下の式で計算する
関連性 = sqrt(sum_{共通開発者} min(プロジェクトAへのコミット数, プロジェクトBへのコミット数))
平方根をとっているのは、Chord Diagram の見た目を整えるためです。
3. 関連性から隣接行列を生成する
4. 隣接行列を D3js に渡し、Chord Diagram で表示する。


ここでは、自分が選択したプロジェクト間のみ対象に可視化しましたが、可視化対象は自由に選択できますossrel を clone し、repos ディレクトリに対象となる git レポジトリを置き、run.sh を走らせるだけで、可視化できます。やり方は README.md に記載しました。
関連性が気になる OSS があったら、ぜひ可視化してみてください。