えいのうにっき

むかしのじぶんのために書いています

読書メモ・詳解システムパフォーマンス 第4章/可観測性ツール

「詳解システムパフォーマンス」の読書メモシリーズ・第3弾。

詳解 システム・パフォーマンス

詳解 システム・パフォーマンス

この回はなんだっかの理由で輪読会に参加できなかったので、前回のような「輪読会の場での話題」みたいなのはなし。スミマセン。

感想

僕自身が システムエンジニア -> PaaS(GAE)エンジニア -> Web アプリケーションエンジニア -> セールスエンジニア(イマココ) 、というキャリアを踏んできたこともあってか、「システムが稼働しているサーバに対してプロファイリング・トレーシングを実施する」という経験をほぼ一度もやってきたことが無かったな、ということを痛感した章だった。つまり何が言いたいかというと、読んでいて、実感を伴う理解を得られた場所が少なかった 、ということ。。

あと、 ロギングは「デフォルトで有効にされる頻度の低いトレーシング」と考えることができる という一文には、「なるほどなぁっ」と思った。

読書メモ

ツールのタイプ

  • システム全体の計測か、プロセスごとの計測かに分類される
  • ほとんどがカウンタかトレーシングを基礎としている

カウンタ

  • カーネルは、イベントの回数を数えたカウンタと呼ばれるさまざまな統計を管理している
システム全体を対象とするカウンタ
  • vmstat : 仮想メモリ、物理メモリの統計情報
  • mpstat : CPU ごとの使用状況
  • iostat : ディスク I/O の使用状況
  • netstat : ネットワークインターフェイスの統計・TCP/IP スタックの統計・接続ごとの統計の一部
  • sar : さまざまな統計
プロセスごとのカウンタ
  • ps : プロセスステータス。メモリや CPU の使用状況など、プロセスのさまざまな統計情報を表示する
  • top : CPU の使用状況など、統計のどれかひとつの値でソートされたトッププロセスを表示する
  • pmap : 利用統計をともなう形でプロセスのメモリセグメントのリストを表示する

これらのツールは、一般に /proc ファイルシステムから統計情報を読み出している。

トレーシング

  • データをキャプチャするために CPU にオーバーヘッドをかけ、保存のためにかなりの量のストレージを必要とすることがあるため、一般にデフォルトでは有効にされていない。
  • ロギングは「デフォルトで有効にされる頻度の低いトレーシング」と考えることができる
システム全体のトレーシング
  • tcpdump : ネットワークパケットのトレーシング
  • snoop : Solaris ベースシステムのためのネットワークパケットトレーシング
  • blktrace : ブロック I/O のトレーシング
  • iosnoop : ブロック I/O のトレーシング・DTrace ベース
  • execsnoop : 新しいプロセスのトレーシング・DTrace ベース
  • dtruss : システム全体のバッファリングされたシステムコールのトレーシング・DTrace ベース
  • DTrace : カーネル内部のトレーシングと静的/動的トレーシングを使った任意のリソースの使用状況の表示
  • SystemTap : カーネル内部のトレーシングと静的/動的トレーシングを使った任意のリソースの使用状況の表示(Linux
  • perf : 静的/動的プローブのトレーシング(Linux)。
プロセスごとのトレーシング
  • strace : Linux ベースシステム用のシステムコールトレーシング
  • truss : Solaris ベースシステム用のシステムコールトレーシング
  • gdb : ソースレベルデバッガ。一般に Linux ベースシステムで使われる
  • mdb : Solaris ベースシステム用の拡張性のあるデバッガ

Dtrace SystemTap perf などのツールは、システム全体のツールでもあるが、単一のプロセスだけを解析する実行モードもサポートしている。

プロファイリング

  • ターゲットの挙動に対してサンプル、スナップショットを集めてターゲットの特徴を表す。
    • 例えば CPU の使用状況など。プログラムカウンタやスタックトレースからサンプルを集め、CPUサイクルを消費しているコードパスの特徴を浮かび上がらせる。
    • サンプルは、通常固定された間隔で収集される
システム全体、プロセスごとのプロファイリング

以下のいずれも、タイマーとハードウェアキャッシュをベースとするプロファイリングを実行する。

  • oprofile : Linux システムのプロファイリング
  • perf : Linux パフォーマンスツールキットのLTE。プロファイリングサブコマンドが含まれている
  • DTrace : profile プロバイダを使うことでタイマーベースによるプログラムプロファイリング、CPC プロバイダを使うことでハードウェアイベントベースでのプロファイリングになる
  • SystemTap : timer tapset を使うことでタイマーベースによるプログラムプロファイリング、perf tapsetを使うことでハードウェアイベントベースでのプロファイリングになる
  • cachegrind : valgrind ツールに含まれている。ハードウェアキャッシュの使用状況をプロファイリングし、kcachegrind を使うことでビジュアライズできる
  • Intel VTune Amplifier XE : LinuxWindows のプロファイリング。
  • Oracle Solaris Studio : パフォーマンスアナライザを使った SolarisLinux のプロファイリング

モニタリング(sar)

可観測性ツールの情報ソース

タイプ Linux Solaris
プロセスごとのカウンタ /proc /proc, lxproc
システム全体のカウンタ /proc, /sys kstat
プロセスごとのトレーシング ptrace, uprobe procfs, dtrace
CPUパフォーマンスカウンタ perf_event libcpc
ネットワークトレーシング libpcap licdlpi, libpcap
ステッドごとのレイテンシ指標 遅延アカウンティング マイクロステートアカウンティング
システム全体のトレーシング tracepoint, kprobe, ftrace dtrace
/proc
  • カーネル統計に対するファイルシステムインターフェイス
  • /proc にはいくつかのディレクトリが含まれており、各ディレクトリは表現しているプロセスのプロセスIDにもとづく名前を付けられている
  • /proc はカーネルにより動的に作成され、ストレージデバイスとは結びついていない(インメモリで実行されている)。
  • ls -f /proc/<pid> などとすると、そのプロセスごとの統計を格納するさまざまなファイルが存在することがわかる。パフォーマンス計測に関連するものとしては以下のようなものがある。
    • limits : 実際に有効になっているリソース制限
    • maps : マッピングされているメモリ領域
    • sched : CPUスケジューラ統計
    • schedstat : CPUの実行時間、レイテンシ、タイムスライス
    • smaps : マッピングされているメモリ領域とその使用状況
    • stat : CPU とメモリの使用状況を含むプロセスのステータスと統計情報
    • statm : ページを単位とするメモリの使用状況のまとめ
    • status : 人間が読める形式にした stat, statm 情報
    • task : タスク単位の統計のディレクト
  • Linux では、システム全体の統計も格納するように /proc を拡張している。例えば以下のようなものがある。
    • cpuinfo : 全ての仮想CPU、モデル名、クロックスピード、キャッシュサイズなどの物理プロセッサ情報
    • diskstats : すべてのディスクデバイスのディスク I/O 統計
    • interrupts : CPU ごとの割り込みカウンタ
    • loadavg : 負荷の平均
    • meminfo : システムメモリの使用状況の解析
    • net/dev : ネットワークインターフェイスの統計
    • net/tcp : アクティブな TCP ソケットについての情報
    • schedstat : システム全体の CPU スケジューラ統計
    • self : 現在のプロセス ID ディレクトリに対するシンボリックリンク
    • slabinfo : カーネルスラブアロケータキャッシュの統計
    • stat : カーネルとシステムリソース統計の概要。CPU / ディスク / ページング / スワップ / プロセス
    • zoneinfo : メモリゾーン情報
  • Solaris ベースのシステムでも、時には Linux 風の /proc が必要になることがある。
    • その解決のため、 Linux 互換な /proc を提供するために lxproc が用意されている

/sys

  • sysfs ファイルシステムをマウントしているもの
  • もともとはデバイスドライバの統計情報を提供するために設計されたもの・今は任意の統計タイプを含むように拡張されたもの
  • /sys ファイルシステムは、「統計情報を格納するための読み出し専用ファイル」と「カーネルの状態を変更するための書き込み可能ファイル」の二種類に分かれている

kstat

  • Solaris ベースのシステムが持っている、システム全体の可観測性ツールフレームワーク。ほとんどのリソースの統計情報が含まれている
  • /proc や /sys とは異なり、疑似ファイルシステムは用意されていない
    • ioctl() を使って /dev/kstat から読み出す・libkstat ライブラリを使う
  • kstat は以下の4要素のタプルとして構造化されている
    • module : 通常は統計情報を作ったカーネルモジュールを参照する
    • instance : モジュールの中には複数のインスタンスを持つものがある
    • name : 統計グループの名前
    • statistic : 個々の統計名
  • kstat の多くの統計は累加されていく。ブート以降の合計値を示す。
  • kstat による統計は不安定なインターフェイスと考えられている。正式には公開されておらず、カーネルが変わるたびに変わる可能性があるものとされている。

遅延アカウンティング

CONFIG_TASK_DELAY_ACCT オプションを指定した Linux システムは、以下の項目についてタスクごとの時間を管理する。これらの統計情報は taskstats を使うユーザーレベルツールで読むことができる。

  • スケジューラレイテンシ:CPUが与えられるのを待つ時間
    • schedstats から情報を取得
  • ブロックI/O:ブロックI/Oが完了するのを待つ時間
  • スワッピング:ページングを待つ時間
  • メモリの回収:メモリ回収ルーチンが実行されるのを待つ時間

マイクロステートアカウンティング

  • Solaris ベースのシステムでは、スレッドごと・CPUごとにこの機能を持っている
  • 定義済みの状態についての分解能の高い時間を記録する。

その他の可観測性ツールの情報ソース

以下のようなものがある。

  • CPC
  • プロセスごとのトレーシング
    • ユーザーレベルソフトウェアイベントをトレーシングする。
    • Linux では、プロセストレーシングをコントロールする ptrace() システムコールがある
    • uprobe も持っている
  • カーネルトレーシング
    • Linux では tracepoint が静的カーネルプローブを提供し、kprobe が動的プローブを提供する
  • ネットワークスニッフィング
    • Linux では、libpcap ライブラリと /proc/net/dev から提供され、tcpdump(8) が使っている
  • プロセスアカウンティング
    • メインフレームの時代からプロセスの実行と実行時間にもとづいてコンピュータの利用料を請求するために使われていた
    • atop(1) を使うことでプロセスアカウンティングにより短命なプロセスの情報をキャッチすることができる
  • システムコール
    • getrusage() 関数など。プロセスを対象として、ユーザー/システム時間、フォールト、メッセージ、コンテキストスイッチなどのリソース使用状況の統計を取得する
追加情報
  • カーネルのバージョンや有効になっているオプション次第では、さらにその他の情報ソースが見つかるかもしれない。そのような情報ソースを見つけるためのテクニックとしては、計測したい部分に関連するカーネルコードを読み、どのような統計や tracepoint が配置されているかを見てみるなどの方法がある。
  • 調べたいことについてのカーネル統計がないという場合もある。動的トレーシング以外では、gdb(1) もしくは mdb(1) といった、カーネル変数をフェッチするデバッガが役に立つこともある。

DTrace

  • プログラミング言語とツールを内蔵している可観測性フレームワーク
  • DTrace は、プローブを介してユーザーレベル、カーネルレベルの全てのコードを計測できる。
    • プローブ:インストルメンテーションポイント
  • 各種アクションを、トレーシングを有効にしたままでリアルタイムで実行できる
    • アクション:イベントのカウント、タイムスタンプの記録、計算の実行、値の表示、データの集計、など
  • トレーシングのためにパフォーマンスにかかるオーバーヘッドを最小限に抑えてある
    • CPU ごとのカーネルバッファを使用することで、メモリの局所性が上がり、キャッシュコヒーレンシのオーバーヘッドが下がり、同期ロックの必要性を取り除くことができているため
    • カーネル内でデータのまとめ計算やフィルタリングを実行できる一連のアクションも提供していて、これもデータのオーバーヘッドを下げている

静的トレーシングと動的トレーシング

  • 静的トレーシング:コードにあらかじめ静的プローブを追加しておくことで実施できるもの。
  • 動的トレーシング:ソフトウェア割り込みを利用しプローブを挿入、アクションを実行するもの。カーネルアドレス空間のライブパッチ。追加されるのは動的トレーシングが有効にされたときだけ=未使用時のオーバーヘッドはゼロ。
  • CPU命令から動的にプローブを組み立てるので、リリースごとに変動し得る。トレーシング対象のソフトウェアの新しいリリースが出たら、動的トレーシングで使われている DTrace プログラムは更新しなければならなくなる場合がある、不安定なインターフェイス

プローブ

  • DTrace のプローブ: provider:module:function:name の4要素のタプルで命名される。
    • io:::start は、io プロバイダの start プローブ。start プローブのあらゆる位置にマッチするもの。

プロバイダ

利用できる DTrace プロバイダは、DTrace とオペレーティングシステムのバージョンによって決まる。以下のものが含まれている。

  • syscall : システムコールトラップテーブル
  • vminfo : 仮想メモリ統計
  • sysinfo : システム統計
  • profile : 任意の頻度のサンプリング
  • sched : カーネルスケジューリングイベント
  • proc : プロセスレベルのイベント(作成、実行、終了)
  • io : ブロックデバイスインターフェースのトレーシング(ディスクI/O)
  • pid : ユーザーレベルの動的トレーシング
  • tcp : TCP プロトコルのイベント(接続、送信、受信)
  • ip : IP プロトコルのイベント(送信、受信)
  • fbt : カーネルレベルの動的トレーシング

プロバイダの多くは、安定したインターフェイスになるように、静的トレーシングを使って実装されている。安定したインターフェイスを持たせるために、可能な限りこれらを使ったほうが良い。動的トレーシングよりも見られる範囲は狭くはなる。

引数

  • プローブには、引数と呼ばれる一連の変数を介してデータを与えることができる。使うかどうかはプロバイダによる。

D言語

  • DTrace の実行文は D言語で書く。

組み込み変数

  • 計算や術後の正否の判断に使われるもの。例えば以下のようなものがある。
    • execname : CPUを使っているプロセス名
    • uid : CPU を使っているユーザーID
    • pid : CPU を使っているプロセスID
    • timestamp : ブート時からn秒単位で計算した現在の時刻
    • vtimestamp : スレッドが CPU を使っていた時間。n秒単位
    • arg0…N : プローブ引数(uint64_t)
    • args[0]..[N] : プローブ引数(typed)
    • curthread : 現在のスレッドのカーネル構造体を指すポインタ
    • probefunc : プローブ記述の function コンポーネント
    • probname : プローブ記述の name コンポーネント
    • curpsinfo : 現在のプロセスについての情報

アクション

  • 広く使われているアクションは以下。
    • trace(arg) : arg を表示する
    • printf(format, arg, …) : 整形された文字列を表示する
    • stringof(addr) : 指定されたカーネルアドレスの文字列を返す
    • copyinstr(addr) : 指定されたユーザー空間アドレスの文字列を返す
    • stack(count) : カーネルレベルスタックトレースを表示する。count を指定した場合はそれ以上のレベルを省略する
    • ustack(count) : 同上
    • func(pc) : 指定されたカーネルプログラムカウンタからカーネル関数名を返す
    • ufunc(pc) : 指定されたユーザープログラムカウンタからユーザー関数名を返す
    • exit(status) : DTrace を終了し、ステータスを返す
    • trunc(@agg, count) : 集積体を切り捨てる。完全に削除するか、指定した count 数の値を残して削除する
    • clear(@agg) : 集積体から値を消去する・キーは残す
    • printa(format, @agg) : 整形して集積体を表示する

変数の型

  • 集積体は CPU ごとに計算できる特別な変数型

1行プログラム

スクリプティング

オーバーヘッド

  • DTrace は、CPU ごとのカーネルバッファとカーネル内での集計計算によって、インストルメンテーションのオーバーヘッドを最小限に押さえている
  • デフォルトでは1秒に1度、カーネル空間からユーザー空間にデータを非同期で渡している
  • ブロックデバイス I/O のトレーシングのオーバーヘッドは無視できることが多い
  • ネットワーク I/O のトレーシングは、パケットが1秒間に数百万もやり取りされることがあるので、大きなオーバーヘッドになることがある。
  • データを変数に保存するときもオーバーヘッドがかかる

ドキュメントと参考資料

  • Dynamic Tracing Guide

SystemTap

  • これもユーザー/カーネルレベルコードのための静的/動的トレーシングのためのもの。
  • DTrace がまだ Linux に移植されていなかった頃に作られたもの。
  • DTrace と同様、プローブと呼ばれるインストルメンテーションポイントをプログラムして使う
    • 静的プローブでは tracepoint、動的プローブでは kprobe、ユーザーレベルプローブでは uprobe を使う
  • 機能的には DTrace と同等かそれ以上になったものの、安全性に問題がある
    • その他にも、起動時間の遅さやドキュメントの不備などの小さな問題もある

プローブ

タップセット

  • syscall , scheduler , memory など。
  • 関連するプローブのグループをタップセットと呼ぶ
  • 新たな実行可能アクションを提供するためにも使われる

アクションと組み込み変数

  • プロセス名は execname()
  • カレントプロセスID は pid()
  • カーネルスタックのバックトレース表示は print_backtrace()

サンプル

オーバーヘッド

  • プログラムを初めて実行するときには、SystemTapコンパイルステージは CPU リソースをかなり消費することがある

ドキュメントと参考資料

  • 膨大な man ページあり
  • オンラインドキュメントに「SystemTap Language Reference」も

perf

  • Linux Performance Events
  • DTrace などのようなリアルタイムでプログラムする機能は持っていないが、静的/動的トレーシングやプロファイリングを実行できる
  • perf(1) では、データは後処理のためにユーザーレベルに渡されるため、頻繁に発生するイベントをトレーシングするときには、このことが大きなオーバーヘッドを生む場合がある

可観測性の観測

  • ツールは、必ずしも正しくない
    • 同じ機能を複数の可観測性ツールが持っている場合は、相互にチェックすることができる
    • 結果がわかっているワークロードを使って予想通りの結果を出すかどうかチェックする、というのも良い
  • man ページは、必ずしも正しくない
  • 利用できる指標は、不完全かもしれない
    • 指標が存在しないことは、不完全な指標があることよりも気づきにくい場合がある
  • 利用できる指標は、適切な設計ではないかもしれない

練習問題

プロファイリングとはなにか。

  • ターゲットの挙動に対してサンプル・スナップショットを集めて、ターゲットの特徴を表そうとすること。
    • 例えば CPU の使用状況など。プログラムカウンタやスタックトレースからサンプルを集め、CPUサイクルを消費しているコードパスの特徴を浮かび上がらせる。
    • サンプルは、通常は固定された間隔で収集する

トレーシングとはなにか。

  • 分析を行なうために、イベントごとのデータを可能な限り集めること。
  • データキャプチャのために CPU にオーバーヘッドをかけ、データの保存のためにストレージを必要とする
    • そのため、デフォルトでは有効にされていないのが一般的。
  • オーバーヘッドによりトレーシングターゲットの動作が遅くなることもあるので、計測された時間を解釈するときにはそのことを計算にいれる必要がある
  • ロギングは「頻度の低いトレーシング」と考えることもできる

静的トレーシングと動的トレーシングの違いはなにか。

  • 静的トレーシング:コードにあらかじめ静的プローブを追加しておくことで実施できるもの。
  • 動的トレーシング:ソフトウェア割り込みを利用しプローブを挿入、アクションを実行するもの。カーネルアドレス空間のライブパッチ。追加されるのは動的トレーシングが有効にされたときだけ=未使用時のオーバーヘッドはゼロ。

第4章/可観測性ツール の読書メモは以上。



follow us in feedly