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