狠狠撸

狠狠撸Share a Scribd company logo
Dtrace in NetBSD読んでいる???
       (まだまだ途中???)



        @akachochin
使用上の注意
●
    このスライドはNetBSDのdtraceのソースを読ん
    だ*途中経過*をまとめたものです。よって、資
    料を書いている私も完璧に理解していません>
    <
●
    このスライドは飲んだくれ飛び入りLTで説明し
    きらないといけないです。よってかなりはしょ
    りまくっています。
●
    よくわからないところはソースを読みましょ
    う。きっと面白いですよ????。
自己紹介
●
    普段は仕事でNetBSDをMFPやプリンタに組み
    込んでます。
●
    今まではMIPSに操を立ててたけど、最近は
    ARMに浮気してます。
●
    ARMってば、ドキュメント多すぎ><
●
    趣味は赤提灯めぐり。場末の飲み屋さいこー。
●
    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とは?
●
    人間データシートが多数を占めるカーネル/VM
    探検隊参加者には釈迦に説法だとは思いますが
    (笑)
●
    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

Dtrace in netbsd

  • 1. Dtrace in NetBSD読んでいる??? (まだまだ途中???) @akachochin
  • 2. 使用上の注意 ● このスライドはNetBSDのdtraceのソースを読ん だ*途中経過*をまとめたものです。よって、資 料を書いている私も完璧に理解していません> < ● このスライドは飲んだくれ飛び入りLTで説明し きらないといけないです。よってかなりはしょ りまくっています。 ● よくわからないところはソースを読みましょ う。きっと面白いですよ????。
  • 3. 自己紹介 ● 普段は仕事でNetBSDをMFPやプリンタに組み 込んでます。 ● 今まではMIPSに操を立ててたけど、最近は ARMに浮気してます。 ● ARMってば、ドキュメント多すぎ>< ● 趣味は赤提灯めぐり。場末の飲み屋さいこー。 ● 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とは? ● 人間データシートが多数を占めるカーネル/VM 探検隊参加者には釈迦に説法だとは思いますが (笑) ● 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