dtrace を一般ユーザで使う2015年10月15日 11時57分23秒

久しぶりに dtrace に触ってみた。以前は root で試運転してみたが、やはり root での作業は好ましくない。しかし一般ユーザではそのままでは使えない。

何はともあれまずは dtrace を読み込む。

$ kldload dtraceall
これで root が使えるようになったが、まだ一般ユーザは使えない。
% dtrace -l
dtrace: failed to initialize dtrace: DTrace requires additional privileges

一度だけなら、chmod で誰でも読み書きできる権限にする。

$ chmod go+rw /dev/dtrace/*
これで、一般ユーザでも使える。
% dtrace -l | head
   ID   PROVIDER            MODULE                          FUNCTION NAME
    1     dtrace                                                     BEGIN
    2     dtrace                                                     END
    3     dtrace                                                     ERROR
    4        fbt            kernel                camstatusentrycomp entry
    5        fbt            kernel                camstatusentrycomp return
    6        fbt            kernel            cam_compat_handle_0x17 entry
    7        fbt            kernel            cam_compat_handle_0x17 return
    8        fbt            kernel            cam_compat_handle_0x18 entry
    9        fbt            kernel            cam_compat_handle_0x18 return

育英入学式2011年04月03日 12時00分52秒

息子の育英学園サタデースクールの入学式を済ませてきた。現在、四歳半になる。年中組での通園となる。その名の通り、土曜日だけの幼稚園になる。

他にも全日制と言われる平日五日間の園もある。こちらは年少組から受け付けている。土曜日だけのサタデースクールだと年中組からになる。

十一時までに登園。その後入学式を行い、二時半には帰宅になる。娘も連れての初登園。気楽に登園する。入園式も無事に終わらせた。式の後に、保護者の懇談会があったが娘と妻はお昼の時間になったので撤退。

家に帰って軽食をとったらすぐに二時半になった。十一匹のねことぶたを幼稚園から借りてきた。十一匹のねことあほうどりは息子の愛読書。ずいぶんと面白い選択をしてくるものだと思った。

dtrace の十例2009年11月21日 09時27分08秒

Top Ten DTrace (D) Scriptsにて、十個の dtrace スクリプトが公開されている。

出力例と共に出ているので、一見の価値あり。

Dtrace をかじってみて2009年04月06日 16時34分05秒

DTrace を Sun にあるサイトを中心に、試行錯誤したが思ったよりも敷居が高い。要点をまとめようと思ったが、予想していたよりも手間が掛かるのと、時間が取れなくて進んでいない。

しかし、真剣に使い始めようとしたら三つの問題に当たった。一つ目は、文法や内部関数に慣れないとなかなか使いこなせないこと。どの様な言語でもそれは当てはまるが、dtrace はその知識の蓄積が必要になる。gdb 等では、break、step。next、continue 等の三つ、四つコマンドがあれば随分と操作できるのと比べると、多きな差がある。

二つ目は、検査点がまだまだ少ないこと。FreeBSD ではまだまだ発展途上であり、カーネルにはそれなりの数の検査点が埋め込まれているが、ユーザランドから使えるのは少ない。その、カーネルに埋め込まれている検査点も偏りがあり、場所によってはほとんどないところもある。

三つ目は、プログラムがカーネルを呼び出した先の追跡が難しいこと。プロセス自体の呼び出し順を調べるのは比較的容易い。しかし、カーネルに動作が渡された後の追跡が至極難しい。例えば、read(2) された後の低次元の動作はカーネル内で行なわれる。そのため、プログラム名ではその動作が追えなくなってしまう。

DTrace を最初に試した時は柔軟性に惹かれたが、使いこなすにはそれなりの時間が必要そうだ。一朝一夕で出来るものでは無さそうだ。今後も時間の余裕がある時に、もっと取り上げていきたい。

D スクリプト言語の構文2009年03月14日 00時33分56秒

dtrace には専用の言語、D スクリプト言語がある。検査点の名前を表示するだけでなく何らかの処理を行ないたい場合は、この言語で書かれたファイルを用いる。プログラム言語としては珍しく awk 型の、パターン・アクションの構文を持つ。

D Program Structure に説明がある。以下に要点をまとめた。

D スクリプト言語は以下の形を取る。


検査点 [, 検査点]
/ 条件 /
{
    動作;
}

カンマで区切ることにより複数の検査点を羅列できる。

また、検査点にはシェルと同じメタ文字が使える。

*
空文字を含めて、全ての文字列に適合する。
?
任意の一文字に適合する。
[ ... ]
括弧の中に羅列された文字列の一つに適合する。二つの文字をハイホン「-」で繋ぐと、その範囲の文字が適合する。最初の文字がエクスクラメーションマーク「!」の場合は、羅列されていない文字に適合するようになる。
\
次に続く文字の特殊効果を無効にする。エスケープ文字。

条件はスラッシュ「/」に挟んで記述する。これらは検査点に処理が移って来た時に、真に評価された時に動作部分の実行される。この条件は省略可能である。D スクリプト言語では、0 が偽に、0 以外が真と評価される。

動作を括弧「{}」内に記述する。動作にはセミコロン「;」で区切られた複数の動作を並べることが出来る。空の動作を用いて、検査点を兎得りすぎたことを調べることも出来る。 -C オプションを用いることで、D スクリプト言語でも、C 言語の cpp プリプロセッサを呼ぶことが出来る。

dtrace 実行環境は実行時のエラー処理を行なうので、ゼロ除算や不正なメモリへのアクセスが起こると報告される。その為、dtrace がカーネルやプロセスを破壊するようなことは起こらない。

DTrace では trace/printf/printa にて出力2009年03月04日 00時56分10秒

D スクリプト言語には三つの出力関数が用意されている。trace 関数で書式を指定しないで、printf 関数で書式を指定して、データを出力できる。printa 関数は連想配列を出力するのに使う。

trace 関数は、dtrace の D スクリプト言語で結果を出力するときに使う一番簡単な関数。出力の形式は、データ型と内容によって決定される。1、2、4、8 バイトであれば数字として出力。もしそれ以外のバイト数であり、表示可能な文字であった場合は、ASCII 文字列として表示される。それ以外であれば 16 進数の羅列として表示される。

printf 関数は、C 言語のような書式を指定して、データを出力できる。printf は D スクリプト言語に組み込まれた関数であり、C 言語には無い機能を提供する。以下に幾つかの特徴的な違いを並べる。

  1. D スクリプト言語コンパイラは printf の書式とデータ型を比べて、問題があると警告する。最近の C 言語のコンパイラは、同じことをする物が多いが、これは必須ではない。
  2. C 言語では、データの大きさを示すために、l や h 等を使う。C 言語では、数値であっても short と long でバイト数が違うのを関数に教えなければならない。しかし、D スクリプト言語ではコンパイラがこれを正しく扱う為、むしろこれらの利用は非推奨になっている。
  3. %a によるポインタの出力にはシンボルとオフセットが表示されたり、%Y にて、ctime 形式の日付が出力できたりとデバッグに便利な書式が追加されている。

printa 関数は Aggregation 型のデータを表示するのに使われる。Aggregation 型はいわゆる連想配列だ。Aggregation/連想配列は統計をとる時などに有用だ。

Output Formatting にて、更に細かい解説がされている。

dtrace の stack() でスタックトレースを見る2009年02月28日 00時45分21秒

コードを追っていると目的の動作が何処で起きるのかは比較的わかりやすい。目的の関数はエラー出力や動作から簡単に掴める。しかし、その呼び出し条件を探るとなると一気に難しくなる。

Java 等では、printStackTrace() などの関数があったり、スクリプト言語でも、スタックトレースを出してくれる言語もある。しかし、C 言語となると一筋縄ではいかない。確かにデバッガを使えば判る時もあるが、プログラムの実行が大きく中断されるので、タイマーなどの動作が変わってしまう事がある。

dtrace が提供する言語、D スクリプト言語には stack 関数が用意されている。


void stack(int nframes)
void stack(void)

例として、swap-in をあげてみよう。名前の通り、関数を見つける事自体は簡単だ。しかし、どのような時に呼ばれるのかを、コードを査読するだけで掴むのは大変だ。そこで、dtrace を使う。


$ dtrace -n 'fbt::swap_pager_getpages:entry{stack()}'
dtrace: description 'fbt::swap_pager_getpages:entry' matched 1 probe

検査点に入ったときにスタックトレースを出力する。

業とスワップを起こす。そのために幾つかの方法を試してみた。


$ dd if=/dev/zero of=/mnt/tmpfs/zero bs=10M count=120
$ cat /mnt/tmpfs/zero > /dev/null
$ rm /mnt/tmpfs/zero
$ tar xvf src.tbz2 -C /mnt/tmpfs
$ cd /mnt/tmpfs/src
$ find . -type d | sed 's#^#../copy/#' | xargs mkdir -p
$ sh -c 'for f in `find . -type f`; do cp $f ../copy/$f; done'

上のコマンドを行なっている時に、出てきた出力は以下の様な結果だった。実際には、swap_pager_getpages が呼ばれる度に出力されるので、実際にはもっと大量の出力が出る。


  1  14739        swap_pager_getpages:entry 
              kernel`vm_thread_swapin+0xcc
              kernel`faultin+0xbf
              kernel`scheduler+0x412
              kernel`mi_startup+0x96
              kernel`0xc045e865

  1  14739        swap_pager_getpages:entry 
              kernel`vm_fault+0x1030
              kernel`trap_pfault+0x15b
              kernel`trap+0x27a
              kernel`0xc0a9e37b

  1  14739        swap_pager_getpages:entry
              kernel`mdstart_swap+0x3e7
              kernel`md_kthread+0x21c
              kernel`fork_exit+0x99
              kernel`0xc0a9e420

  1  14739        swap_pager_getpages:entry 
              kernel`vm_fault+0x1030
              kernel`trap_pfault+0x15b
              kernel`trap+0x417
              kernel`0xc0a9e37b
              kernel`kern_lstat+0x4f
              kernel`lstat+0x2f
              kernel`syscall+0x366
              kernel`0xc0a9e410

wap_pager_getpages:entry
              0xc4d19f54
              kernel`VOP_READ_APV+0x42
              kernel`vn_read+0x2d4
              kernel`dofileread+0x96
              kernel`kern_readv+0x58
              kernel`read+0x4f
              kernel`syscall+0x366
              kernel`0xc0a9e410

  1  14739        swap_pager_getpages:entry 
              kernel`vm_fault+0x1030
              kernel`vm_fault_wire+0x4a
              kernel`vm_map_wire+0x256
              kernel`vslock+0x1d5
              kernel`sysctl_wire_old_buffer+0x5e
              kernel`sysctl_kern_proc+0x178
              kernel`sysctl_root+0x127
              kernel`userland_sysctl+0x134
              kernel`__sysctl+0xde
              kernel`syscall+0x366
              kernel`0xc0a9e410

色々と出てきた。

DTrace で検査点を有効にする2009年02月20日 12時10分51秒

dtrace はプロバイダ名、モジュール名、関数名、プローブ名の四つの要素から検査点を絞り込む。一番利用頻度が高いのは、dtrace -n provider:module:function:name の形だろう。

これらの四つの要素は、dtrace -l で調べられる。


$ kldload dtraceall
$ dtrace -l
   ID   PROVIDER            MODULE                          FUNCTION NAME
    1     dtrace                                                     BEGIN
    2     dtrace                                                     END
    3     dtrace                                                     ERROR
    4   dtmalloc                                                 fbt malloc
    5   dtmalloc                                                 fbt free
    6   dtmalloc                                              cyclic malloc
    7   dtmalloc                                              cyclic free
    8   dtmalloc                                             solaris malloc
...
34294        fbt             tmpfs                    tmpfs_pathconf entry
34295        fbt             tmpfs                    tmpfs_pathconf return
34296        fbt             tmpfs                     tmpfs_symlink entry
34297        fbt             tmpfs                     tmpfs_symlink return
34298        fbt             tmpfs                       tmpfs_mkdir entry
34299        fbt             tmpfs                       tmpfs_mkdir return

これらの中から追跡したい動作を探しだし、有効にする。

基本的な検査点の指定方法は、名前を使う。検査点の名前のみ指定する場合は、entry を指定しても、return を指定しても大差はない。


$ dtrace -n fbt:tmpfs:tmpfs_mkdir:entry
dtrace: description 'fbt:tmpfs:tmpfs_mkdir:entry' matched 1 probe

これで、fbt プロバイダの中の tmpfs モジュールの中の tmpfs_mkdir 関数に入る時に、dtrace が出力を出す。

これだけでも充分に使い物になる場合が多い。例えば、ソースコードを眺めて大体の動作は掴めた後に、実際に呼ばれるのを確かめるのに使える。


$ dtrace -n fbt:tmpfs:tmpfs_mkdir:entry
dtrace: description 'fbt:tmpfs:tmpfs_mkdir:entry' matched 1 probe
^Z
Suspended
$ bg
[1]    dtrace -n fbt:tmpfs:tmpfs_mkdir:entry &
$ mkdir /mnt/ufs/dir
$ mkdir /mnt/tmpfs/dir
$ CPU     ID                    FUNCTION:NAME
  0  34298                tmpfs_mkdir:entry 
$ rmdir /usr/tmp/dir
$ fg
dtrace -n fbt:tmpfs:tmpfs_mkdir:entry
^C


いつ出力が起きているかを具体的に示すために、dtrace を後ろに追いやった。Ctl-C で dtrace を終了する。

一度に二つの検査点を指定することも出来る。


$ dtrace -n fbt:tmpfs:tmpfs_mkdir:entry,tmpfs_create:entry
dtrace: description 'fbt:tmpfs:tmpfs_mkdir:entry,tmpfs_create:entry' matched 2 p
robes

4 つの要素のうち、前半は省略可能だ。例えば、::tmpfs_create:entry と指定すると、tmpfs_create:entry と指定したのと同じになる。tmpfs 関数なので他のプロバイダやモジュールと名前が衝突する事はないが、open などだと、複数の検査点が出来る。

これと同じような事をしようとすると、printf を入れたりデバッガを使ったり出来る。しかし、printf は簡単だが、再コンパイルとインストールを何回も繰り返す事になる。デバッガでも strip されているとライブラリやらバイナリやらを作り直す必要がある事も多く、またカーネルデバッガは色々と敷居が高くなる。dtrace -n 名前 を使うだけでこれらの手間から開放され、コードを追いかける効率が格段と上がった。

DTrace を使った感想2009年02月17日 23時50分32秒

dtrace を設定し、実験を始めた。数日後に何故だか動かなくなり焦った。CURRENT を準備している時に間違って 7.1-RELEASE を触ったらしく、再構築し直したら dtrace が再度動くようになった。

三十分から一時間ぐらい使ってみたが、とても良い。dtrace 以前は、カーネルを読んで、少し触ってみる時には、コードを読んで動作を把握した後に、printf などを入れて自分の解釈が正しいかを調べたりした。カーネルモジュールが使える場合でも、再構築やモジュールの再読み込みなどで面倒だったのだが、dtrace を使うとそれらに費やす時間が必要無くなる。


$ dtrace -l | wc -l
  35405

dtrace に三万五千にものぼる検査点が埋め込まれていて、全ての関数ではないが必要充分なだけは網羅されている。カーネルの動作を追うだけなら、格段と楽に出来るようになった。

病み付きになる便利さだ。今後、何回かに分けてまとめたいと思う。

Running DTrace on FreeBSD 7.1-RELEASE2009年02月15日 00時00分28秒

Before using dtrace(1) on FreeBSD, kernel has to be recompiled with appropriate options to enable dtrace.

options KDTRACE_HOOKS        # all architectures
options KDTRACE_FRAME        # amd64-only

Then, kernel MUST be compiled with WITH_CTF=1. It was said that this option cannot added to /etc/make.conf.


$ kldload dtraceall
This module (opensolaris) contains code covered by the
Common Development and Distribution License (CDDL)
see http://opensolaris.org/os/licensing/opensolaris_license/
$ dtrace -l | head -10
   ID   PROVIDER            MODULE                          FUNCTION NAME
    1     dtrace                                                     BEGIN
    2     dtrace                                                     END
    3     dtrace                                                     ERROR
    4   dtmalloc                                                 fbt malloc
    5   dtmalloc                                                 fbt free
    6   dtmalloc                                              cyclic malloc
    7   dtmalloc                                              cyclic free
    8   dtmalloc                                             solaris malloc
    9   dtmalloc                                             solaris free

Run kldload dtracell and dtrace -l to verify that it is probing.

If you encounter the following error, it is likely that you forget to compile kernel with WITH_CTF=1.


$ dtrace -n 'syscall::open:entry'
dtrace: invalid probe specifier syscall::open:entry: "/usr/lib/dtrace/psinfo.d",
 line 37: syntax error near "uid_t"

Enjoy hacking!