Solaris 動的トレースガイド

トランスレータの宣言

トランスレータ」は、インタフェースの提供元から提供される D 代入文の集合です。これを使って、入力式を構造型のオブジェクトに翻訳できます。以下では、stdio.h に定義された ANSI-C 標準ライブラリルーチンの例を使って、トランスレータの必要性および使用方法について説明します。これらのルーチンは、FILE という名前のデータ構造に作用します。FILE の実装アーティファクトは、C プログラマによって抽出されています。データ構造の抽象オブジェクトは、公開ヘッダーファイルでデータ構造の前方宣言だけを行い、対応する構造体定義を別の非公開ヘッダーファイルに保存することによって作成できます。これが標準の方法です。

C プログラムの作成中に、FILE 構造体のファイル記述子について調べる場合は、FILE 構造体のメンバーを間接参照するのではなく、fileno(3C) 関数を使って記述子を取得します。Solaris ヘッダーファイルは、この規則を徹底させるため、FILE を不透明な前方宣言タグとして定義しています。このため、<stdio.h> を含む C プログラムから FILE を間接参照することはできません。libc.so.1 ライブラリ内の fileno() が、次のような C プログラムによって実装されていると仮定します。

int
fileno(FILE *fp)
{
	struct file_impl *ip = (struct file_impl *)fp;

	return (ip->fd);
}

この例の fileno() は、引数として FILE ポインタをとり、これを対応する内部 libc 構造のポインタ struct file_impl へキャストします。そして、実装構造の fd メンバーの値を返します。なぜ Solaris には、このようなインタフェースが実装されているのでしょうか。それは、Sun がクライアントプログラムから現在の libc 実装の詳細を抽出することにより、継続的に libc の内部実装の詳細を発展させ、変更しながら、強力なバイナリ互換性の実現に努めることができるからです。上の例で、fd メンバーのサイズや struct file_impl 内での位置は、パッチで変更されることすらありえます。しかし、fileno(3C) を呼び出す既存のバイナリは、これらのアーティファクトに依存していないため、このような変更の影響を受けません。

残念ながら、DTrace のような監視機能付きソフトウェアでは、実装内部に注目しないと有効な結果が得られません。このため、Solaris ライブラリ内やカーネル内に定義されている任意の C 関数を呼び出す余裕はありません。stdio.h に宣言されているルーチンを実装するために、D プログラム内に struct file_impl のコピーを宣言することも可能ですが、その場合 D プログラムはライブラリの非公開実装アーティファクトに依存することになります。こうした非公開実装アーティファクトは、将来のマイクロリリースやマイナーリリースで、場合によってはパッチを適用しただけで、使用できなくなる可能性があります。理想的な D プログラム用構文は、ライブラリの実装に結合され、適宜更新されながらも、より安定性の高い抽象層を提供できるような構文です。

新しいトランスレータを作成するときは、次のような宣言を使用します。

translator output-type < input-type input-identifier > {
	member-name = expression ;
	member-name = expression ;
	...
};	

output-type には、構造体を指定します。これが、翻訳の結果の型になります。input-type には、入力式の型を指定し、山括弧 (< >) で囲んで、トランスレータ式で入力式の別名として使用できる入力識別子 input-identifier を 1 つ追加します。トランスレータの本体は、中括弧 ({ }) 内に記述され、セミコロン (;) で終わります。トランスレータを構成するのは、メンバー名 (member-name) と、翻訳式に対応する識別子です。メンバー宣言では、出力型 output-type の固有のメンバーを指定する必要があります。また、D 代入演算子 (=) の規則に従って、指定したメンバーの型と互換性のある型の式を割り当てる必要があります。

たとえば、使用可能ないくつかの libc インタフェースに基づいて、stdio ファイルについての安定した情報の構造体を定義します。

struct file_info {
	int file_fd;   /* file descriptor from fileno(3C) */
	int file_eof;  /* eof flag from feof(3C) */
};

続いて、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(3C) の実装例と同じように、キャストと間接参照を行います。EOF 指示子の値を得るときも、同様の翻訳が行われます。

Sun では、D プログラムから起動可能な、Solaris インタフェース用のトランスレータセットを提供しています。これらのトランスレータは、対応するインタフェースの実装が変更されても、以前に定義されたインタフェースの安定性規則に従って保持されます。これらのトランスレータについては、D でトランスレータを呼び出す方法を説明したあと説明します。トランスレータ機能自体は、ソフトウェアパッケージの状態の監視用の独自のトランスレータを D プログラマに提供したいと考える、アプリケーション開発者やライブラリ開発者も、対象としています。