NetBSD dtrace格闘記その2

(NetBSD6.0でdtrace環境を手っ取り早く整えたい方は、末尾のほうにある「まとめ」を読んでくださいな。)
前回の日記では
*NetBSD6.0ではdtraceが導入された。公式のWiki(http://wiki.netbsd.org/how_to_enable_and_run_dtrace/)に導入方法が書いてあった。
* しかし、modloadしようとするとsolarisモジュール以外のモジュールがいないといわれる。
* build.shでdistributionしてできたオブジェクト群の中にはモジュールが存在している。ということはインストールができていないだけ。
*build.shにはinstallmodulesというOperationが存在するが、そのときに指定するディレクトリがよくわからない。
*modloadのソースをシステムコール含め読んでみると、どうやらmodloadは/stand/i386/6.0/modulesディレクトリの下を見に行っていることがわかった。
*よって、installmodulesに指定するディレクトリは/stand/i386/6.0/modulesを指定すればいいんじゃね?という仮説が立った。
ということを書いた。

そこで、今日はbuild.shでinstallmodulesを試す。

./build.sh -a i386 -O ../obj -T ../tools installmodules=/stand/i386/6.0/modules

途中までうまくいくが、sys/modules/xldscriptsのところでエラーが発生する。

install ===> sys/modules/xldscripts
#   install  /stand/i386/6.0/modules/usr/libdata/ldscripts/kmodule
/usr/src/../tools/bin/i486--netbsdelf-install  -N /usr/src/etc -c  -r  -o root  -g wheel  -m 444   kmodule /stand/i386/6.0/modules/usr/libdata/ldscripts/kmodule
i486--netbsdelf-install: /stand/i386/6.0/modules/usr/libdata/ldscripts: mkstemp: No such file or directory

*** Failed target:  /stand/i386/6.0/modules/usr/libdata/ldscripts/kmodule
*** Failed command: /usr/src/../tools/bin/i486--netbsdelf-install -N /usr/src/etc -c -r -o root -g wheel -m 444 kmodule /stand/i386/6.0/modules/usr/libdata/ldscripts/kmodule
*** Error code 1

ログによると、/stand/i386/6.0/modules/usr/libdata/ldscripts/kmoduleが存在しないと言われているようだ。

けど、/stand/i386/6.0/modules/の下のさらに/usr/libdata/ldscripts/kmoduleを探しにいくのはおかしい。
他のモジュールのインストールログを見ると「/stand/i386/6.0/modules/stand/i386/6.0/modules」なんてディレクトリ構成になっている・・・。
どうやら指定したディレクトリに/stand/i386/6.0/modulesディレクトリを作成し、そこにモジュールをインストールしていると推定。

今回モジュールをインストールしたいのは/stand/i386/6.0/modulesなので、installmodulesで「/」を指定することにした。

./build.sh -a i386 -O ../obj -T ../tools installmodules=/

installmodulesは成功した。しかし、再度modloadを試みてもやはりsolaris以外のモジュールがロードできないし、/stand/i386/6.0/modulesの下に該当モジュールがいない。

よって、「インストールすべきモジュール群はどのようにして決まっているのか」を追うことにする。
まず、ソースファイルが置かれたルートディレクトリ(build.shがあるディレクトリ)にあるMakefileを見る。build.shがinstallmodules時に叩いているMakefileだ。

installmodulesターゲットでは、諸々のチェックをしたあと、以下の3行を実行する。

        ${MAKEDIRTARGET} sys/modules install DESTDIR=${INSTALLMODULESDIR:U/}
        @echo   "make ${.TARGET} started at:  ${START_TIME}"
        @printf "make ${.TARGET} finished at: " && date

${MAKEDIRTARGET}とは何でこれは何処にいるのか?
grepをかけて調べると、share/mk/bsd.own.mkにいることがわかる。

bsd.own.mkの説明書きによると

#
# MAKEDIRTARGET dir target [extra make(1) params]
#       run "cd $${dir} && ${MAKEDIRTARGETENV} ${MAKE} [params] $${target}", with a pretty message
#

とのことである。このことからcd sys/modulesしてmakeをしていることが伺える。
そこで、sys/modulesの下のMakefileを見る。すると以下の記述が存在する。

.if (${MKDTRACE} != "no")
SUBDIR+=        dtrace
.endif

# we need solaris for the dtrace and zfs modules
.if (${MKDTRACE} != "no" || ${MKZFS} != "no")
SUBDIR+=        solaris
.endif

これによるとdtraceはMKDTRACEというビルド変数が"no"以外でないとmakeの対象にしてもらえないらしい。
ここで、solarisモジュールはインストールできている。
その理由はshare/mk/bsd.own.mkの中で以下のように定義されているためである。

#
# We want to build zfs only for i386 and amd64 by default for now.
#
.if ${MACHINE} == "amd64" || ${MACHINE} == "i386"
MKZFS?=         yes
.endif

しかし、MKDTRACEはどこの時点でnoにされているのか。grepをかけてみた。
ヒットしたファイルの一つであるshare/mk/bsd.own.mkを確認すると、以下の記述があった。

#
# MK* options which default to "no".  Note that MKZFS has a different
# default for some platforms, see above.
#
_MKVARS.no= \
        MKBSDGREP MKBSDTAR \
        MKCATPAGES MKCRYPTO_RC5 MKDEBUG \
        MKDEBUGLIB MKDTRACE MKEXTSRC \
        MKMANZ MKOBJDIRS \
        MKLLVM MKPCC \
        MKPIGZGZIP \
        MKREPRO \
        MKSOFTFLOAT MKSTRIPIDENT \
        MKUNPRIVED MKUPDATE MKX11 MKZFS
.for var in ${_MKVARS.no}
${var}?=no
.endfor

ああ、列挙されているビルド変数を"no"にしているのね。ここまでのところで、installmodulesしてもモジュールがインストールできないのはビルド変数MKDTRACEが"no"だったからということがわかる。
ということで再度build.shをかけてみる。

./build.sh -a i386 -O ../obj -T ../tools -V MKDTRACE=yes installmodules=/

これまで失敗していた「modload dtrace」を実行してみると、今度は成功した。
「modload sdt」も「modload fbt」も同様に成功した。

公式ブログで述べられているデバイスファイル作成も成功。

mkdir /dev/dtrace 
mknod /dev/dtrace/dtrace c dtrace 0

プルーブの一覧を表示するために「dtrace -l」を実行したが、これが失敗する。(dtrace: Command not found.)
build.shでdistributionしてからinstallをしていなかった事に気づく。よって、以下のコマンドを実行する。

./build.sh -a i386 -O ../obj -T ../tools -V MKDTRACE=yes install=/

実行後、rehashし、再度dtraceコマンドを実行する。今度は無事にprobeの情報を出力することができた。

まとめ
NetBSD6.0でdtraceの環境を作るためには以下の手順が必要。
(ソースが/usr/src以下に、objディレクトリは/usr/objに、ツールチェインが/usr/toolsにあるという前提で記載する。)
(1)ツールチェインを作成する。

./build.sh -a i386 -O ../obj -T ../tools tools

(2)カーネルをビルドする。ビルドする際、カーネルコンフィグファイルに以下の3記述があることを確認する。(コメントアウトされていないか要確認。なければ追加)

options  INSECURE
options  KDTRACE_HOOKS
options  MODULAR

(3)distributionをビルドする。

./build.sh -a i386 -O ../obj -T ../tools -V MKMODULAR=yes -V MKDTRACE=yes distribution

(4)(3)でビルドしたブツをインストールする。

./build.sh -a i386 -O ../obj -T ../tools -V MKDTRACE=yes install=/

(5)(3)でビルドされたモジュールをインストールする。

./build.sh -a i386 -O ../obj -T ../tools -V MKDTRACE=yes installmodules=/

(6)モジュールをロードする。

modload solaris
modload dtrace
modload sdt
modload fbt

(7)デバイスファイルの作成を行う。

mkdir /dev/dtrace
mknod /dev/dtrace/dtrace c dtrace 0

これでdtrace -lしてプルーブの一覧が表示されれば成功です。
公式ブログでは端折っている箇所があり、苦労しましたが、いろいろと勉強になりました。