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() */
};

続いて、FILEfile_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;

fFILE *型の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トランスレータ

入力型 入力型の属性 出力型 出力型の属性

struct task_struct *

非公開/非公開/共通

psinfo_t *

安定/安定/共通

struct task_struct *

非公開/非公開/共通

lwpsinfo_t *

安定/安定/共通

安定した変換

トランスレータは、情報を安定したデータ構造体に変換する機能を備えていますが、データの変換時に発生するあらゆる安定性の問題を解決できるわけではありません。たとえば、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

curpsinfocurlwpsinfoはどちらも、curthread変数、プロセス記述子を表すカーネルの非公開データ構造体へのポインタ、安定したlwpsinfo_t型へのインライン変換として定義されています。Dコンパイラは、このライブラリ・ファイルを処理し、inline宣言をキャッシュに格納します。これにより、curpsinfocurlwpsinfoの見た目は他のD変数と同じになります。この宣言に続く#pragma文は、curpsinfo識別子とcurlwpsinfo識別子の属性を明示的に「安定/安定/共通」にリセットし、インライン式でcurthreadの参照をマスクします。