tom__bo’s Blog

MySQL!! MySQL!! @tom__bo

Systems Performance 読んでいく (4章 その2)

Systems Performance: Enterprise and Cloudを読んでいく。

前回の続き。

Solarisに関する記述は省略しています。

4.3 DTrace

DTraceはプログラミング言語やツールを含む観測用のフレームワーク
この節ではDTraceの基本である、動的・静的なトレーシング、probe, provider, D, action, variable, ワンライナースクリプティングをまとめている。
これらはこの後の章で、LinuxでもSolarisでも利用する。

DTraceはprobesと呼ばれる機構によって、ユーザ・カーネルレベルの両方のコードを観測することが出来る。 probesがヒットすると、任意のD言語によるactionsが実行される。 actionsはカウントイベントやタイムスタンプの記録計算、値の出力、データの集計などを行うことが出来る。 これらの処理はトレーシングを有効にした状態でリアルタイムに実行することができる。

DTraceがspa_sync()を実行する時間をトレーシングする例 f:id:tom__bo:20161214160217p:plain

他のトレーシングツールと異なる重要な点はDTraceはオーバーヘッドを極力減らしていることで、プロダクションセーフになっていることである。 これを実現している1つの方法は、CPUごとのカーネルバッファを利用していることである。 これにより、メモリの空間的局所性を高め、キャッシュミスを減らし、同期のためのロックを不要にしている。

DTraceは静的・動的なトレーシングの両方をサポートしている。

4.3.1 Static and Dynamic Tracing

静的・動的なトレーシングを理解する一つの方法は、ソースとCPU命令を調べることだ。

bio.cの例 f:id:tom__bo:20161214160233p:plain

DTRACW_IO1マクロはソースがコンパイルされる前に加えられた、static probeの例である。 ここに動的なトレーシングのコードは見つからないが、それは実行された後に加えられるからである。

bio.cの命令トレーシング結果の例 f:id:tom__bo:20161214160248p:plain

動的トレーシングを行うと、検出された命令にint命令が含まれていることがわかる。 これはsoft割り込みを発生させ、動的トレーシングを実行している様子である。 これは動的トレーシングを有効にした場合のみ現れるもので、有効にしていない時はオーバーヘッドがないのが特徴である。

4.3.2 Probes

DTraceのProbeは4つのタプルからなる。

provider: module: function: name
  • provider : probeに関連するコレクション、ソフトウェア・ライブラリと似たようなもの
  • module and function : 動的に生成され、probeするコードの場所を特定する
  • name : このprobe自体の名前

これらを特定するために、ワイルドカード(*)を使うことができ、空欄にすることもワイルドカードとみなされる。

io:::start

上記のようにした場合、ioプロバイダからの全てを調査するstartプルーブとなる。

4.3.3 Providers

DTraceのプロバイダは、使用しているDTraceとOSのバージョンに依存するが大抵は以下が備わっている。

  • syscall
  • vminfo (vertual memory statistics)
  • sysinfo
  • profile
  • sched (kernel scheduling events)
  • proc (process-level events)
  • io
  • pid (user-level dynamic tracing)
  • tcp
  • ip
  • fbt (kernel-level dynamic tracing)

これらに加えて、高級言語(Java, JavaScript, Perl, Python, ...)のprobeもある。 これらの大半は静的なトレーシングを利用して実装されているので、可能ならばこれらを動的トレーシングから利用するのが良い。

4.3.4 Arguments

Probeはargumentと呼ばれる変数を介してデータを提供することが出来る。 argumentの使い方はproviderに依存する。 例えば、syscallプロバイダはそれぞれのシステムコールへのエントリーを提供し、probeを返す。 これらは以下のargument変数に値をセットする。

  • Entry :arg0...argN (システムコールへの引き数)
  • Return : arg0 or arg1 (戻り値、errnoもセットされる)

それぞれのプロバイダがどういったargumentやreturnを必要とするかはドキュメントをみるように。

4.3.5 D Language

D言語awk-likeでワンライナースクリプトとしても使える。
DTraceの宣言は以下の様に使える

probe_description /predicate/ {action}

action部分はprobeが発火した時に、セミコロンで区切った文を実行できる。 predicateはオプショナルなフィルタリング表現である。 例えば以下のように書ける。

proc:::exec-success /execname == "httpd"/ {trace(pid);}

4.3.6 Built-in Variables

Built-in変数は計算とpredicate(フィルタ)として使え、それ自身をactionによって出力させることも出来る。

表4.2にBuilt-in変数の代表例 f:id:tom__bo:20161214160336p:plain

4.3.7 Actions

代表的なactionを表4.3に示す。

表4.3 f:id:tom__bo:20161214160345p:plain

4.3.8 Variable Types

変数のタイプを表4.4に示す。

表4.4 f:id:tom__bo:20161214160355p:plain

thread-local変数はスレッドごとのスコープで、タイムスタンプなどのデータを受けられる。 clause-local変数は中間時点での計算に利用でき、actionが同じprobeに対して処理をしている時だけ有効である。 aggregationは特別な変数型で、CPUごとの計算を行い後から集計することが出来る。
aggretgationのためのactionを表4.5に示す。

表4.5 f:id:tom__bo:20161214160403p:plain

aggregationとhistogramアクションの例として、次の例ではread()システムコールの返すサイズを表示している。

4.3.9 One-Liners

DTraceは簡潔で強力なワンライナーを書けるようにしてくれる。以下はその例。

DTraceの例 f:id:tom__bo:20161214160423p:plain

この本の中で以降もさらなるDTraceのワンライナーを使う。

4.3.10 Scripting

DTraceのワンライナーはファイルに保存することも出来、更に長いプログラムを書くことも可能である。 例えばbitesize.dスクリプトはプロセス名毎のdiskI/Oを表示する

bitesize.dスクリプト f:id:tom__bo:20161214160435p:plain

プログラムが#!から始まっているように、コマンドラインから実行することも出来る。 また、#pragmaから始まる行で標準の出力を抑制して、必要な出力のみを表示させている。
この結果の例を以下に示す。

bitesize.dの結果の例 f:id:tom__bo:20161214160445p:plain

4.3.11 Overheads

これまでにも述べてきたようにDTraceは命令のオーバーヘッドを、CPUごとのカーネルバッファで行うことで最小化している。 この他にも、カーネルスペースからユーザスペースに1秒に1回の頻度でまとめてデータを送ったり、システムの応答が遅くなった場合はトレースを中断するなどして、オーバーヘッドを落とし、安全性を高めている。 DTraceによるオーバーヘッドは、トレースする頻度や、集計のためのスクリプトの複雑度に依存する。 また、それらを保存するときにも発生するので、このことを考慮する必要がある。

4.3.12 Documentation and Resources

より詳細なドキュメントがDynamic Tracing Guideとしてオンライン上にある。 また、AppendixDに便利なDTraceワンライナー集がある。 スクリプトや戦略の参考として、DTrace+ Dynamic Tracing in Oracle Solaris, Mac OS X and FreeBSD[Gregg 11]などもある。

4.4 SystemTap

SystemTapもまた静的・動的なトレーシングが可能である。 これはLinuxでDTraceができなかった時にRed Hat, IBM, Intelといったチームで考案された。 DTraceと同じように、probeと呼ばれる命令を起点とした任意のactionの実行が可能になっている。 これらはリアルタイムで実行でき、コマンドラインからワンライナースクリプトとしても実行できる。

SystemTapはトレーシングのための他のカーネルフレームワークに由来している。 static probesやkprobes, uprobesといったものは他のツール(perf, LTTng)でも使われている。

何年かして、SystemTapはDTraceと同じように良くなり、時には驚くこともあるが、安定性に問題があり、未熟な部分も多い。 しかし、DTraceも安定性に問題があることもあり、もしSystemTapを使いたいのであれば、本書のDTraceのサンプルの殆どはSystemTapに変換することが出来る。
Appendix Eに変換のガイドがある。

4.4.1 Probes

Probeの定義は括弧の中で引き数としてピリオドで区切って定義される。
幾つかのサンプルを示す。

  • begin
  • end
  • syscall.read
  • syscall.read.return
  • kernel.function("sys_read")
  • kernel.function("sys_read").return
  • socket.send
  • timer.ms(100)
  • timer.profileprocess("a.out").statement("*@main.c:100")

多くのprobeは関連するbuilt-in変数を提供している。

4.4.2 Tapsets

probeと関連するものにtapsetがある。 probeの多くはprobe nameにtapset nameを含んでいる。

その例として以下がある。

  • syscall
  • ioblock
  • scheduler
  • memory
  • scsi
  • networking
  • tcp
  • socket

tapsetはprobeの追加的なアクションとして使われることもある。

4.4.3 Actions and Built-ins

SystemTapは多くのactionやbuilt-in、プロセス名のためのexecname()や現在のプロセスIDを示すためのpid()などを提供する。
さらなる例はAppendix Eにリストにされている。

4.4.4 Examples

以下のサンプルではread()システムコールが返すサイズをワンライナーで取得する例である。 これはDTraceで以前に示したサンプルとの比較にもなる。

SystemTapでの例 f:id:tom__bo:20161214160654p:plain

4.4.5 Overheads

DTraceと同じようなオーバーヘッドがかかる。 (トレースする頻度やその時の処理内容、保存処理の量や複雑度に依存する。)

4.4.6 Documentation and Resources

オンラインドキュメントとmanページがある。

4.5 perf

Linux Performance Events(LPE)、または略してperf(!?)は幅広い領域のパフォーマンス監視のために進化してきた。 DTraceやSystemTapのような現在はリアルタイムでのプログラミングを可能にする機能はないものの、perfは静的・動的なトレーシング、プロファイリングを可能にしている。 また、stack trace, local variables, data typesを使える。 さらに、kernelのメインラインであるため、元々入っていることが多く使いやすい上、ユーザが必要とする質問の多くに応えることが出来る。

幾つかのトレーシングによるオーバーヘッドは、DTraceやSystemTapと同じである一方で、ユーザレベルへのデータの受け渡しはpost-processingによって行われるため、頻度によっては甚大なオーバーヘッドになることがある。

4.6 Observing Observability

全てのシステムにはバグが存在し、ドキュメントも同様である。 監視ツールに限ったことではないが、以下のことに気をつけるべきである。

  • ツールは常に正しいわけではない
  • manページは常に正しいわけではない
  • メトリクスは常に完全というわけではない
  • メトリクスは低機能なデザインかもしれない

もし複数の観測ツールで検知できる項目に重複があるならば、クロスチェックすることが出来る。 また、すでに負荷のわかっているものを使って、ツールが正しく動作しているかを試すことも効果がある。 逆にドキュメントにはバグが有るとされていても、ドキュメントが更新されておらず修復されている場合もある。 実際にはすべての項目についてダブルチェックするような余裕はないかもしれない、しかし、こうした状況があることを考慮に入れておくことも重要である。