Dtrace in netbsd

1,155 views
1,026 views

Published on

Dtraceのソース読んだ概要です。まだまだ途中なのですが、経過報告がてらLTでしゃべろうかと。
(This slide is written in Japanese.I don't finish read all source code of dtrace,but I'd like to talk about my experience and share the knowledge.)

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,155
On SlideShare
0
From Embeds
0
Number of Embeds
49
Actions
Shares
0
Downloads
7
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Dtrace in netbsd

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

×