dtrace機能を有効にしたNetBSDインストールCDを作成してみた

dtrace機能を有効にしたNetBSDインストールCDを作成してみた

NetBSDのdtrace機能を試してみようと思い立ち、@akachochinさんのNetBSD dtrace格闘記その2を元にdtrace機能を有効にしたインストールCDの作成手順をまとめてみました。

インストールCD作成準備

@akachochinさんのblogでは、インストール済みのNetBSD環境に直接dtraceまわりのカーネルモジュールをインストールしています。私の環境ではdtraceを有効にしたNetBSDのインストールCDを作成したいため、まずはインストールCD作成用のNetBSD環境を準備します。

ソースコードを展開する

NetBSD-6.1.3-i386仮想マシンにインストールした後、ソースコードを展開します。

# mount /dev/cd0a /cdrom
# cd /cdrom/source/sets/
# ls -l *.tgz
-r--r--r--  1 root  wheel   43069713 Jan 19 20:15 gnusrc.tgz
-r--r--r--  1 root  wheel    7661899 Jan 19 20:16 sharesrc.tgz
-r--r--r--  1 root  wheel  203542266 Jan 19 20:14 src.tgz
-r--r--r--  1 root  wheel   40189162 Jan 19 20:15 syssrc.tgz
-r--r--r--  1 root  wheel  158393066 Jan 19 20:18 xsrc.tgz

ソースコードは/usr/src以下に展開します。

# for i in *.tgz; do tar zxvf $i -C /; done

カーネルコンフィグを修正してdtrace機能を有効化する

dtrace機能を有効にするためのカーネルコンフィグを追加します。INSECURE,KDTRACE_HOOKS,MODULARを有効にする必要がありますが、GENERICコンフィグではINSECURE,MODULARは既に有効になっていたため、KDTRACE_HOOKSのみ追加します。

# cd /usr/src/sys/
# diff -u arch/i386/conf/GENERIC.orig arch/i386/conf/GENERIC
--- arch/i386/conf/GENERIC.orig 2012-08-16 00:33:00.000000000 +0900
+++ arch/i386/conf/GENERIC      2014-04-13 02:23:38.000000000 +0900
@@ -74,6 +74,7 @@
 # Standard system options

 options        INSECURE        # disable kernel security levels - X needs this
+options                KDTRACE_HOOKS

 options        RTC_OFFSET=0    # hardware clock is this many mins. west of GMT
 options        NTP             # NTP phase/frequency locked loop

build.shを実行する

build.shを使用して一通りのビルドを行います。ビルドは時間がかかるので、以下のスクリプトを作成し、バッチ処理的にビルドを行うようにしました。

上記のスクリプトMac mini(2.4GHz Intel Core 2 Duo,メモリ 8GB)のVirtualBox上のNetBSDで走らせたところ、全てのビルドが完了するまで15時間程度かかりました。

ビルドが完了すると、以下の場所にdtrace機能が有効になったインストールCDが生成されます。

/usr/obj/distrib/i386/cdroms/installcd/NetBSD-6.1.3-i386.iso 

OSインストール後のdtrace機能を利用するための設定投入

インストールCDが作成できたので、このCDを使用してNetBSDを再度インストールします。インストール後、dtrace用にデバイスファイルを作成します(OSインストール後に一度行うだけで良い)。

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

そして、OS起動時にdtraceに必要なカーネルモジュールをロードするよう設定します。もっとスマートな方法があると思うのですが、今回は/etc/rc.localに以下の処理を追加しました。

_MODULE="
solaris
dtrace
sdt
fbt
"
for i in ${_MODULE}
do
    echo "modload ${i}"
    modload ${i}
done

OSを再起動するとdtrace機能が利用可能になっています。

# dtrace -l | wc -l
  45326

dtrace機能を試してみる

NetBSDでdtraceが利用できるようになったので、例としてgetpid()システムコールをフックさせてみます。

"getpid"というキーワードにマッチする関数は、sys_getpid(),sys_getpid_with_ppid()の2つがあるようです。

# dtrace -l | grep getpid
21001        fbt            netbsd                        sys_getpid entry
21002        fbt            netbsd                        sys_getpid return
21003        fbt            netbsd              sys_getpid_with_ppid entry
21004        fbt            netbsd              sys_getpid_with_ppid return

どちらがgetpid()システムコールの実体か分からないので、"getpid"でマッチするようにしてみます。dtrace -nを実行すると、その端末内で出力待ちの状態になります。

# dtrace -n fbt:netbsd:*getpid*:entry
dtrace: description 'fbt:netbsd:*getpid*:entry' matched 2 probes

別の端末にて、getpid()を実行するサンプルプログラムを用意します。

# cat _getpid.c
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
        printf("%d\n", getpid());
        return 0;
}
# gcc -Wall -Werror -g -o _getpid _getpid.c

サンプルプログラムを実行すると、dtraceを実行している端末に以下が出力されます。

# dtrace -n fbt:netbsd:*getpid*:entry
dtrace: description 'fbt:netbsd:*getpid*:entry' matched 2 probes
CPU     ID                    FUNCTION:NAME
  0  21003       sys_getpid_with_ppid:entry
  0  21003       sys_getpid_with_ppid:entry

getpid()の実体はsys_getpid_with_ppid()のようです。プロセスの生成時にもsys_getpid_with_ppid()は呼ばれるようで、出力が2回になっています。

# cat -n /usr/src/sys/kern/kern_prot.c
    81  /* ARGSUSED */
    82  int
    83  sys_getpid_with_ppid(struct lwp *l, const void *v, register_t *retval)
    84  {
    85          struct proc *p = l->l_proc;
    86
    87          retval[0] = p->p_pid;
    88          retval[1] = p->p_ppid;
    89          return (0);
    90  }

まとめ

NetBSDでdtrace機能を試してみました。デフォルトインストールではdtrace機能が有効で無いため、カーネルカーネルモジュールの構築が必要です。インストールCDが欲しかったので、備忘録を兼ねた一通りのビルド手順についてもまとめてみました。