17 トランスレータ
「DTraceの安定性機能」では、DTraceを使用してプログラムの安定性属性を計算し、報告する方法について説明します。「安定」または「発展中」のインタフェースのみを使用してDTraceプログラムを作成することが理想的です。ただし、残念ながら、下位レベルの問題のデバッグ時やシステム・パフォーマンスの測定時には、システム・コールのような安定性の高いインタフェースに関連付けられているプローブではなく、カーネルの関数のように内部オペレーティング・システム・ルーチンに関連付けられているプローブを有効にする必要がある場合があります。ソフトウェア・スタック内の深い位置にあるプローブのデータは通常、実装アーティファクトの集合であって、Linuxシステム・コール・インタフェースのデータのような安定したデータ構造は備えていません。安定したDプログラムの作成を支援するため、DTraceには、実装アーティファクトをDプログラム文から、アクセス可能な安定したデータ構造に変換する機能があります。
トランスレータの宣言
トランスレータは、インタフェースのサプライヤによって提供されるD代入文の集合です。トランスレータを使用すると、入力式をstruct
型のオブジェクトに変換できます。トランスレータを使用する必要性を理解するために、stdio.h
に定義されているANSI C標準ライブラリ・ルーチンの例を説明します。これらのルーチンは、FILE
という名前のデータ構造体を操作し、FILEには、Cプログラマによって抽象化された実装アーティファクトが含まれています。データ構造体の抽象オブジェクトは、公開ヘッダー・ファイルでデータ構造体の前方宣言のみを行い、対応するstruct
定義を別の非公開ヘッダー・ファイルに保存することによって作成できます。これが標準の方法です。
Cプログラムの作成中に、FILE
struct
のファイル記述子について調べる場合は、FILE
struct
のメンバーを間接参照するのではなく、fileno()
関数を使用して記述子を取得します。Oracle Linuxヘッダー・ファイルは、このルールを適用するために、FILE
を不透明な前方宣言タグとして定義しています。そのため、<stdio.h>
をインクルードするCプログラムからFILEを間接参照することはできません。
/lib/libc.so.6
ライブラリ内に次の仮定的な例(fileno
がCで実装されている)があるとします。実際の実装はこの例とはまったく異なるものになることに注意してください。
int fileno(FILE *fp) { struct file_impl *ip = (struct file_impl *)fp; return (ip->fd); }
この例の仮定的なfileno
は、引数としてFILE
ポインタをとり、内部libc
構造体に対応するポインタstruct file_impl
へそれをキャストします。そして、実装構造のfd
メンバーの値を返します。
残念ながら、DTraceのような監視機能付きソフトウェアでは、実装内部に注目しないと有効な結果が得られません。そのため、Oracle Linuxライブラリやカーネルに定義されている任意のC関数をコールすることはできません。stdio.h
で宣言されているルーチンをインストゥルメントするために、Dプログラムでstruct file_impl
のコピーを宣言することも可能ですが、その場合Dプログラムはライブラリの非公開実装アーティファクトに依存することになり、将来のマイクロ・リリースやマイナー・リリースで、場合によってはパッチを適用したのみで、使用できなくなる可能性があります。理想的なDプログラム用構文は、ライブラリの実装にバインドされ、適宜更新されながらも、より安定性の高い抽象層を提供できるような構文です。
新しいトランスレータを作成するには、次のような宣言を使用します。
translator output-type < input-type input-identifier > { member-name = expression ; member-name = expression ; ... };
output-typeには、struct
を指定します。これが変換の結果の型になります。input-typeには、入力式の型を指定し、山カッコ(<>
)で囲んで、トランスレータ式で入力式の別名として使用できる入力識別子input-identifierを1つ追加します。トランスレータの本体は、中カッコ({}
)内に記述され、セミコロン(;
)で終わります。トランスレータを構成するのは、member-nameと、変換式に対応する識別子です。メンバー宣言では、出力型output-typeの固有のメンバーを指定する必要があります。また、D代入演算子(=
)のルールに従って、指定したメンバーの型と互換性のある型の式を割り当てる必要があります。
たとえば、使用可能ないくつかのlibc
インタフェースに基づいて、stdio
ファイルについての安定した情報のstruct
を定義します。
struct file_info { int file_fd; /* file descriptor from fileno() */ int file_eof; /* eof flag from feof() */ };
続いて、FILE
をfile_info
へ変換する仮定的なDトランスレータを定義できます。
translator struct file_info < FILE *F > { file_fd = ((struct file_impl *)F)->fd; file_eof = ((struct file_impl *)F)->eof; };
この仮定的なトランスレータでは、FILE *
型の入力式に、入力識別子F
が割り当てられます。これで、識別子F
をトランスレータ・メンバー式でFILE *
型の変数として使用できます。この変数は、トランスレータ宣言の本体内でしか可視的に使用されません。出力file_fd
メンバーの値を特定するために、トランスレータは前の例に示すfileno()
の仮定的な実装と同じように、キャストと間接参照を実行します。EOF
指示子の値を得るときも、同様の変換が行われます。
xlate D演算子
D演算子のxlate
は、入力式を定義済の変換出力構造へ変換するときに使用します。xlate
演算子を式で使用する形式は、次のとおりです。
xlate <output-type> ( input-expression )
たとえば、先ほど定義したFILE
struct
の仮定的なトランスレータを呼び出し、file_fd
メンバーにアクセスする場合は、次のような式を作成します。
xlate <struct file_info *>(f)->file_fd;
f
はFILE *
型のD変数です。xlate
式そのものには、output-typeで定義された型が割り当てられます。トランスレータを定義すると、このトランスレータを使用して、入力式を出力struct
型またはそのstruct
へのポインタに変換できます。
入力式をstruct
に変換する場合は、演算子「.
」を使用して、特定の出力メンバーを間接参照するか、変換済のstruct
全体を別のD変数に割り当てて、すべてのメンバーの値のコピーを作成します。単一のメンバーを間接参照する場合、Dコンパイラはそのメンバーの式に対応するコードしか生成しません。変換済のstruct
に「&
」演算子を適用して、そのアドレスを取得することはできません。コピーが作成されるか、いずれかのメンバーが参照されるまで、データ・オブジェクト自体が存在しないためです。
入力式をstruct
へのポインタに変換する場合は、演算子->
を使用して、出力の特定メンバーを間接参照するか、単項演算子*
を使用してポインタを間接参照します。後者の場合、式をstruct
に変換した場合と同じ結果が得られます。単一のメンバーを間接参照する場合、Dコンパイラはそのメンバーの式に対応するコードしか生成しません。変換済のポインタを別のD変数に割り当てることはできません。コピーが作成されるか、いずれかのメンバーが参照されるまで、データ・オブジェクトが存在しないためです。
トランスレータ宣言では、1つ以上の出力型のメンバーの式を省略できます。xlate
式を使用して、変換式が定義されていないメンバーにアクセスしようとすると、Dコンパイラからエラー・メッセージが返され、プログラムのコンパイルが中止されます。構造体の割当てによって出力型全体をコピーする場合、変換式が定義されていないメンバーはゼロで初期化されます。
xlate
操作に適したトランスレータを見つけるために、Dコンパイラは使用可能なトランスレータを次の順番でチェックします。
-
まずコンパイラは、厳密な入力式の型から出力型への変換をチェックします。
-
次にコンパイラは、基礎となる型名の
typedef
別名に従って、入力型と出力型の解決を行います。その後、解決済の入力型から解決済の出力型への変換をチェックします。 -
次にコンパイラは、互換性のある入力型から解決済の出力型への変換をチェックします。コンパイラは、関数コールの引数と関数のプロトタイプの互換性について判断するときに使用されるものと同じルールに従って、入力式の型とトランスレータの入力型に互換性があるかどうかを判断します。
これらのルールに従って一致するトランスレータが見つからない場合は、Dコンパイラからエラー・メッセージが出力され、プログラムのコンパイルが失敗します。
プロセス・モデル・トランスレータ
DTraceライブラリ・ファイル/usr/lib64/dtrace/version/procfs.d
は、Dプログラムで使用するトランスレータのセットです。これらのトランスレータによって、プログラムはプロセス記述子(struct task_struct
)のオペレーティング・システムのカーネル実装から、安定した構造体psinfo
およびlwpsinfo
に変換されます。これらの構造体は、プロセスID、プロセスの優先度、コマンド名、初期引数、およびpsコマンドで表示されるその他のデータなど、プロセスやスレッドについての安定した役立つ情報を定義します。次の表では、procfs.d
トランスレータについて説明します。
表17-1 procfs.dトランスレータ
入力型 | 入力型の属性 | 出力型 | 出力型の属性 |
---|---|---|---|
|
非公開/非公開/共通 |
|
安定/安定/共通 |
|
非公開/非公開/共通 |
|
安定/安定/共通 |
安定した変換
トランスレータは、情報を安定したデータ構造体に変換する機能を備えていますが、データの変換時に発生するあらゆる安定性の問題を解決できるわけではありません。たとえば、xlate
操作の入力式が、変更の可能性があるデータを参照している場合、結果のDプログラムも変更される可能性があります。プログラムの安定性は常に、蓄積されたDプログラム文と式の最小の安定性として計算されるためです。そのため、安定したプログラムを作成するためには、トランスレータに安定した入力式を定義する必要があります。このような安定した変換を促進するために、Dのインライン機構を使用できます。
DTraceのprocfs.d
ライブラリは、安定した変換として前述したcurlwpsinfo
変数とcurpsinfo
変数を提供します。たとえば、curpsinfo
変数とcurlwpsinfo
変数は、実際にはinline
であり、次のように宣言されます。
inline psinfo_t *curpsinfo = xlate <psinfo_t *> (curthread); #pragma D attributes Stable/Stable/Common curpsinfo inline lwpsinfo_t *curlwpsinfo = xlate <lwpsinfo_t *> (curthread); #pragma D attributes Stable/Stable/Common curlwpsinfo
curpsinfo
とcurlwpsinfo
はどちらも、curthread
変数、プロセス記述子を表すカーネルの非公開データ構造体へのポインタ、安定したlwpsinfo_t
型へのインライン変換として定義されています。Dコンパイラは、このライブラリ・ファイルを処理し、inline
宣言をキャッシュに格納します。これにより、curpsinfo
とcurlwpsinfo
の見た目は他のD変数と同じになります。この宣言に続く#pragma
文は、curpsinfo
識別子とcurlwpsinfo
識別子の属性を明示的に「安定/安定/共通」にリセットし、インライン式でcurthread
の参照をマスクします。