BSD界隈四方山話

第86回DTraceの使い方 その6

ファイルシステム

次のような感じでdtrace(8)コマンドを実行すると、コマンドがどのファイルを開いたかを調べられるといったことは以前説明しました。コマンドやデーモンが使っている設定ファイル、使っているデータ、書き込んでいるファイル、影響を与えているファイルなんかを調べたいときにこの方法を使うと便利です。

 open(2)システムコールをトレース
# dtrace -n 'syscall::open:entry{printf("%s %s",execname,copyinstr(arg0))}'
dtrace: description 'syscall::open:entry' matched 2 probes
CPU     ID                    FUNCTION:NAME
  1  64044                       open:entry pkg /etc/pkg/
  1  64044                       open:entry pkg /usr/local/etc/pkg/repos/
  1  64044                       open:entry pkg /tmp/meta.txz.WJDegY
  1  64044                       open:entry pkg /etc/resolv.conf
  2  64044                       open:entry pkg /etc/nsswitch.conf
  2  64044                       open:entry pkg /etc/hosts
  2  64044                       open:entry pkg /etc/services
  2  64044                       open:entry pkg /usr/share/zoneinfo/UTC
  2  64044                       open:entry pkg /usr/share/zoneinfo/posixrules
  2  64044                       open:entry pkg /etc/localtime
  2  64044                       open:entry pkg /usr/share/zoneinfo/posixrules
  2  64044                       open:entry pkg /tmp/packagesite.yaml.txz.z61Zy6
  2  64044                       open:entry pkg /etc/hosts
  2  64044                       open:entry pkg /etc/services
  3  64044                       open:entry cron /etc/spwd.db
  3  64044                       open:entry cron /etc/login.conf.db
  3  64044                       open:entry cron /etc/group
  3  64044                       open:entry cron /root/.login_conf.db
  3  64044                       open:entry cron /root/.login_conf
  3  64044                       open:entry cron /etc/login.conf.db
  0  64044                       open:entry cron /etc/spwd.db
  0  64044                       open:entry cron /etc/login.conf.db
  0  64044                       open:entry cron /etc/group
  0  64044                       open:entry cron /root/.login_conf.db
  0  64044                       open:entry cron /root/.login_conf
  0  64044                       open:entry cron /etc/login.conf.db
  0  64044                       open:entry sh /etc/libmap.conf
  0  64044                       open:entry sh /var/run/ld-elf.so.hints
  0  64044                       open:entry sh /lib/libedit.so.7
  0  64044                       open:entry sh /lib/libncurses.so.8
  0  64044                       open:entry sh /lib/libc.so.7
  3  64044                       open:entry atrun .
  0  64044                       open:entry atrun /etc/libmap.conf
  0  64044                       open:entry atrun /var/run/ld-elf.so.hints
  0  64044                       open:entry atrun /usr/lib/libpam.so.5
  0  64044                       open:entry atrun /lib/libutil.so.9
  0  64044                       open:entry atrun /lib/libc.so.7
  0  64044                       open:entry atrun .

上記サンプルですと、4列目がコマンド名、5列目がオープンしたファイルのリストになっています。このコマンドを実行してから調べたいコマンドやデーモンを実行すると、どんなファイルが使われているのかを調べることができます。

dtrace(8)を使うと、ほかのプローブを指定してそうした情報を得ることもできます。次のDスクリプトを実行するとvfs::vop_create:entryとvfs::vop_remove:entryをモニタリングしてどのファイルが作成されたとか、削除されたとかを調べることができます。

リスト ほかのプローブを指定してもうちょっと細かくみてみる(vfslite.d)
#!/usr/sbin/dtrace -s

#pragma D option quiet
#pragma D option switchrate=10hz

dtrace:::BEGIN
{
    printf("%-12s %6s %6s %-12.12s %-12s %s\n", "TIME(ms)",
        "UID", "PID", "PROCESS", "CALL", "DIR/FILE");
}

vfs::vop_create:entry,
vfs::vop_remove:entry
{
    this->dir = args[0]->v_cache_dd != NULL ?
        stringof(args[0]->v_cache_dd->nc_name) : "<null>";
    this->name = args[1]->a_cnp->cn_nameptr != NULL ?
        stringof(args[1]->a_cnp->cn_nameptr) : "<null>";

    printf("%-12d %6d %6d %-12.12s %-12s %s/%s\n",
        timestamp / 1000000, uid, pid, execname, probefunc,
        this->dir, this->name);
}

実行するとこんな感じになります。vop_*のところでモニタリングしてデータを得ていることがわかります。中身がわかっていないとそもそもこういった使い方はできませんけれども、こういったことができるということは知っておいて損はないと思います。

 実行結果
# ./vfslite.d
TIME(ms)        UID    PID PROCESS      CALL         DIR/FILE
2201282829        0  53958 pkg          vop_create   tmp/meta.txz.Oobq5r
2201282829        0  53958 pkg          vop_remove   tmp/meta.txz.Oobq5r
2201283089        0  53958 pkg          vop_create   tmp/packagesite.yaml.txz.LtYHfS
2201283089        0  53958 pkg          vop_remove   tmp/packagesite.yaml.txz.LtYHfS
2201284582        0  53960 pkg          vop_create   tmp/meta.txz.ImITVo
2201284582        0  53960 pkg          vop_remove   tmp/meta.txz.ImITVo
2201284820        0  53960 pkg          vop_create   tmp/packagesite.yaml.txz.Fmr1Cn
2201284820        0  53960 pkg          vop_remove   tmp/packagesite.yaml.txz.Fmr1Cn
2201285058        0  53960 pkg          vop_create   pkg/local.sqlite-journal
2201285060        0  53960 pkg          vop_remove   pkg/local.sqlite-journal
2201285060        0  53960 pkg          vop_create   pkg/local.sqlite-journal
2201285061        0  53960 pkg          vop_remove   pkg/local.sqlite-journal
2201285097        0  53960 pkg          vop_create   pkg/local.sqlite-journal
2201285098        0  53960 pkg          vop_remove   pkg/local.sqlite-journal
2201285098        0  53960 pkg          vop_create   pkg/local.sqlite-journal
2201285099        0  53960 pkg          vop_remove   pkg/local.sqlite-journal

簡単なコマンドですが、次のようにchdir(2)システムコールでデータを取得すると、いわゆるcdコマンドでディレクトリを移動したときをフックして移動先のディレクトリの一覧を得ていくことができます。このコマンドであればcdコマンドでカレントディレクトリを変更するごとにトレース結果が出力されるので、動作がわかりやすいと思います。

 cdコマンド(chdir(2)システムコール)のディレクトリを追っていく
# dtrace -n 'syscall::chdir:entry{printf("%s",copyinstr(arg0))}'
dtrace: description 'syscall::chdir:entry' matched 2 probes
CPU     ID                    FUNCTION:NAME
  3  64058                      chdir:entry /root
  3  64058                      chdir:entry /usr/local
  3  64058                      chdir:entry /usr/local/share
  2  64058                      chdir:entry /usr/share
  2  64058                      chdir:entry /usr/share/doc
  3  64058                      chdir:entry /usr/share/doc/llvm
  0  64058                      chdir:entry /usr/share/doc/llvm/clang
  0  64058                      chdir:entry /etc
  0  64058                      chdir:entry /usr/local/etc
  0  64058                      chdir:entry /usr/local/etc/rc.d
  0  64058                      chdir:entry /root
  1  64058                      chdir:entry /home
  1  64058                      chdir:entry /home/suzuki

情報を表示する*stat系のコマンドだと決まった情報しか取得できませんが、dtrace(8)を使うと欲しい情報を欲しい形に加工して得ることができますので、使えるようになればなるほど便利です。書籍『DTrace Dynamic Tracing In Oracle Solaris, Mac OS X & FreeBSD』にはいろんなサンプルと説明が載っていますので(そのままだと実行できないサンプルも多いのですが⁠⁠、説明を読みながらいろいろ試してもらえればと思います。

ここに掲載したサンプルスクリプトは『DTrace Dynamic Tracing In Oracle Solaris, Mac OS X & FreeBSD』by Brendan Gregg and Jim Mauro(P.304、P.306、P.345)より抜粋したものです(FreeBSDで実行できるようにコマンドの一部は書き換えてあります。実行結果もFreeBSDで実行した結果を掲載しています⁠⁠。

勉強会

第60回 2月23日(木)19:00~FreeBSD勉強会

発表内容検討中。発表ネタをお持ちの方、ぜひご連絡ください。

参加申請はこちらから。

第61回 3月23日(木)19:00~FreeBSD勉強会:リキャップ・ザ・AsiaBSDCon 2017 ~日本語でふりかえるABC~

2017年3月9~12日まで東京でAsiaBSDCon 2017が開催される予定です。ぜひこのカンファレンスにご参加いただきたいわけなのですが、なかにはどうしても仕事の都合で参加できなかったとか、正直英語がよくわからなかったとか、そういった方もいらっしゃるのではないかと思います。

3月のFreeBSD勉強会では、AsiaBSDCon 2017のあとというこのタイミングを活かして、AsiaBSDCon 2017の発表内容を振り返ってみよう、というのをやってみようと思います。AsiaBSDConに参加しているにもかかわらず、これまで一度もプロシーディングを読み返したことすらないというあなた、ぜひプロシーディングを持参してご参加ください :) AsiaBSDConに参加できなかったというあなたも、この機会をお見逃しなく(できればAsiaBSDConそのものに参加した方が絶対的によいです、あしからず⁠⁠。

FreeBSD勉強会 発表者募集

FreeBSD勉強会では発表者を募集しています。FreeBSDに関して発表を行いたい場合、@daichigoto までメッセージをお願いします。1時間半~2時間ほどの発表資料を作成していただき発表をお願いできればと思います。

おすすめ記事

記事・ニュース一覧