狠狠撸

狠狠撸Share a Scribd company logo
Dtrace in NetBSD読んでいる???
(まだまだ途中???Ver1.1)
@akachochin
使用上の注意
●
このスライドはNetBSDのdtraceのソースを読ん
だ*途中経過*をまとめたものです。よって、資
料を書いている私も完璧に理解していません>
<
●
このスライドは今年の「カーネル/VM探検隊」
で飛び入りLTで使ったスライドです。(しかも
ほとんど直してない???!)よってかなりは
しょりまくっています。
●
よくわからないところはソースを読みましょ
う。きっと面白いですよ????。
自己紹介
●
普段は仕事でNetBSDをMFPやプリンタに組み
込んでます。
●
今まではMIPSに操を立ててたけど、最近は
色々あります(笑)
●
趣味は赤提灯めぐり。場末の飲み屋さいこー。
●
Twitterは@akachochinです。皆様フォローを(笑)
でも、たまに酔いどれてつぶやきます。ご勘
弁。
dtraceって何だ?
●
今は亡きSunがSolarisに組み込んだ動的トレー
スツール(言語)。
●
DTrace を使用することにより、システム、ア
プリケーションに手を加えることなく、動的な
トレースをとることができる。
●
NetBSD6.0からx86限定だけど、dtraceがサポー
トされた。
●
FreeBSDやMac OS Xでもサポートされているら
しいぞ。
もうちょい詳しく!その1~用語~
●
dtrace では、OSとユーザープロセスに動的に変
更を加え、「プローブ」と呼ばれる計測ポイン
トを仕込むことができる。要はプローブを実行
することでdtrace側の処理を実行し、必要な記
録をとることができる。(OracleのWeb Pageの内
容を一部改)
●
dtrace のプローブは、「プロバイダ」と呼ばれ
るカーネルモジュールのセットから提供されて
いる。
まあ、プロバイダはプローブを有効にするなど
もうちょい詳しく!その2
システムでサポートしているプロバイダとプ
ローブの組はdtrace -lで表示できるよ。こんな
感じ。
● # dtrace -l | head -n 10
● ID PROVIDER MODULE FUNCTION
NAME
● 1 dtrace BEGIN
● 4 fbt netbsd AES_GMAC_Final entry
● 5 fbt netbsd AES_GMAC_Final return
もうちょい詳しく!その3
プロセスごとのwriteシステムコールにかかる平均時間を求める
fbt:netbsd:sys_write:entry
{
self->ts = timestamp;
}
fbt:netbsd:sys_write:return
{
@time[execname] = avg(timestamp – self->ts);
self->ts = 0;
}
もうちょい詳しく!その4
●
先の実装例はプロセスごとのsys_write()にかか
る平均時間を求めるものです。
●
ということは、sys_write()の先頭とreturnの箇所
で何らかの方法でdtrace処理が呼び出されない
といけないはず???。
ここで疑問
●
プロバイダたるカーネルモジュールが動的に
「プローブ」を有効にすることはわかった。
●
しかし、「動的に有効」ってどうやってやって
いるの?
●
実際、例に出てきたsys_write()読んでも怪しげ
なマクロなどのプローブらしきものは出てこな
いんだけど???。
ソースを探検する前に
●
dtrace -lの結果から、システムコールに対応し
ているプロバイダ(カーネルモジュール)はfbtの
ようだ。
●
超絶はしょりまくりでネタバレすると、dtrace
のハンドリング(ロギング処理)をしているのは
dtrace_probe()というdtraceカーネルモジュール
関数である。
●
dtraceカーネルモジュールはプロバイダではな
くdtraceの処理を行うメインモジュールであ
る。
ソースを探検!その1
●
dtrace_probe()はfbt_invop()から呼ばれており、
この関数はdtrace_invop_add()の引数で渡されて
いるものである。このパターンは多分関数ポイ
ンタの登録...?
●
dtrace_invop_add()のソースは
external/cddl/osnet/dev/dtrace/i386(amd64)/dtrace_s
ubr.c である。もろアーキテクチャ依存な匂
い。
ソースを探検!その2
●
i386側の実装を見ることにする。
dtrace_invop_add()にはdtrace_invop_hdlrというハ
ンドラ構造体がある。ここにfbt_invop()が登録
される。
●
別のソースに実装されているdtrace_invop()では
dtrace_invop_hdlrに登録した関数を呼び出して
いる。
ソースを探検!その3
●
dtrace_invop()は、dtraceカーネルモジュールの
アセンブラ関数でdtrace_invop_startと言う関数
から呼ばれている。
●
dtrace_invop_startのアドレスはdtraceの初期化関
数でdtrace_invop_jump_addrという変数に詰め込
まれている。
●
つまり、_dtrace_invop_jump_addrというポイン
タ経由でdtrace_invop_start()が呼ばれているとい
うことになる。
ソースを探検!その4
●
で、突き詰めていくと、dtrace_invop_jump_addr
という関数ポインタに行き着く。
●
dtrace_invop_jump_addrはどこにあるのか?
dtraceモジュールの中で呼んでいる箇所はな
い。
●
で、カーネルのソースを調べてみると???
●
sys/arch/i386/i386/vector.S 内で呼んでいること
がわかる。ベクタですよ、ベクタ。
●
調べてみると、trap6のベクタから呼ばれている
ソースを探検!その5
●
ここまでをまとめると???
trap6の例外ベクタ --> dtrace_invop_jump_addr経
由でdtrace_invop_startをコール -->
dtrace_invop_startから dtrace_invop()をコール -->
dtrace_invop()ではdtrace_invop_add()であらかじ
め登録されたdtrace_probe()をコール
という非常にややこしいことになっています。
●
起点はtrap6です。ええ。
閑話休題、trap6とは?
●
Intel? 64 and IA-32 Architectures Software
Developer’s Manual 1846ページによると???
「Interrupt 6—Invalid Opcode Exception (#UD)」
とあります。
●
要するに不正命令例外です。
ソースを探検!その6
●
ここまでのところで、どうやら「不正命令」を
実行させることでtrap6を発動し、その勢いで
dtraceの処理を呼び出すことがわかってきた。
●
でも、ビルド時にはない「不正命令」を各カー
ネル関数にどうやって埋め込むのだろうか?
●
最初の方でお話ししたとおり、「dtraceスクリ
プトで、プルーブを有効にしたときに」初めて
dtrace処理が走りますが、これが肝。
●
つまり、dtraceでプルーブを有効にする処理を
ソースを探検!その7
●
これまた細かい途中経過をすっ飛ばすと、fbt
カーネルモジュールのfbt_enable()という関数で
プルーブを有効にしている。
●
その中に↓なコードがあるよん。
● for (; fbt != NULL; fbt = fbt->fbtp_next) {
● *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
● }
ソースを探検!その8
●
前後の処理ではWrite Protectionを外
し、patchpointなるアドレスの指す箇所に
patchvalという値を書き込んでいる。
●
何となく、patchvalが「不正命令」でそれを
patchpointが指す関数の頭と末尾に書き込んでい
るのだろうことは推定がつく。
●
では、patchpointをどう特定しているのだろう
か?
ソースを探検!その9
●
fbtp_patchpointおよびfbtp_patchvalは
fbt_provide_module_cb()という関数で設定され
ている。
●
この中では渡ってきたアドレスとサイズを使っ
て命令の解析をしているようだ。
●
関数先頭を判定するのに「push %ebp」かを見
たり、関数末尾を判定するのに「ret」であるか
見たり。
(他にもパターンはありますが、代表的なもの
ソースを探検!その10
●
なるほど。与えられた関数のアドレスとそのサ
イズを使ってそこにある命令を解析しつつ、関
数の先頭と末尾を特定するのはわかりました。
●
そして、FBT_PATCHVALで定義される未定義
命令をpatchvalに、関数の先頭と末尾近辺のア
ドレスをpatchpointに設定している。
●
もちろんpatchpointの指す元の命令はきちんと
バックアップしてます!
ソースを探検!その11
●
では、関数のアドレスとそのサイズはどこから
取得しているの?
●
先にお話ししたfbt_provide_module_cb()は
ksyms_mod_foreach()経由で呼ばれている。
●
ksyms_mod_foreach()は引数で指定したモジュー
ル(カーネルモジュールやカーネル自身)のシン
ボル群について都度指定した関数(ここでは
fbt_provide_module_cb())を呼び出す。
ソースを探検!その12
●
シンボルやそのサイズはどこにあるの?
●
細かい事をはしょると、ELFの.symtabセクショ
ンに存在する。
●
データフォーマットを知りたい?それなら
http://www.skyfree.org/linux/references/ELF_Form
at.pdf
内で「Elf32_Sym」をキーワードに検索してく
れ!
というわけで強引にまとめ
●
dtraceのトレース関数はtrap6経由で呼ばれる。
●
カーネルやカーネルモジュールの.symtabセク
ションの情報を使って、関数のシンボルを獲得
している。
●
シンボル情報(アドレス、サイズ)を使って、そ
れが指す命令群を解析して「不正命令」を埋め
込む場所を特定する。
●
dtraceのトレースが有効になったら、動的に
「不正命令」を埋め込んでtrap6を引き起こすこ
かなりはしょりまくりで乱暴でした
が
●
ご清聴ありがとうございましたm(_ _)m

More Related Content

NetBSD BOFで話したDTraceの話